roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     nane : this.name,
12532                     value : this.value,
12533                     cls : 'd-none  form-control'
12534                 },
12535                 
12536                 {
12537                     tag: 'input',
12538                     multiple : 'multiple',
12539                     type : 'file',
12540                     cls : 'd-none  roo-card-upload-selector'
12541                 },
12542                 
12543                 {
12544                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12545                 },
12546                 {
12547                     cls : 'card-columns roo-card-uploader-container'
12548                 }
12549
12550             ]
12551         };
12552            
12553          
12554         return cfg;
12555     },
12556     
12557     getChildContainer : function() /// what children are added to.
12558     {
12559         return this.containerEl;
12560     },
12561    
12562     getButtonContainer : function() /// what children are added to.
12563     {
12564         return this.el.select(".roo-card-uploader-button-container").first();
12565     },
12566    
12567     initEvents : function()
12568     {
12569         
12570         Roo.bootstrap.Input.prototype.initEvents.call(this);
12571         
12572         var t = this;
12573         this.addxtype({
12574             xns: Roo.bootstrap,
12575
12576             xtype : 'Button',
12577             container_method : 'getButtonContainer' ,            
12578             html :  this.html, // fix changable?
12579             cls : 'w-100 ',
12580             listeners : {
12581                 'click' : function(btn, e) {
12582                     t.onClick(e);
12583                 }
12584             }
12585         });
12586         
12587         
12588         
12589         
12590         this.urlAPI = (window.createObjectURL && window) || 
12591                                 (window.URL && URL.revokeObjectURL && URL) || 
12592                                 (window.webkitURL && webkitURL);
12593                         
12594          
12595          
12596          
12597         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12598         
12599         this.selectorEl.on('change', this.onFileSelected, this);
12600         if (this.images) {
12601             var t = this;
12602             this.images.forEach(function(img) {
12603                 t.addCard(img)
12604             });
12605             this.images = false;
12606         }
12607         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12608          
12609        
12610     },
12611     
12612    
12613     onClick : function(e)
12614     {
12615         e.preventDefault();
12616          
12617         this.selectorEl.dom.click();
12618          
12619     },
12620     
12621     onFileSelected : function(e)
12622     {
12623         e.preventDefault();
12624         
12625         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12626             return;
12627         }
12628         
12629         Roo.each(this.selectorEl.dom.files, function(file){    
12630             this.addFile(file);
12631         }, this);
12632          
12633     },
12634     
12635       
12636     
12637       
12638     
12639     addFile : function(file)
12640     {
12641            
12642         if(typeof(file) === 'string'){
12643             throw "Add file by name?"; // should not happen
12644             return;
12645         }
12646         
12647         if(!file || !this.urlAPI){
12648             return;
12649         }
12650         
12651         // file;
12652         // file.type;
12653         
12654         var _this = this;
12655         
12656         
12657         var url = _this.urlAPI.createObjectURL( file);
12658            
12659         this.addCard({
12660             id : Roo.bootstrap.CardUploader.ID--,
12661             is_uploaded : false,
12662             src : url,
12663             title : file.name,
12664             mimetype : file.type,
12665             preview : false,
12666             is_deleted : 0
12667         })
12668         
12669     },
12670     
12671     addCard : function (data)
12672     {
12673         // hidden input element?
12674         // if the file is not an image...
12675         //then we need to use something other that and header_image
12676         var t = this;
12677         //   remove.....
12678         var footer = [
12679             {
12680                 xns : Roo.bootstrap,
12681                 xtype : 'CardFooter',
12682                 items: [
12683                     {
12684                         xns : Roo.bootstrap,
12685                         xtype : 'Element',
12686                         cls : 'd-flex',
12687                         items : [
12688                             
12689                             {
12690                                 xns : Roo.bootstrap,
12691                                 xtype : 'Button',
12692                                 html : String.format("<small>{0}</small>", data.title),
12693                                 cls : 'col-11 text-left',
12694                                 size: 'sm',
12695                                 weight: 'link',
12696                                 fa : 'download',
12697                                 listeners : {
12698                                     click : function() {
12699                                         this.downloadCard(data.id)
12700                                     }
12701                                 }
12702                             },
12703                           
12704                             {
12705                                 xns : Roo.bootstrap,
12706                                 xtype : 'Button',
12707                                 
12708                                 size : 'sm',
12709                                 weight: 'danger',
12710                                 cls : 'col-1',
12711                                 fa : 'times',
12712                                 listeners : {
12713                                     click : function() {
12714                                         t.removeCard(data.id)
12715                                     }
12716                                 }
12717                             }
12718                         ]
12719                     }
12720                     
12721                 ] 
12722             }
12723             
12724         ];
12725
12726         var cn = this.addxtype(
12727             {
12728                  
12729                 xns : Roo.bootstrap,
12730                 xtype : 'Card',
12731                 closeable : true,
12732                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12733                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12734                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12735                 data : data,
12736                 html : false,
12737                  
12738                 items : footer,
12739                 initEvents : function() {
12740                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12741                     this.imgEl = this.el.select('.card-img-top').first();
12742                     if (this.imgEl) {
12743                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12744                         this.imgEl.set({ 'pointer' : 'cursor' });
12745                                   
12746                     }
12747                     
12748                   
12749                 }
12750                 
12751             }
12752         );
12753         // dont' really need ot update items.
12754         // this.items.push(cn);
12755         this.fileCollection.add(cn);
12756         this.updateInput();
12757         
12758     },
12759     removeCard : function(id)
12760     {
12761         
12762         var card  = this.fileCollection.get(id);
12763         card.data.is_deleted = 1;
12764         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12765         this.fileCollection.remove(card);
12766         //this.items = this.items.filter(function(e) { return e != card });
12767         // dont' really need ot update items.
12768         card.el.dom.parentNode.removeChild(card.el.dom);
12769         
12770     },
12771     reset: function()
12772     {
12773         this.fileCollection.each(function(card) {
12774             card.el.dom.parentNode.removeChild(card.el.dom);    
12775         });
12776         this.fileCollection.clear();
12777         this.updateInput();
12778     },
12779     
12780     updateInput : function()
12781     {
12782         var i =0;
12783         var data = [];
12784         var dom = this.inputEl().dom;
12785         var fc = this.fileCollection;
12786         var next = function() {
12787             if (i >= fc.length) {
12788                 dom.value = JSON.stringify(data);
12789                 return;
12790             }
12791             var reader = new FileReader();
12792             reader.onloadend = function(evt) {  
12793                 // file is loaded
12794                 var ee = Roo.apply({}, fc[i]);
12795                 ee.src = evt.target.result;
12796                 data.push(ee);
12797                 
12798             };
12799             reader.readAsDataURL(fc[i].src); 
12800             
12801         }
12802         
12803         
12804     }
12805     
12806     
12807 });
12808
12809
12810 Roo.bootstrap.CardUploader.ID = -1;/*
12811  * Based on:
12812  * Ext JS Library 1.1.1
12813  * Copyright(c) 2006-2007, Ext JS, LLC.
12814  *
12815  * Originally Released Under LGPL - original licence link has changed is not relivant.
12816  *
12817  * Fork - LGPL
12818  * <script type="text/javascript">
12819  */
12820
12821
12822 /**
12823  * @class Roo.data.SortTypes
12824  * @singleton
12825  * Defines the default sorting (casting?) comparison functions used when sorting data.
12826  */
12827 Roo.data.SortTypes = {
12828     /**
12829      * Default sort that does nothing
12830      * @param {Mixed} s The value being converted
12831      * @return {Mixed} The comparison value
12832      */
12833     none : function(s){
12834         return s;
12835     },
12836     
12837     /**
12838      * The regular expression used to strip tags
12839      * @type {RegExp}
12840      * @property
12841      */
12842     stripTagsRE : /<\/?[^>]+>/gi,
12843     
12844     /**
12845      * Strips all HTML tags to sort on text only
12846      * @param {Mixed} s The value being converted
12847      * @return {String} The comparison value
12848      */
12849     asText : function(s){
12850         return String(s).replace(this.stripTagsRE, "");
12851     },
12852     
12853     /**
12854      * Strips all HTML tags to sort on text only - Case insensitive
12855      * @param {Mixed} s The value being converted
12856      * @return {String} The comparison value
12857      */
12858     asUCText : function(s){
12859         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12860     },
12861     
12862     /**
12863      * Case insensitive string
12864      * @param {Mixed} s The value being converted
12865      * @return {String} The comparison value
12866      */
12867     asUCString : function(s) {
12868         return String(s).toUpperCase();
12869     },
12870     
12871     /**
12872      * Date sorting
12873      * @param {Mixed} s The value being converted
12874      * @return {Number} The comparison value
12875      */
12876     asDate : function(s) {
12877         if(!s){
12878             return 0;
12879         }
12880         if(s instanceof Date){
12881             return s.getTime();
12882         }
12883         return Date.parse(String(s));
12884     },
12885     
12886     /**
12887      * Float sorting
12888      * @param {Mixed} s The value being converted
12889      * @return {Float} The comparison value
12890      */
12891     asFloat : function(s) {
12892         var val = parseFloat(String(s).replace(/,/g, ""));
12893         if(isNaN(val)) {
12894             val = 0;
12895         }
12896         return val;
12897     },
12898     
12899     /**
12900      * Integer sorting
12901      * @param {Mixed} s The value being converted
12902      * @return {Number} The comparison value
12903      */
12904     asInt : function(s) {
12905         var val = parseInt(String(s).replace(/,/g, ""));
12906         if(isNaN(val)) {
12907             val = 0;
12908         }
12909         return val;
12910     }
12911 };/*
12912  * Based on:
12913  * Ext JS Library 1.1.1
12914  * Copyright(c) 2006-2007, Ext JS, LLC.
12915  *
12916  * Originally Released Under LGPL - original licence link has changed is not relivant.
12917  *
12918  * Fork - LGPL
12919  * <script type="text/javascript">
12920  */
12921
12922 /**
12923 * @class Roo.data.Record
12924  * Instances of this class encapsulate both record <em>definition</em> information, and record
12925  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12926  * to access Records cached in an {@link Roo.data.Store} object.<br>
12927  * <p>
12928  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12929  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12930  * objects.<br>
12931  * <p>
12932  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12933  * @constructor
12934  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12935  * {@link #create}. The parameters are the same.
12936  * @param {Array} data An associative Array of data values keyed by the field name.
12937  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12938  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12939  * not specified an integer id is generated.
12940  */
12941 Roo.data.Record = function(data, id){
12942     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12943     this.data = data;
12944 };
12945
12946 /**
12947  * Generate a constructor for a specific record layout.
12948  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12949  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12950  * Each field definition object may contain the following properties: <ul>
12951  * <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,
12952  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12953  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12954  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12955  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12956  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12957  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12958  * this may be omitted.</p></li>
12959  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12960  * <ul><li>auto (Default, implies no conversion)</li>
12961  * <li>string</li>
12962  * <li>int</li>
12963  * <li>float</li>
12964  * <li>boolean</li>
12965  * <li>date</li></ul></p></li>
12966  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12967  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12968  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12969  * by the Reader into an object that will be stored in the Record. It is passed the
12970  * following parameters:<ul>
12971  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12972  * </ul></p></li>
12973  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12974  * </ul>
12975  * <br>usage:<br><pre><code>
12976 var TopicRecord = Roo.data.Record.create(
12977     {name: 'title', mapping: 'topic_title'},
12978     {name: 'author', mapping: 'username'},
12979     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12980     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12981     {name: 'lastPoster', mapping: 'user2'},
12982     {name: 'excerpt', mapping: 'post_text'}
12983 );
12984
12985 var myNewRecord = new TopicRecord({
12986     title: 'Do my job please',
12987     author: 'noobie',
12988     totalPosts: 1,
12989     lastPost: new Date(),
12990     lastPoster: 'Animal',
12991     excerpt: 'No way dude!'
12992 });
12993 myStore.add(myNewRecord);
12994 </code></pre>
12995  * @method create
12996  * @static
12997  */
12998 Roo.data.Record.create = function(o){
12999     var f = function(){
13000         f.superclass.constructor.apply(this, arguments);
13001     };
13002     Roo.extend(f, Roo.data.Record);
13003     var p = f.prototype;
13004     p.fields = new Roo.util.MixedCollection(false, function(field){
13005         return field.name;
13006     });
13007     for(var i = 0, len = o.length; i < len; i++){
13008         p.fields.add(new Roo.data.Field(o[i]));
13009     }
13010     f.getField = function(name){
13011         return p.fields.get(name);  
13012     };
13013     return f;
13014 };
13015
13016 Roo.data.Record.AUTO_ID = 1000;
13017 Roo.data.Record.EDIT = 'edit';
13018 Roo.data.Record.REJECT = 'reject';
13019 Roo.data.Record.COMMIT = 'commit';
13020
13021 Roo.data.Record.prototype = {
13022     /**
13023      * Readonly flag - true if this record has been modified.
13024      * @type Boolean
13025      */
13026     dirty : false,
13027     editing : false,
13028     error: null,
13029     modified: null,
13030
13031     // private
13032     join : function(store){
13033         this.store = store;
13034     },
13035
13036     /**
13037      * Set the named field to the specified value.
13038      * @param {String} name The name of the field to set.
13039      * @param {Object} value The value to set the field to.
13040      */
13041     set : function(name, value){
13042         if(this.data[name] == value){
13043             return;
13044         }
13045         this.dirty = true;
13046         if(!this.modified){
13047             this.modified = {};
13048         }
13049         if(typeof this.modified[name] == 'undefined'){
13050             this.modified[name] = this.data[name];
13051         }
13052         this.data[name] = value;
13053         if(!this.editing && this.store){
13054             this.store.afterEdit(this);
13055         }       
13056     },
13057
13058     /**
13059      * Get the value of the named field.
13060      * @param {String} name The name of the field to get the value of.
13061      * @return {Object} The value of the field.
13062      */
13063     get : function(name){
13064         return this.data[name]; 
13065     },
13066
13067     // private
13068     beginEdit : function(){
13069         this.editing = true;
13070         this.modified = {}; 
13071     },
13072
13073     // private
13074     cancelEdit : function(){
13075         this.editing = false;
13076         delete this.modified;
13077     },
13078
13079     // private
13080     endEdit : function(){
13081         this.editing = false;
13082         if(this.dirty && this.store){
13083             this.store.afterEdit(this);
13084         }
13085     },
13086
13087     /**
13088      * Usually called by the {@link Roo.data.Store} which owns the Record.
13089      * Rejects all changes made to the Record since either creation, or the last commit operation.
13090      * Modified fields are reverted to their original values.
13091      * <p>
13092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093      * of reject operations.
13094      */
13095     reject : function(){
13096         var m = this.modified;
13097         for(var n in m){
13098             if(typeof m[n] != "function"){
13099                 this.data[n] = m[n];
13100             }
13101         }
13102         this.dirty = false;
13103         delete this.modified;
13104         this.editing = false;
13105         if(this.store){
13106             this.store.afterReject(this);
13107         }
13108     },
13109
13110     /**
13111      * Usually called by the {@link Roo.data.Store} which owns the Record.
13112      * Commits all changes made to the Record since either creation, or the last commit operation.
13113      * <p>
13114      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13115      * of commit operations.
13116      */
13117     commit : function(){
13118         this.dirty = false;
13119         delete this.modified;
13120         this.editing = false;
13121         if(this.store){
13122             this.store.afterCommit(this);
13123         }
13124     },
13125
13126     // private
13127     hasError : function(){
13128         return this.error != null;
13129     },
13130
13131     // private
13132     clearError : function(){
13133         this.error = null;
13134     },
13135
13136     /**
13137      * Creates a copy of this record.
13138      * @param {String} id (optional) A new record id if you don't want to use this record's id
13139      * @return {Record}
13140      */
13141     copy : function(newId) {
13142         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13143     }
13144 };/*
13145  * Based on:
13146  * Ext JS Library 1.1.1
13147  * Copyright(c) 2006-2007, Ext JS, LLC.
13148  *
13149  * Originally Released Under LGPL - original licence link has changed is not relivant.
13150  *
13151  * Fork - LGPL
13152  * <script type="text/javascript">
13153  */
13154
13155
13156
13157 /**
13158  * @class Roo.data.Store
13159  * @extends Roo.util.Observable
13160  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13161  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13162  * <p>
13163  * 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
13164  * has no knowledge of the format of the data returned by the Proxy.<br>
13165  * <p>
13166  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13167  * instances from the data object. These records are cached and made available through accessor functions.
13168  * @constructor
13169  * Creates a new Store.
13170  * @param {Object} config A config object containing the objects needed for the Store to access data,
13171  * and read the data into Records.
13172  */
13173 Roo.data.Store = function(config){
13174     this.data = new Roo.util.MixedCollection(false);
13175     this.data.getKey = function(o){
13176         return o.id;
13177     };
13178     this.baseParams = {};
13179     // private
13180     this.paramNames = {
13181         "start" : "start",
13182         "limit" : "limit",
13183         "sort" : "sort",
13184         "dir" : "dir",
13185         "multisort" : "_multisort"
13186     };
13187
13188     if(config && config.data){
13189         this.inlineData = config.data;
13190         delete config.data;
13191     }
13192
13193     Roo.apply(this, config);
13194     
13195     if(this.reader){ // reader passed
13196         this.reader = Roo.factory(this.reader, Roo.data);
13197         this.reader.xmodule = this.xmodule || false;
13198         if(!this.recordType){
13199             this.recordType = this.reader.recordType;
13200         }
13201         if(this.reader.onMetaChange){
13202             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13203         }
13204     }
13205
13206     if(this.recordType){
13207         this.fields = this.recordType.prototype.fields;
13208     }
13209     this.modified = [];
13210
13211     this.addEvents({
13212         /**
13213          * @event datachanged
13214          * Fires when the data cache has changed, and a widget which is using this Store
13215          * as a Record cache should refresh its view.
13216          * @param {Store} this
13217          */
13218         datachanged : true,
13219         /**
13220          * @event metachange
13221          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13222          * @param {Store} this
13223          * @param {Object} meta The JSON metadata
13224          */
13225         metachange : true,
13226         /**
13227          * @event add
13228          * Fires when Records have been added to the Store
13229          * @param {Store} this
13230          * @param {Roo.data.Record[]} records The array of Records added
13231          * @param {Number} index The index at which the record(s) were added
13232          */
13233         add : true,
13234         /**
13235          * @event remove
13236          * Fires when a Record has been removed from the Store
13237          * @param {Store} this
13238          * @param {Roo.data.Record} record The Record that was removed
13239          * @param {Number} index The index at which the record was removed
13240          */
13241         remove : true,
13242         /**
13243          * @event update
13244          * Fires when a Record has been updated
13245          * @param {Store} this
13246          * @param {Roo.data.Record} record The Record that was updated
13247          * @param {String} operation The update operation being performed.  Value may be one of:
13248          * <pre><code>
13249  Roo.data.Record.EDIT
13250  Roo.data.Record.REJECT
13251  Roo.data.Record.COMMIT
13252          * </code></pre>
13253          */
13254         update : true,
13255         /**
13256          * @event clear
13257          * Fires when the data cache has been cleared.
13258          * @param {Store} this
13259          */
13260         clear : true,
13261         /**
13262          * @event beforeload
13263          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13264          * the load action will be canceled.
13265          * @param {Store} this
13266          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13267          */
13268         beforeload : true,
13269         /**
13270          * @event beforeloadadd
13271          * Fires after a new set of Records has been loaded.
13272          * @param {Store} this
13273          * @param {Roo.data.Record[]} records The Records that were loaded
13274          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13275          */
13276         beforeloadadd : true,
13277         /**
13278          * @event load
13279          * Fires after a new set of Records has been loaded, before they are added to the store.
13280          * @param {Store} this
13281          * @param {Roo.data.Record[]} records The Records that were loaded
13282          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13283          * @params {Object} return from reader
13284          */
13285         load : true,
13286         /**
13287          * @event loadexception
13288          * Fires if an exception occurs in the Proxy during loading.
13289          * Called with the signature of the Proxy's "loadexception" event.
13290          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13291          * 
13292          * @param {Proxy} 
13293          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13294          * @param {Object} load options 
13295          * @param {Object} jsonData from your request (normally this contains the Exception)
13296          */
13297         loadexception : true
13298     });
13299     
13300     if(this.proxy){
13301         this.proxy = Roo.factory(this.proxy, Roo.data);
13302         this.proxy.xmodule = this.xmodule || false;
13303         this.relayEvents(this.proxy,  ["loadexception"]);
13304     }
13305     this.sortToggle = {};
13306     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13307
13308     Roo.data.Store.superclass.constructor.call(this);
13309
13310     if(this.inlineData){
13311         this.loadData(this.inlineData);
13312         delete this.inlineData;
13313     }
13314 };
13315
13316 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13317      /**
13318     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13319     * without a remote query - used by combo/forms at present.
13320     */
13321     
13322     /**
13323     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13324     */
13325     /**
13326     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13327     */
13328     /**
13329     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13330     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13331     */
13332     /**
13333     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13334     * on any HTTP request
13335     */
13336     /**
13337     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13338     */
13339     /**
13340     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13341     */
13342     multiSort: false,
13343     /**
13344     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13345     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13346     */
13347     remoteSort : false,
13348
13349     /**
13350     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13351      * loaded or when a record is removed. (defaults to false).
13352     */
13353     pruneModifiedRecords : false,
13354
13355     // private
13356     lastOptions : null,
13357
13358     /**
13359      * Add Records to the Store and fires the add event.
13360      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13361      */
13362     add : function(records){
13363         records = [].concat(records);
13364         for(var i = 0, len = records.length; i < len; i++){
13365             records[i].join(this);
13366         }
13367         var index = this.data.length;
13368         this.data.addAll(records);
13369         this.fireEvent("add", this, records, index);
13370     },
13371
13372     /**
13373      * Remove a Record from the Store and fires the remove event.
13374      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13375      */
13376     remove : function(record){
13377         var index = this.data.indexOf(record);
13378         this.data.removeAt(index);
13379  
13380         if(this.pruneModifiedRecords){
13381             this.modified.remove(record);
13382         }
13383         this.fireEvent("remove", this, record, index);
13384     },
13385
13386     /**
13387      * Remove all Records from the Store and fires the clear event.
13388      */
13389     removeAll : function(){
13390         this.data.clear();
13391         if(this.pruneModifiedRecords){
13392             this.modified = [];
13393         }
13394         this.fireEvent("clear", this);
13395     },
13396
13397     /**
13398      * Inserts Records to the Store at the given index and fires the add event.
13399      * @param {Number} index The start index at which to insert the passed Records.
13400      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13401      */
13402     insert : function(index, records){
13403         records = [].concat(records);
13404         for(var i = 0, len = records.length; i < len; i++){
13405             this.data.insert(index, records[i]);
13406             records[i].join(this);
13407         }
13408         this.fireEvent("add", this, records, index);
13409     },
13410
13411     /**
13412      * Get the index within the cache of the passed Record.
13413      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13414      * @return {Number} The index of the passed Record. Returns -1 if not found.
13415      */
13416     indexOf : function(record){
13417         return this.data.indexOf(record);
13418     },
13419
13420     /**
13421      * Get the index within the cache of the Record with the passed id.
13422      * @param {String} id The id of the Record to find.
13423      * @return {Number} The index of the Record. Returns -1 if not found.
13424      */
13425     indexOfId : function(id){
13426         return this.data.indexOfKey(id);
13427     },
13428
13429     /**
13430      * Get the Record with the specified id.
13431      * @param {String} id The id of the Record to find.
13432      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13433      */
13434     getById : function(id){
13435         return this.data.key(id);
13436     },
13437
13438     /**
13439      * Get the Record at the specified index.
13440      * @param {Number} index The index of the Record to find.
13441      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13442      */
13443     getAt : function(index){
13444         return this.data.itemAt(index);
13445     },
13446
13447     /**
13448      * Returns a range of Records between specified indices.
13449      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13450      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13451      * @return {Roo.data.Record[]} An array of Records
13452      */
13453     getRange : function(start, end){
13454         return this.data.getRange(start, end);
13455     },
13456
13457     // private
13458     storeOptions : function(o){
13459         o = Roo.apply({}, o);
13460         delete o.callback;
13461         delete o.scope;
13462         this.lastOptions = o;
13463     },
13464
13465     /**
13466      * Loads the Record cache from the configured Proxy using the configured Reader.
13467      * <p>
13468      * If using remote paging, then the first load call must specify the <em>start</em>
13469      * and <em>limit</em> properties in the options.params property to establish the initial
13470      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13471      * <p>
13472      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13473      * and this call will return before the new data has been loaded. Perform any post-processing
13474      * in a callback function, or in a "load" event handler.</strong>
13475      * <p>
13476      * @param {Object} options An object containing properties which control loading options:<ul>
13477      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13478      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13479      * passed the following arguments:<ul>
13480      * <li>r : Roo.data.Record[]</li>
13481      * <li>options: Options object from the load call</li>
13482      * <li>success: Boolean success indicator</li></ul></li>
13483      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13484      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13485      * </ul>
13486      */
13487     load : function(options){
13488         options = options || {};
13489         if(this.fireEvent("beforeload", this, options) !== false){
13490             this.storeOptions(options);
13491             var p = Roo.apply(options.params || {}, this.baseParams);
13492             // if meta was not loaded from remote source.. try requesting it.
13493             if (!this.reader.metaFromRemote) {
13494                 p._requestMeta = 1;
13495             }
13496             if(this.sortInfo && this.remoteSort){
13497                 var pn = this.paramNames;
13498                 p[pn["sort"]] = this.sortInfo.field;
13499                 p[pn["dir"]] = this.sortInfo.direction;
13500             }
13501             if (this.multiSort) {
13502                 var pn = this.paramNames;
13503                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13504             }
13505             
13506             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13507         }
13508     },
13509
13510     /**
13511      * Reloads the Record cache from the configured Proxy using the configured Reader and
13512      * the options from the last load operation performed.
13513      * @param {Object} options (optional) An object containing properties which may override the options
13514      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13515      * the most recently used options are reused).
13516      */
13517     reload : function(options){
13518         this.load(Roo.applyIf(options||{}, this.lastOptions));
13519     },
13520
13521     // private
13522     // Called as a callback by the Reader during a load operation.
13523     loadRecords : function(o, options, success){
13524         if(!o || success === false){
13525             if(success !== false){
13526                 this.fireEvent("load", this, [], options, o);
13527             }
13528             if(options.callback){
13529                 options.callback.call(options.scope || this, [], options, false);
13530             }
13531             return;
13532         }
13533         // if data returned failure - throw an exception.
13534         if (o.success === false) {
13535             // show a message if no listener is registered.
13536             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13537                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13538             }
13539             // loadmask wil be hooked into this..
13540             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13541             return;
13542         }
13543         var r = o.records, t = o.totalRecords || r.length;
13544         
13545         this.fireEvent("beforeloadadd", this, r, options, o);
13546         
13547         if(!options || options.add !== true){
13548             if(this.pruneModifiedRecords){
13549                 this.modified = [];
13550             }
13551             for(var i = 0, len = r.length; i < len; i++){
13552                 r[i].join(this);
13553             }
13554             if(this.snapshot){
13555                 this.data = this.snapshot;
13556                 delete this.snapshot;
13557             }
13558             this.data.clear();
13559             this.data.addAll(r);
13560             this.totalLength = t;
13561             this.applySort();
13562             this.fireEvent("datachanged", this);
13563         }else{
13564             this.totalLength = Math.max(t, this.data.length+r.length);
13565             this.add(r);
13566         }
13567         
13568         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13569                 
13570             var e = new Roo.data.Record({});
13571
13572             e.set(this.parent.displayField, this.parent.emptyTitle);
13573             e.set(this.parent.valueField, '');
13574
13575             this.insert(0, e);
13576         }
13577             
13578         this.fireEvent("load", this, r, options, o);
13579         if(options.callback){
13580             options.callback.call(options.scope || this, r, options, true);
13581         }
13582     },
13583
13584
13585     /**
13586      * Loads data from a passed data block. A Reader which understands the format of the data
13587      * must have been configured in the constructor.
13588      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13589      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13590      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13591      */
13592     loadData : function(o, append){
13593         var r = this.reader.readRecords(o);
13594         this.loadRecords(r, {add: append}, true);
13595     },
13596     
13597      /**
13598      * using 'cn' the nested child reader read the child array into it's child stores.
13599      * @param {Object} rec The record with a 'children array
13600      */
13601     loadDataFromChildren : function(rec)
13602     {
13603         this.loadData(this.reader.toLoadData(rec));
13604     },
13605     
13606
13607     /**
13608      * Gets the number of cached records.
13609      * <p>
13610      * <em>If using paging, this may not be the total size of the dataset. If the data object
13611      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13612      * the data set size</em>
13613      */
13614     getCount : function(){
13615         return this.data.length || 0;
13616     },
13617
13618     /**
13619      * Gets the total number of records in the dataset as returned by the server.
13620      * <p>
13621      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13622      * the dataset size</em>
13623      */
13624     getTotalCount : function(){
13625         return this.totalLength || 0;
13626     },
13627
13628     /**
13629      * Returns the sort state of the Store as an object with two properties:
13630      * <pre><code>
13631  field {String} The name of the field by which the Records are sorted
13632  direction {String} The sort order, "ASC" or "DESC"
13633      * </code></pre>
13634      */
13635     getSortState : function(){
13636         return this.sortInfo;
13637     },
13638
13639     // private
13640     applySort : function(){
13641         if(this.sortInfo && !this.remoteSort){
13642             var s = this.sortInfo, f = s.field;
13643             var st = this.fields.get(f).sortType;
13644             var fn = function(r1, r2){
13645                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13646                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13647             };
13648             this.data.sort(s.direction, fn);
13649             if(this.snapshot && this.snapshot != this.data){
13650                 this.snapshot.sort(s.direction, fn);
13651             }
13652         }
13653     },
13654
13655     /**
13656      * Sets the default sort column and order to be used by the next load operation.
13657      * @param {String} fieldName The name of the field to sort by.
13658      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13659      */
13660     setDefaultSort : function(field, dir){
13661         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13662     },
13663
13664     /**
13665      * Sort the Records.
13666      * If remote sorting is used, the sort is performed on the server, and the cache is
13667      * reloaded. If local sorting is used, the cache is sorted internally.
13668      * @param {String} fieldName The name of the field to sort by.
13669      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13670      */
13671     sort : function(fieldName, dir){
13672         var f = this.fields.get(fieldName);
13673         if(!dir){
13674             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13675             
13676             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13677                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13678             }else{
13679                 dir = f.sortDir;
13680             }
13681         }
13682         this.sortToggle[f.name] = dir;
13683         this.sortInfo = {field: f.name, direction: dir};
13684         if(!this.remoteSort){
13685             this.applySort();
13686             this.fireEvent("datachanged", this);
13687         }else{
13688             this.load(this.lastOptions);
13689         }
13690     },
13691
13692     /**
13693      * Calls the specified function for each of the Records in the cache.
13694      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13695      * Returning <em>false</em> aborts and exits the iteration.
13696      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13697      */
13698     each : function(fn, scope){
13699         this.data.each(fn, scope);
13700     },
13701
13702     /**
13703      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13704      * (e.g., during paging).
13705      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13706      */
13707     getModifiedRecords : function(){
13708         return this.modified;
13709     },
13710
13711     // private
13712     createFilterFn : function(property, value, anyMatch){
13713         if(!value.exec){ // not a regex
13714             value = String(value);
13715             if(value.length == 0){
13716                 return false;
13717             }
13718             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13719         }
13720         return function(r){
13721             return value.test(r.data[property]);
13722         };
13723     },
13724
13725     /**
13726      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13727      * @param {String} property A field on your records
13728      * @param {Number} start The record index to start at (defaults to 0)
13729      * @param {Number} end The last record index to include (defaults to length - 1)
13730      * @return {Number} The sum
13731      */
13732     sum : function(property, start, end){
13733         var rs = this.data.items, v = 0;
13734         start = start || 0;
13735         end = (end || end === 0) ? end : rs.length-1;
13736
13737         for(var i = start; i <= end; i++){
13738             v += (rs[i].data[property] || 0);
13739         }
13740         return v;
13741     },
13742
13743     /**
13744      * Filter the records by a specified property.
13745      * @param {String} field A field on your records
13746      * @param {String/RegExp} value Either a string that the field
13747      * should start with or a RegExp to test against the field
13748      * @param {Boolean} anyMatch True to match any part not just the beginning
13749      */
13750     filter : function(property, value, anyMatch){
13751         var fn = this.createFilterFn(property, value, anyMatch);
13752         return fn ? this.filterBy(fn) : this.clearFilter();
13753     },
13754
13755     /**
13756      * Filter by a function. The specified function will be called with each
13757      * record in this data source. If the function returns true the record is included,
13758      * otherwise it is filtered.
13759      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13760      * @param {Object} scope (optional) The scope of the function (defaults to this)
13761      */
13762     filterBy : function(fn, scope){
13763         this.snapshot = this.snapshot || this.data;
13764         this.data = this.queryBy(fn, scope||this);
13765         this.fireEvent("datachanged", this);
13766     },
13767
13768     /**
13769      * Query the records by a specified property.
13770      * @param {String} field A field on your records
13771      * @param {String/RegExp} value Either a string that the field
13772      * should start with or a RegExp to test against the field
13773      * @param {Boolean} anyMatch True to match any part not just the beginning
13774      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13775      */
13776     query : function(property, value, anyMatch){
13777         var fn = this.createFilterFn(property, value, anyMatch);
13778         return fn ? this.queryBy(fn) : this.data.clone();
13779     },
13780
13781     /**
13782      * Query by a function. The specified function will be called with each
13783      * record in this data source. If the function returns true the record is included
13784      * in the results.
13785      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13786      * @param {Object} scope (optional) The scope of the function (defaults to this)
13787       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13788      **/
13789     queryBy : function(fn, scope){
13790         var data = this.snapshot || this.data;
13791         return data.filterBy(fn, scope||this);
13792     },
13793
13794     /**
13795      * Collects unique values for a particular dataIndex from this store.
13796      * @param {String} dataIndex The property to collect
13797      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13798      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13799      * @return {Array} An array of the unique values
13800      **/
13801     collect : function(dataIndex, allowNull, bypassFilter){
13802         var d = (bypassFilter === true && this.snapshot) ?
13803                 this.snapshot.items : this.data.items;
13804         var v, sv, r = [], l = {};
13805         for(var i = 0, len = d.length; i < len; i++){
13806             v = d[i].data[dataIndex];
13807             sv = String(v);
13808             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13809                 l[sv] = true;
13810                 r[r.length] = v;
13811             }
13812         }
13813         return r;
13814     },
13815
13816     /**
13817      * Revert to a view of the Record cache with no filtering applied.
13818      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13819      */
13820     clearFilter : function(suppressEvent){
13821         if(this.snapshot && this.snapshot != this.data){
13822             this.data = this.snapshot;
13823             delete this.snapshot;
13824             if(suppressEvent !== true){
13825                 this.fireEvent("datachanged", this);
13826             }
13827         }
13828     },
13829
13830     // private
13831     afterEdit : function(record){
13832         if(this.modified.indexOf(record) == -1){
13833             this.modified.push(record);
13834         }
13835         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13836     },
13837     
13838     // private
13839     afterReject : function(record){
13840         this.modified.remove(record);
13841         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13842     },
13843
13844     // private
13845     afterCommit : function(record){
13846         this.modified.remove(record);
13847         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13848     },
13849
13850     /**
13851      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13852      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13853      */
13854     commitChanges : function(){
13855         var m = this.modified.slice(0);
13856         this.modified = [];
13857         for(var i = 0, len = m.length; i < len; i++){
13858             m[i].commit();
13859         }
13860     },
13861
13862     /**
13863      * Cancel outstanding changes on all changed records.
13864      */
13865     rejectChanges : function(){
13866         var m = this.modified.slice(0);
13867         this.modified = [];
13868         for(var i = 0, len = m.length; i < len; i++){
13869             m[i].reject();
13870         }
13871     },
13872
13873     onMetaChange : function(meta, rtype, o){
13874         this.recordType = rtype;
13875         this.fields = rtype.prototype.fields;
13876         delete this.snapshot;
13877         this.sortInfo = meta.sortInfo || this.sortInfo;
13878         this.modified = [];
13879         this.fireEvent('metachange', this, this.reader.meta);
13880     },
13881     
13882     moveIndex : function(data, type)
13883     {
13884         var index = this.indexOf(data);
13885         
13886         var newIndex = index + type;
13887         
13888         this.remove(data);
13889         
13890         this.insert(newIndex, data);
13891         
13892     }
13893 });/*
13894  * Based on:
13895  * Ext JS Library 1.1.1
13896  * Copyright(c) 2006-2007, Ext JS, LLC.
13897  *
13898  * Originally Released Under LGPL - original licence link has changed is not relivant.
13899  *
13900  * Fork - LGPL
13901  * <script type="text/javascript">
13902  */
13903
13904 /**
13905  * @class Roo.data.SimpleStore
13906  * @extends Roo.data.Store
13907  * Small helper class to make creating Stores from Array data easier.
13908  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13909  * @cfg {Array} fields An array of field definition objects, or field name strings.
13910  * @cfg {Object} an existing reader (eg. copied from another store)
13911  * @cfg {Array} data The multi-dimensional array of data
13912  * @constructor
13913  * @param {Object} config
13914  */
13915 Roo.data.SimpleStore = function(config)
13916 {
13917     Roo.data.SimpleStore.superclass.constructor.call(this, {
13918         isLocal : true,
13919         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13920                 id: config.id
13921             },
13922             Roo.data.Record.create(config.fields)
13923         ),
13924         proxy : new Roo.data.MemoryProxy(config.data)
13925     });
13926     this.load();
13927 };
13928 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13929  * Based on:
13930  * Ext JS Library 1.1.1
13931  * Copyright(c) 2006-2007, Ext JS, LLC.
13932  *
13933  * Originally Released Under LGPL - original licence link has changed is not relivant.
13934  *
13935  * Fork - LGPL
13936  * <script type="text/javascript">
13937  */
13938
13939 /**
13940 /**
13941  * @extends Roo.data.Store
13942  * @class Roo.data.JsonStore
13943  * Small helper class to make creating Stores for JSON data easier. <br/>
13944 <pre><code>
13945 var store = new Roo.data.JsonStore({
13946     url: 'get-images.php',
13947     root: 'images',
13948     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13949 });
13950 </code></pre>
13951  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13952  * JsonReader and HttpProxy (unless inline data is provided).</b>
13953  * @cfg {Array} fields An array of field definition objects, or field name strings.
13954  * @constructor
13955  * @param {Object} config
13956  */
13957 Roo.data.JsonStore = function(c){
13958     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13959         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13960         reader: new Roo.data.JsonReader(c, c.fields)
13961     }));
13962 };
13963 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13964  * Based on:
13965  * Ext JS Library 1.1.1
13966  * Copyright(c) 2006-2007, Ext JS, LLC.
13967  *
13968  * Originally Released Under LGPL - original licence link has changed is not relivant.
13969  *
13970  * Fork - LGPL
13971  * <script type="text/javascript">
13972  */
13973
13974  
13975 Roo.data.Field = function(config){
13976     if(typeof config == "string"){
13977         config = {name: config};
13978     }
13979     Roo.apply(this, config);
13980     
13981     if(!this.type){
13982         this.type = "auto";
13983     }
13984     
13985     var st = Roo.data.SortTypes;
13986     // named sortTypes are supported, here we look them up
13987     if(typeof this.sortType == "string"){
13988         this.sortType = st[this.sortType];
13989     }
13990     
13991     // set default sortType for strings and dates
13992     if(!this.sortType){
13993         switch(this.type){
13994             case "string":
13995                 this.sortType = st.asUCString;
13996                 break;
13997             case "date":
13998                 this.sortType = st.asDate;
13999                 break;
14000             default:
14001                 this.sortType = st.none;
14002         }
14003     }
14004
14005     // define once
14006     var stripRe = /[\$,%]/g;
14007
14008     // prebuilt conversion function for this field, instead of
14009     // switching every time we're reading a value
14010     if(!this.convert){
14011         var cv, dateFormat = this.dateFormat;
14012         switch(this.type){
14013             case "":
14014             case "auto":
14015             case undefined:
14016                 cv = function(v){ return v; };
14017                 break;
14018             case "string":
14019                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14020                 break;
14021             case "int":
14022                 cv = function(v){
14023                     return v !== undefined && v !== null && v !== '' ?
14024                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14025                     };
14026                 break;
14027             case "float":
14028                 cv = function(v){
14029                     return v !== undefined && v !== null && v !== '' ?
14030                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14031                     };
14032                 break;
14033             case "bool":
14034             case "boolean":
14035                 cv = function(v){ return v === true || v === "true" || v == 1; };
14036                 break;
14037             case "date":
14038                 cv = function(v){
14039                     if(!v){
14040                         return '';
14041                     }
14042                     if(v instanceof Date){
14043                         return v;
14044                     }
14045                     if(dateFormat){
14046                         if(dateFormat == "timestamp"){
14047                             return new Date(v*1000);
14048                         }
14049                         return Date.parseDate(v, dateFormat);
14050                     }
14051                     var parsed = Date.parse(v);
14052                     return parsed ? new Date(parsed) : null;
14053                 };
14054              break;
14055             
14056         }
14057         this.convert = cv;
14058     }
14059 };
14060
14061 Roo.data.Field.prototype = {
14062     dateFormat: null,
14063     defaultValue: "",
14064     mapping: null,
14065     sortType : null,
14066     sortDir : "ASC"
14067 };/*
14068  * Based on:
14069  * Ext JS Library 1.1.1
14070  * Copyright(c) 2006-2007, Ext JS, LLC.
14071  *
14072  * Originally Released Under LGPL - original licence link has changed is not relivant.
14073  *
14074  * Fork - LGPL
14075  * <script type="text/javascript">
14076  */
14077  
14078 // Base class for reading structured data from a data source.  This class is intended to be
14079 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14080
14081 /**
14082  * @class Roo.data.DataReader
14083  * Base class for reading structured data from a data source.  This class is intended to be
14084  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14085  */
14086
14087 Roo.data.DataReader = function(meta, recordType){
14088     
14089     this.meta = meta;
14090     
14091     this.recordType = recordType instanceof Array ? 
14092         Roo.data.Record.create(recordType) : recordType;
14093 };
14094
14095 Roo.data.DataReader.prototype = {
14096     
14097     
14098     readerType : 'Data',
14099      /**
14100      * Create an empty record
14101      * @param {Object} data (optional) - overlay some values
14102      * @return {Roo.data.Record} record created.
14103      */
14104     newRow :  function(d) {
14105         var da =  {};
14106         this.recordType.prototype.fields.each(function(c) {
14107             switch( c.type) {
14108                 case 'int' : da[c.name] = 0; break;
14109                 case 'date' : da[c.name] = new Date(); break;
14110                 case 'float' : da[c.name] = 0.0; break;
14111                 case 'boolean' : da[c.name] = false; break;
14112                 default : da[c.name] = ""; break;
14113             }
14114             
14115         });
14116         return new this.recordType(Roo.apply(da, d));
14117     }
14118     
14119     
14120 };/*
14121  * Based on:
14122  * Ext JS Library 1.1.1
14123  * Copyright(c) 2006-2007, Ext JS, LLC.
14124  *
14125  * Originally Released Under LGPL - original licence link has changed is not relivant.
14126  *
14127  * Fork - LGPL
14128  * <script type="text/javascript">
14129  */
14130
14131 /**
14132  * @class Roo.data.DataProxy
14133  * @extends Roo.data.Observable
14134  * This class is an abstract base class for implementations which provide retrieval of
14135  * unformatted data objects.<br>
14136  * <p>
14137  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14138  * (of the appropriate type which knows how to parse the data object) to provide a block of
14139  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14140  * <p>
14141  * Custom implementations must implement the load method as described in
14142  * {@link Roo.data.HttpProxy#load}.
14143  */
14144 Roo.data.DataProxy = function(){
14145     this.addEvents({
14146         /**
14147          * @event beforeload
14148          * Fires before a network request is made to retrieve a data object.
14149          * @param {Object} This DataProxy object.
14150          * @param {Object} params The params parameter to the load function.
14151          */
14152         beforeload : true,
14153         /**
14154          * @event load
14155          * Fires before the load method's callback is called.
14156          * @param {Object} This DataProxy object.
14157          * @param {Object} o The data object.
14158          * @param {Object} arg The callback argument object passed to the load function.
14159          */
14160         load : true,
14161         /**
14162          * @event loadexception
14163          * Fires if an Exception occurs during data retrieval.
14164          * @param {Object} This DataProxy object.
14165          * @param {Object} o The data object.
14166          * @param {Object} arg The callback argument object passed to the load function.
14167          * @param {Object} e The Exception.
14168          */
14169         loadexception : true
14170     });
14171     Roo.data.DataProxy.superclass.constructor.call(this);
14172 };
14173
14174 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14175
14176     /**
14177      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14178      */
14179 /*
14180  * Based on:
14181  * Ext JS Library 1.1.1
14182  * Copyright(c) 2006-2007, Ext JS, LLC.
14183  *
14184  * Originally Released Under LGPL - original licence link has changed is not relivant.
14185  *
14186  * Fork - LGPL
14187  * <script type="text/javascript">
14188  */
14189 /**
14190  * @class Roo.data.MemoryProxy
14191  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14192  * to the Reader when its load method is called.
14193  * @constructor
14194  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14195  */
14196 Roo.data.MemoryProxy = function(data){
14197     if (data.data) {
14198         data = data.data;
14199     }
14200     Roo.data.MemoryProxy.superclass.constructor.call(this);
14201     this.data = data;
14202 };
14203
14204 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14205     
14206     /**
14207      * Load data from the requested source (in this case an in-memory
14208      * data object passed to the constructor), read the data object into
14209      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14210      * process that block using the passed callback.
14211      * @param {Object} params This parameter is not used by the MemoryProxy class.
14212      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14213      * object into a block of Roo.data.Records.
14214      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14215      * The function must be passed <ul>
14216      * <li>The Record block object</li>
14217      * <li>The "arg" argument from the load function</li>
14218      * <li>A boolean success indicator</li>
14219      * </ul>
14220      * @param {Object} scope The scope in which to call the callback
14221      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14222      */
14223     load : function(params, reader, callback, scope, arg){
14224         params = params || {};
14225         var result;
14226         try {
14227             result = reader.readRecords(params.data ? params.data :this.data);
14228         }catch(e){
14229             this.fireEvent("loadexception", this, arg, null, e);
14230             callback.call(scope, null, arg, false);
14231             return;
14232         }
14233         callback.call(scope, result, arg, true);
14234     },
14235     
14236     // private
14237     update : function(params, records){
14238         
14239     }
14240 });/*
14241  * Based on:
14242  * Ext JS Library 1.1.1
14243  * Copyright(c) 2006-2007, Ext JS, LLC.
14244  *
14245  * Originally Released Under LGPL - original licence link has changed is not relivant.
14246  *
14247  * Fork - LGPL
14248  * <script type="text/javascript">
14249  */
14250 /**
14251  * @class Roo.data.HttpProxy
14252  * @extends Roo.data.DataProxy
14253  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14254  * configured to reference a certain URL.<br><br>
14255  * <p>
14256  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14257  * from which the running page was served.<br><br>
14258  * <p>
14259  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14260  * <p>
14261  * Be aware that to enable the browser to parse an XML document, the server must set
14262  * the Content-Type header in the HTTP response to "text/xml".
14263  * @constructor
14264  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14265  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14266  * will be used to make the request.
14267  */
14268 Roo.data.HttpProxy = function(conn){
14269     Roo.data.HttpProxy.superclass.constructor.call(this);
14270     // is conn a conn config or a real conn?
14271     this.conn = conn;
14272     this.useAjax = !conn || !conn.events;
14273   
14274 };
14275
14276 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14277     // thse are take from connection...
14278     
14279     /**
14280      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14281      */
14282     /**
14283      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14284      * extra parameters to each request made by this object. (defaults to undefined)
14285      */
14286     /**
14287      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14288      *  to each request made by this object. (defaults to undefined)
14289      */
14290     /**
14291      * @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)
14292      */
14293     /**
14294      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14295      */
14296      /**
14297      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14298      * @type Boolean
14299      */
14300   
14301
14302     /**
14303      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14304      * @type Boolean
14305      */
14306     /**
14307      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14308      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14309      * a finer-grained basis than the DataProxy events.
14310      */
14311     getConnection : function(){
14312         return this.useAjax ? Roo.Ajax : this.conn;
14313     },
14314
14315     /**
14316      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14317      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14318      * process that block using the passed callback.
14319      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14320      * for the request to the remote server.
14321      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14322      * object into a block of Roo.data.Records.
14323      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14324      * The function must be passed <ul>
14325      * <li>The Record block object</li>
14326      * <li>The "arg" argument from the load function</li>
14327      * <li>A boolean success indicator</li>
14328      * </ul>
14329      * @param {Object} scope The scope in which to call the callback
14330      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14331      */
14332     load : function(params, reader, callback, scope, arg){
14333         if(this.fireEvent("beforeload", this, params) !== false){
14334             var  o = {
14335                 params : params || {},
14336                 request: {
14337                     callback : callback,
14338                     scope : scope,
14339                     arg : arg
14340                 },
14341                 reader: reader,
14342                 callback : this.loadResponse,
14343                 scope: this
14344             };
14345             if(this.useAjax){
14346                 Roo.applyIf(o, this.conn);
14347                 if(this.activeRequest){
14348                     Roo.Ajax.abort(this.activeRequest);
14349                 }
14350                 this.activeRequest = Roo.Ajax.request(o);
14351             }else{
14352                 this.conn.request(o);
14353             }
14354         }else{
14355             callback.call(scope||this, null, arg, false);
14356         }
14357     },
14358
14359     // private
14360     loadResponse : function(o, success, response){
14361         delete this.activeRequest;
14362         if(!success){
14363             this.fireEvent("loadexception", this, o, response);
14364             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14365             return;
14366         }
14367         var result;
14368         try {
14369             result = o.reader.read(response);
14370         }catch(e){
14371             this.fireEvent("loadexception", this, o, response, e);
14372             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14373             return;
14374         }
14375         
14376         this.fireEvent("load", this, o, o.request.arg);
14377         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14378     },
14379
14380     // private
14381     update : function(dataSet){
14382
14383     },
14384
14385     // private
14386     updateResponse : function(dataSet){
14387
14388     }
14389 });/*
14390  * Based on:
14391  * Ext JS Library 1.1.1
14392  * Copyright(c) 2006-2007, Ext JS, LLC.
14393  *
14394  * Originally Released Under LGPL - original licence link has changed is not relivant.
14395  *
14396  * Fork - LGPL
14397  * <script type="text/javascript">
14398  */
14399
14400 /**
14401  * @class Roo.data.ScriptTagProxy
14402  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14403  * other than the originating domain of the running page.<br><br>
14404  * <p>
14405  * <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
14406  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14407  * <p>
14408  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14409  * source code that is used as the source inside a &lt;script> tag.<br><br>
14410  * <p>
14411  * In order for the browser to process the returned data, the server must wrap the data object
14412  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14413  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14414  * depending on whether the callback name was passed:
14415  * <p>
14416  * <pre><code>
14417 boolean scriptTag = false;
14418 String cb = request.getParameter("callback");
14419 if (cb != null) {
14420     scriptTag = true;
14421     response.setContentType("text/javascript");
14422 } else {
14423     response.setContentType("application/x-json");
14424 }
14425 Writer out = response.getWriter();
14426 if (scriptTag) {
14427     out.write(cb + "(");
14428 }
14429 out.print(dataBlock.toJsonString());
14430 if (scriptTag) {
14431     out.write(");");
14432 }
14433 </pre></code>
14434  *
14435  * @constructor
14436  * @param {Object} config A configuration object.
14437  */
14438 Roo.data.ScriptTagProxy = function(config){
14439     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14440     Roo.apply(this, config);
14441     this.head = document.getElementsByTagName("head")[0];
14442 };
14443
14444 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14445
14446 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14447     /**
14448      * @cfg {String} url The URL from which to request the data object.
14449      */
14450     /**
14451      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14452      */
14453     timeout : 30000,
14454     /**
14455      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14456      * the server the name of the callback function set up by the load call to process the returned data object.
14457      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14458      * javascript output which calls this named function passing the data object as its only parameter.
14459      */
14460     callbackParam : "callback",
14461     /**
14462      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14463      * name to the request.
14464      */
14465     nocache : true,
14466
14467     /**
14468      * Load data from the configured URL, read the data object into
14469      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14470      * process that block using the passed callback.
14471      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14472      * for the request to the remote server.
14473      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14474      * object into a block of Roo.data.Records.
14475      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14476      * The function must be passed <ul>
14477      * <li>The Record block object</li>
14478      * <li>The "arg" argument from the load function</li>
14479      * <li>A boolean success indicator</li>
14480      * </ul>
14481      * @param {Object} scope The scope in which to call the callback
14482      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14483      */
14484     load : function(params, reader, callback, scope, arg){
14485         if(this.fireEvent("beforeload", this, params) !== false){
14486
14487             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14488
14489             var url = this.url;
14490             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14491             if(this.nocache){
14492                 url += "&_dc=" + (new Date().getTime());
14493             }
14494             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14495             var trans = {
14496                 id : transId,
14497                 cb : "stcCallback"+transId,
14498                 scriptId : "stcScript"+transId,
14499                 params : params,
14500                 arg : arg,
14501                 url : url,
14502                 callback : callback,
14503                 scope : scope,
14504                 reader : reader
14505             };
14506             var conn = this;
14507
14508             window[trans.cb] = function(o){
14509                 conn.handleResponse(o, trans);
14510             };
14511
14512             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14513
14514             if(this.autoAbort !== false){
14515                 this.abort();
14516             }
14517
14518             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14519
14520             var script = document.createElement("script");
14521             script.setAttribute("src", url);
14522             script.setAttribute("type", "text/javascript");
14523             script.setAttribute("id", trans.scriptId);
14524             this.head.appendChild(script);
14525
14526             this.trans = trans;
14527         }else{
14528             callback.call(scope||this, null, arg, false);
14529         }
14530     },
14531
14532     // private
14533     isLoading : function(){
14534         return this.trans ? true : false;
14535     },
14536
14537     /**
14538      * Abort the current server request.
14539      */
14540     abort : function(){
14541         if(this.isLoading()){
14542             this.destroyTrans(this.trans);
14543         }
14544     },
14545
14546     // private
14547     destroyTrans : function(trans, isLoaded){
14548         this.head.removeChild(document.getElementById(trans.scriptId));
14549         clearTimeout(trans.timeoutId);
14550         if(isLoaded){
14551             window[trans.cb] = undefined;
14552             try{
14553                 delete window[trans.cb];
14554             }catch(e){}
14555         }else{
14556             // if hasn't been loaded, wait for load to remove it to prevent script error
14557             window[trans.cb] = function(){
14558                 window[trans.cb] = undefined;
14559                 try{
14560                     delete window[trans.cb];
14561                 }catch(e){}
14562             };
14563         }
14564     },
14565
14566     // private
14567     handleResponse : function(o, trans){
14568         this.trans = false;
14569         this.destroyTrans(trans, true);
14570         var result;
14571         try {
14572             result = trans.reader.readRecords(o);
14573         }catch(e){
14574             this.fireEvent("loadexception", this, o, trans.arg, e);
14575             trans.callback.call(trans.scope||window, null, trans.arg, false);
14576             return;
14577         }
14578         this.fireEvent("load", this, o, trans.arg);
14579         trans.callback.call(trans.scope||window, result, trans.arg, true);
14580     },
14581
14582     // private
14583     handleFailure : function(trans){
14584         this.trans = false;
14585         this.destroyTrans(trans, false);
14586         this.fireEvent("loadexception", this, null, trans.arg);
14587         trans.callback.call(trans.scope||window, null, trans.arg, false);
14588     }
14589 });/*
14590  * Based on:
14591  * Ext JS Library 1.1.1
14592  * Copyright(c) 2006-2007, Ext JS, LLC.
14593  *
14594  * Originally Released Under LGPL - original licence link has changed is not relivant.
14595  *
14596  * Fork - LGPL
14597  * <script type="text/javascript">
14598  */
14599
14600 /**
14601  * @class Roo.data.JsonReader
14602  * @extends Roo.data.DataReader
14603  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14604  * based on mappings in a provided Roo.data.Record constructor.
14605  * 
14606  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14607  * in the reply previously. 
14608  * 
14609  * <p>
14610  * Example code:
14611  * <pre><code>
14612 var RecordDef = Roo.data.Record.create([
14613     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14614     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14615 ]);
14616 var myReader = new Roo.data.JsonReader({
14617     totalProperty: "results",    // The property which contains the total dataset size (optional)
14618     root: "rows",                // The property which contains an Array of row objects
14619     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14620 }, RecordDef);
14621 </code></pre>
14622  * <p>
14623  * This would consume a JSON file like this:
14624  * <pre><code>
14625 { 'results': 2, 'rows': [
14626     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14627     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14628 }
14629 </code></pre>
14630  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14631  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14632  * paged from the remote server.
14633  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14634  * @cfg {String} root name of the property which contains the Array of row objects.
14635  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14636  * @cfg {Array} fields Array of field definition objects
14637  * @constructor
14638  * Create a new JsonReader
14639  * @param {Object} meta Metadata configuration options
14640  * @param {Object} recordType Either an Array of field definition objects,
14641  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14642  */
14643 Roo.data.JsonReader = function(meta, recordType){
14644     
14645     meta = meta || {};
14646     // set some defaults:
14647     Roo.applyIf(meta, {
14648         totalProperty: 'total',
14649         successProperty : 'success',
14650         root : 'data',
14651         id : 'id'
14652     });
14653     
14654     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14655 };
14656 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14657     
14658     readerType : 'Json',
14659     
14660     /**
14661      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14662      * Used by Store query builder to append _requestMeta to params.
14663      * 
14664      */
14665     metaFromRemote : false,
14666     /**
14667      * This method is only used by a DataProxy which has retrieved data from a remote server.
14668      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14669      * @return {Object} data A data block which is used by an Roo.data.Store object as
14670      * a cache of Roo.data.Records.
14671      */
14672     read : function(response){
14673         var json = response.responseText;
14674        
14675         var o = /* eval:var:o */ eval("("+json+")");
14676         if(!o) {
14677             throw {message: "JsonReader.read: Json object not found"};
14678         }
14679         
14680         if(o.metaData){
14681             
14682             delete this.ef;
14683             this.metaFromRemote = true;
14684             this.meta = o.metaData;
14685             this.recordType = Roo.data.Record.create(o.metaData.fields);
14686             this.onMetaChange(this.meta, this.recordType, o);
14687         }
14688         return this.readRecords(o);
14689     },
14690
14691     // private function a store will implement
14692     onMetaChange : function(meta, recordType, o){
14693
14694     },
14695
14696     /**
14697          * @ignore
14698          */
14699     simpleAccess: function(obj, subsc) {
14700         return obj[subsc];
14701     },
14702
14703         /**
14704          * @ignore
14705          */
14706     getJsonAccessor: function(){
14707         var re = /[\[\.]/;
14708         return function(expr) {
14709             try {
14710                 return(re.test(expr))
14711                     ? new Function("obj", "return obj." + expr)
14712                     : function(obj){
14713                         return obj[expr];
14714                     };
14715             } catch(e){}
14716             return Roo.emptyFn;
14717         };
14718     }(),
14719
14720     /**
14721      * Create a data block containing Roo.data.Records from an XML document.
14722      * @param {Object} o An object which contains an Array of row objects in the property specified
14723      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14724      * which contains the total size of the dataset.
14725      * @return {Object} data A data block which is used by an Roo.data.Store object as
14726      * a cache of Roo.data.Records.
14727      */
14728     readRecords : function(o){
14729         /**
14730          * After any data loads, the raw JSON data is available for further custom processing.
14731          * @type Object
14732          */
14733         this.o = o;
14734         var s = this.meta, Record = this.recordType,
14735             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14736
14737 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14738         if (!this.ef) {
14739             if(s.totalProperty) {
14740                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14741                 }
14742                 if(s.successProperty) {
14743                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14744                 }
14745                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14746                 if (s.id) {
14747                         var g = this.getJsonAccessor(s.id);
14748                         this.getId = function(rec) {
14749                                 var r = g(rec);  
14750                                 return (r === undefined || r === "") ? null : r;
14751                         };
14752                 } else {
14753                         this.getId = function(){return null;};
14754                 }
14755             this.ef = [];
14756             for(var jj = 0; jj < fl; jj++){
14757                 f = fi[jj];
14758                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14759                 this.ef[jj] = this.getJsonAccessor(map);
14760             }
14761         }
14762
14763         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14764         if(s.totalProperty){
14765             var vt = parseInt(this.getTotal(o), 10);
14766             if(!isNaN(vt)){
14767                 totalRecords = vt;
14768             }
14769         }
14770         if(s.successProperty){
14771             var vs = this.getSuccess(o);
14772             if(vs === false || vs === 'false'){
14773                 success = false;
14774             }
14775         }
14776         var records = [];
14777         for(var i = 0; i < c; i++){
14778                 var n = root[i];
14779             var values = {};
14780             var id = this.getId(n);
14781             for(var j = 0; j < fl; j++){
14782                 f = fi[j];
14783             var v = this.ef[j](n);
14784             if (!f.convert) {
14785                 Roo.log('missing convert for ' + f.name);
14786                 Roo.log(f);
14787                 continue;
14788             }
14789             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14790             }
14791             var record = new Record(values, id);
14792             record.json = n;
14793             records[i] = record;
14794         }
14795         return {
14796             raw : o,
14797             success : success,
14798             records : records,
14799             totalRecords : totalRecords
14800         };
14801     },
14802     // used when loading children.. @see loadDataFromChildren
14803     toLoadData: function(rec)
14804     {
14805         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14806         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14807         return { data : data, total : data.length };
14808         
14809     }
14810 });/*
14811  * Based on:
14812  * Ext JS Library 1.1.1
14813  * Copyright(c) 2006-2007, Ext JS, LLC.
14814  *
14815  * Originally Released Under LGPL - original licence link has changed is not relivant.
14816  *
14817  * Fork - LGPL
14818  * <script type="text/javascript">
14819  */
14820
14821 /**
14822  * @class Roo.data.ArrayReader
14823  * @extends Roo.data.DataReader
14824  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14825  * Each element of that Array represents a row of data fields. The
14826  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14827  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14828  * <p>
14829  * Example code:.
14830  * <pre><code>
14831 var RecordDef = Roo.data.Record.create([
14832     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14833     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14834 ]);
14835 var myReader = new Roo.data.ArrayReader({
14836     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14837 }, RecordDef);
14838 </code></pre>
14839  * <p>
14840  * This would consume an Array like this:
14841  * <pre><code>
14842 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14843   </code></pre>
14844  
14845  * @constructor
14846  * Create a new JsonReader
14847  * @param {Object} meta Metadata configuration options.
14848  * @param {Object|Array} recordType Either an Array of field definition objects
14849  * 
14850  * @cfg {Array} fields Array of field definition objects
14851  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14852  * as specified to {@link Roo.data.Record#create},
14853  * or an {@link Roo.data.Record} object
14854  *
14855  * 
14856  * created using {@link Roo.data.Record#create}.
14857  */
14858 Roo.data.ArrayReader = function(meta, recordType)
14859 {    
14860     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14861 };
14862
14863 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14864     
14865       /**
14866      * Create a data block containing Roo.data.Records from an XML document.
14867      * @param {Object} o An Array of row objects which represents the dataset.
14868      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14869      * a cache of Roo.data.Records.
14870      */
14871     readRecords : function(o)
14872     {
14873         var sid = this.meta ? this.meta.id : null;
14874         var recordType = this.recordType, fields = recordType.prototype.fields;
14875         var records = [];
14876         var root = o;
14877         for(var i = 0; i < root.length; i++){
14878                 var n = root[i];
14879             var values = {};
14880             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14881             for(var j = 0, jlen = fields.length; j < jlen; j++){
14882                 var f = fields.items[j];
14883                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14884                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14885                 v = f.convert(v);
14886                 values[f.name] = v;
14887             }
14888             var record = new recordType(values, id);
14889             record.json = n;
14890             records[records.length] = record;
14891         }
14892         return {
14893             records : records,
14894             totalRecords : records.length
14895         };
14896     },
14897     // used when loading children.. @see loadDataFromChildren
14898     toLoadData: function(rec)
14899     {
14900         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14901         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14902         
14903     }
14904     
14905     
14906 });/*
14907  * - LGPL
14908  * * 
14909  */
14910
14911 /**
14912  * @class Roo.bootstrap.ComboBox
14913  * @extends Roo.bootstrap.TriggerField
14914  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14915  * @cfg {Boolean} append (true|false) default false
14916  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14917  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14918  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14919  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14920  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14921  * @cfg {Boolean} animate default true
14922  * @cfg {Boolean} emptyResultText only for touch device
14923  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14924  * @cfg {String} emptyTitle default ''
14925  * @cfg {Number} width fixed with? experimental
14926  * @constructor
14927  * Create a new ComboBox.
14928  * @param {Object} config Configuration options
14929  */
14930 Roo.bootstrap.ComboBox = function(config){
14931     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14932     this.addEvents({
14933         /**
14934          * @event expand
14935          * Fires when the dropdown list is expanded
14936         * @param {Roo.bootstrap.ComboBox} combo This combo box
14937         */
14938         'expand' : true,
14939         /**
14940          * @event collapse
14941          * Fires when the dropdown list is collapsed
14942         * @param {Roo.bootstrap.ComboBox} combo This combo box
14943         */
14944         'collapse' : true,
14945         /**
14946          * @event beforeselect
14947          * Fires before a list item is selected. Return false to cancel the selection.
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         * @param {Roo.data.Record} record The data record returned from the underlying store
14950         * @param {Number} index The index of the selected item in the dropdown list
14951         */
14952         'beforeselect' : true,
14953         /**
14954          * @event select
14955          * Fires when a list item is selected
14956         * @param {Roo.bootstrap.ComboBox} combo This combo box
14957         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14958         * @param {Number} index The index of the selected item in the dropdown list
14959         */
14960         'select' : true,
14961         /**
14962          * @event beforequery
14963          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14964          * The event object passed has these properties:
14965         * @param {Roo.bootstrap.ComboBox} combo This combo box
14966         * @param {String} query The query
14967         * @param {Boolean} forceAll true to force "all" query
14968         * @param {Boolean} cancel true to cancel the query
14969         * @param {Object} e The query event object
14970         */
14971         'beforequery': true,
14972          /**
14973          * @event add
14974          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14975         * @param {Roo.bootstrap.ComboBox} combo This combo box
14976         */
14977         'add' : true,
14978         /**
14979          * @event edit
14980          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14981         * @param {Roo.bootstrap.ComboBox} combo This combo box
14982         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14983         */
14984         'edit' : true,
14985         /**
14986          * @event remove
14987          * Fires when the remove value from the combobox array
14988         * @param {Roo.bootstrap.ComboBox} combo This combo box
14989         */
14990         'remove' : true,
14991         /**
14992          * @event afterremove
14993          * Fires when the remove value from the combobox array
14994         * @param {Roo.bootstrap.ComboBox} combo This combo box
14995         */
14996         'afterremove' : true,
14997         /**
14998          * @event specialfilter
14999          * Fires when specialfilter
15000             * @param {Roo.bootstrap.ComboBox} combo This combo box
15001             */
15002         'specialfilter' : true,
15003         /**
15004          * @event tick
15005          * Fires when tick the element
15006             * @param {Roo.bootstrap.ComboBox} combo This combo box
15007             */
15008         'tick' : true,
15009         /**
15010          * @event touchviewdisplay
15011          * Fires when touch view require special display (default is using displayField)
15012             * @param {Roo.bootstrap.ComboBox} combo This combo box
15013             * @param {Object} cfg set html .
15014             */
15015         'touchviewdisplay' : true
15016         
15017     });
15018     
15019     this.item = [];
15020     this.tickItems = [];
15021     
15022     this.selectedIndex = -1;
15023     if(this.mode == 'local'){
15024         if(config.queryDelay === undefined){
15025             this.queryDelay = 10;
15026         }
15027         if(config.minChars === undefined){
15028             this.minChars = 0;
15029         }
15030     }
15031 };
15032
15033 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15034      
15035     /**
15036      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15037      * rendering into an Roo.Editor, defaults to false)
15038      */
15039     /**
15040      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15041      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15042      */
15043     /**
15044      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15045      */
15046     /**
15047      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15048      * the dropdown list (defaults to undefined, with no header element)
15049      */
15050
15051      /**
15052      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15053      */
15054      
15055      /**
15056      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15057      */
15058     listWidth: undefined,
15059     /**
15060      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15061      * mode = 'remote' or 'text' if mode = 'local')
15062      */
15063     displayField: undefined,
15064     
15065     /**
15066      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15067      * mode = 'remote' or 'value' if mode = 'local'). 
15068      * Note: use of a valueField requires the user make a selection
15069      * in order for a value to be mapped.
15070      */
15071     valueField: undefined,
15072     /**
15073      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15074      */
15075     modalTitle : '',
15076     
15077     /**
15078      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15079      * field's data value (defaults to the underlying DOM element's name)
15080      */
15081     hiddenName: undefined,
15082     /**
15083      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15084      */
15085     listClass: '',
15086     /**
15087      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15088      */
15089     selectedClass: 'active',
15090     
15091     /**
15092      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15093      */
15094     shadow:'sides',
15095     /**
15096      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15097      * anchor positions (defaults to 'tl-bl')
15098      */
15099     listAlign: 'tl-bl?',
15100     /**
15101      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15102      */
15103     maxHeight: 300,
15104     /**
15105      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15106      * query specified by the allQuery config option (defaults to 'query')
15107      */
15108     triggerAction: 'query',
15109     /**
15110      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15111      * (defaults to 4, does not apply if editable = false)
15112      */
15113     minChars : 4,
15114     /**
15115      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15116      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15117      */
15118     typeAhead: false,
15119     /**
15120      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15121      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15122      */
15123     queryDelay: 500,
15124     /**
15125      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15126      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15127      */
15128     pageSize: 0,
15129     /**
15130      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15131      * when editable = true (defaults to false)
15132      */
15133     selectOnFocus:false,
15134     /**
15135      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15136      */
15137     queryParam: 'query',
15138     /**
15139      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15140      * when mode = 'remote' (defaults to 'Loading...')
15141      */
15142     loadingText: 'Loading...',
15143     /**
15144      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15145      */
15146     resizable: false,
15147     /**
15148      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15149      */
15150     handleHeight : 8,
15151     /**
15152      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15153      * traditional select (defaults to true)
15154      */
15155     editable: true,
15156     /**
15157      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15158      */
15159     allQuery: '',
15160     /**
15161      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15162      */
15163     mode: 'remote',
15164     /**
15165      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15166      * listWidth has a higher value)
15167      */
15168     minListWidth : 70,
15169     /**
15170      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15171      * allow the user to set arbitrary text into the field (defaults to false)
15172      */
15173     forceSelection:false,
15174     /**
15175      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15176      * if typeAhead = true (defaults to 250)
15177      */
15178     typeAheadDelay : 250,
15179     /**
15180      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15181      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15182      */
15183     valueNotFoundText : undefined,
15184     /**
15185      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15186      */
15187     blockFocus : false,
15188     
15189     /**
15190      * @cfg {Boolean} disableClear Disable showing of clear button.
15191      */
15192     disableClear : false,
15193     /**
15194      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15195      */
15196     alwaysQuery : false,
15197     
15198     /**
15199      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15200      */
15201     multiple : false,
15202     
15203     /**
15204      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15205      */
15206     invalidClass : "has-warning",
15207     
15208     /**
15209      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15210      */
15211     validClass : "has-success",
15212     
15213     /**
15214      * @cfg {Boolean} specialFilter (true|false) special filter default false
15215      */
15216     specialFilter : false,
15217     
15218     /**
15219      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15220      */
15221     mobileTouchView : true,
15222     
15223     /**
15224      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15225      */
15226     useNativeIOS : false,
15227     
15228     /**
15229      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15230      */
15231     mobile_restrict_height : false,
15232     
15233     ios_options : false,
15234     
15235     //private
15236     addicon : false,
15237     editicon: false,
15238     
15239     page: 0,
15240     hasQuery: false,
15241     append: false,
15242     loadNext: false,
15243     autoFocus : true,
15244     tickable : false,
15245     btnPosition : 'right',
15246     triggerList : true,
15247     showToggleBtn : true,
15248     animate : true,
15249     emptyResultText: 'Empty',
15250     triggerText : 'Select',
15251     emptyTitle : '',
15252     width : false,
15253     
15254     // element that contains real text value.. (when hidden is used..)
15255     
15256     getAutoCreate : function()
15257     {   
15258         var cfg = false;
15259         //render
15260         /*
15261          * Render classic select for iso
15262          */
15263         
15264         if(Roo.isIOS && this.useNativeIOS){
15265             cfg = this.getAutoCreateNativeIOS();
15266             return cfg;
15267         }
15268         
15269         /*
15270          * Touch Devices
15271          */
15272         
15273         if(Roo.isTouch && this.mobileTouchView){
15274             cfg = this.getAutoCreateTouchView();
15275             return cfg;;
15276         }
15277         
15278         /*
15279          *  Normal ComboBox
15280          */
15281         if(!this.tickable){
15282             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15283             return cfg;
15284         }
15285         
15286         /*
15287          *  ComboBox with tickable selections
15288          */
15289              
15290         var align = this.labelAlign || this.parentLabelAlign();
15291         
15292         cfg = {
15293             cls : 'form-group roo-combobox-tickable' //input-group
15294         };
15295         
15296         var btn_text_select = '';
15297         var btn_text_done = '';
15298         var btn_text_cancel = '';
15299         
15300         if (this.btn_text_show) {
15301             btn_text_select = 'Select';
15302             btn_text_done = 'Done';
15303             btn_text_cancel = 'Cancel'; 
15304         }
15305         
15306         var buttons = {
15307             tag : 'div',
15308             cls : 'tickable-buttons',
15309             cn : [
15310                 {
15311                     tag : 'button',
15312                     type : 'button',
15313                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15314                     //html : this.triggerText
15315                     html: btn_text_select
15316                 },
15317                 {
15318                     tag : 'button',
15319                     type : 'button',
15320                     name : 'ok',
15321                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15322                     //html : 'Done'
15323                     html: btn_text_done
15324                 },
15325                 {
15326                     tag : 'button',
15327                     type : 'button',
15328                     name : 'cancel',
15329                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15330                     //html : 'Cancel'
15331                     html: btn_text_cancel
15332                 }
15333             ]
15334         };
15335         
15336         if(this.editable){
15337             buttons.cn.unshift({
15338                 tag: 'input',
15339                 cls: 'roo-select2-search-field-input'
15340             });
15341         }
15342         
15343         var _this = this;
15344         
15345         Roo.each(buttons.cn, function(c){
15346             if (_this.size) {
15347                 c.cls += ' btn-' + _this.size;
15348             }
15349
15350             if (_this.disabled) {
15351                 c.disabled = true;
15352             }
15353         });
15354         
15355         var box = {
15356             tag: 'div',
15357             style : 'display: contents',
15358             cn: [
15359                 {
15360                     tag: 'input',
15361                     type : 'hidden',
15362                     cls: 'form-hidden-field'
15363                 },
15364                 {
15365                     tag: 'ul',
15366                     cls: 'roo-select2-choices',
15367                     cn:[
15368                         {
15369                             tag: 'li',
15370                             cls: 'roo-select2-search-field',
15371                             cn: [
15372                                 buttons
15373                             ]
15374                         }
15375                     ]
15376                 }
15377             ]
15378         };
15379         
15380         var combobox = {
15381             cls: 'roo-select2-container input-group roo-select2-container-multi',
15382             cn: [
15383                 
15384                 box
15385 //                {
15386 //                    tag: 'ul',
15387 //                    cls: 'typeahead typeahead-long dropdown-menu',
15388 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15389 //                }
15390             ]
15391         };
15392         
15393         if(this.hasFeedback && !this.allowBlank){
15394             
15395             var feedback = {
15396                 tag: 'span',
15397                 cls: 'glyphicon form-control-feedback'
15398             };
15399
15400             combobox.cn.push(feedback);
15401         }
15402         
15403         
15404         
15405         var indicator = {
15406             tag : 'i',
15407             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15408             tooltip : 'This field is required'
15409         };
15410         if (Roo.bootstrap.version == 4) {
15411             indicator = {
15412                 tag : 'i',
15413                 style : 'display:none'
15414             };
15415         }
15416         if (align ==='left' && this.fieldLabel.length) {
15417             
15418             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15419             
15420             cfg.cn = [
15421                 indicator,
15422                 {
15423                     tag: 'label',
15424                     'for' :  id,
15425                     cls : 'control-label col-form-label',
15426                     html : this.fieldLabel
15427
15428                 },
15429                 {
15430                     cls : "", 
15431                     cn: [
15432                         combobox
15433                     ]
15434                 }
15435
15436             ];
15437             
15438             var labelCfg = cfg.cn[1];
15439             var contentCfg = cfg.cn[2];
15440             
15441
15442             if(this.indicatorpos == 'right'){
15443                 
15444                 cfg.cn = [
15445                     {
15446                         tag: 'label',
15447                         'for' :  id,
15448                         cls : 'control-label col-form-label',
15449                         cn : [
15450                             {
15451                                 tag : 'span',
15452                                 html : this.fieldLabel
15453                             },
15454                             indicator
15455                         ]
15456                     },
15457                     {
15458                         cls : "",
15459                         cn: [
15460                             combobox
15461                         ]
15462                     }
15463
15464                 ];
15465                 
15466                 
15467                 
15468                 labelCfg = cfg.cn[0];
15469                 contentCfg = cfg.cn[1];
15470             
15471             }
15472             
15473             if(this.labelWidth > 12){
15474                 labelCfg.style = "width: " + this.labelWidth + 'px';
15475             }
15476             if(this.width * 1 > 0){
15477                 contentCfg.style = "width: " + this.width + 'px';
15478             }
15479             if(this.labelWidth < 13 && this.labelmd == 0){
15480                 this.labelmd = this.labelWidth;
15481             }
15482             
15483             if(this.labellg > 0){
15484                 labelCfg.cls += ' col-lg-' + this.labellg;
15485                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15486             }
15487             
15488             if(this.labelmd > 0){
15489                 labelCfg.cls += ' col-md-' + this.labelmd;
15490                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15491             }
15492             
15493             if(this.labelsm > 0){
15494                 labelCfg.cls += ' col-sm-' + this.labelsm;
15495                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15496             }
15497             
15498             if(this.labelxs > 0){
15499                 labelCfg.cls += ' col-xs-' + this.labelxs;
15500                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15501             }
15502                 
15503                 
15504         } else if ( this.fieldLabel.length) {
15505 //                Roo.log(" label");
15506                  cfg.cn = [
15507                    indicator,
15508                     {
15509                         tag: 'label',
15510                         //cls : 'input-group-addon',
15511                         html : this.fieldLabel
15512                     },
15513                     combobox
15514                 ];
15515                 
15516                 if(this.indicatorpos == 'right'){
15517                     cfg.cn = [
15518                         {
15519                             tag: 'label',
15520                             //cls : 'input-group-addon',
15521                             html : this.fieldLabel
15522                         },
15523                         indicator,
15524                         combobox
15525                     ];
15526                     
15527                 }
15528
15529         } else {
15530             
15531 //                Roo.log(" no label && no align");
15532                 cfg = combobox
15533                      
15534                 
15535         }
15536          
15537         var settings=this;
15538         ['xs','sm','md','lg'].map(function(size){
15539             if (settings[size]) {
15540                 cfg.cls += ' col-' + size + '-' + settings[size];
15541             }
15542         });
15543         
15544         return cfg;
15545         
15546     },
15547     
15548     _initEventsCalled : false,
15549     
15550     // private
15551     initEvents: function()
15552     {   
15553         if (this._initEventsCalled) { // as we call render... prevent looping...
15554             return;
15555         }
15556         this._initEventsCalled = true;
15557         
15558         if (!this.store) {
15559             throw "can not find store for combo";
15560         }
15561         
15562         this.indicator = this.indicatorEl();
15563         
15564         this.store = Roo.factory(this.store, Roo.data);
15565         this.store.parent = this;
15566         
15567         // if we are building from html. then this element is so complex, that we can not really
15568         // use the rendered HTML.
15569         // so we have to trash and replace the previous code.
15570         if (Roo.XComponent.build_from_html) {
15571             // remove this element....
15572             var e = this.el.dom, k=0;
15573             while (e ) { e = e.previousSibling;  ++k;}
15574
15575             this.el.remove();
15576             
15577             this.el=false;
15578             this.rendered = false;
15579             
15580             this.render(this.parent().getChildContainer(true), k);
15581         }
15582         
15583         if(Roo.isIOS && this.useNativeIOS){
15584             this.initIOSView();
15585             return;
15586         }
15587         
15588         /*
15589          * Touch Devices
15590          */
15591         
15592         if(Roo.isTouch && this.mobileTouchView){
15593             this.initTouchView();
15594             return;
15595         }
15596         
15597         if(this.tickable){
15598             this.initTickableEvents();
15599             return;
15600         }
15601         
15602         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15603         
15604         if(this.hiddenName){
15605             
15606             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15607             
15608             this.hiddenField.dom.value =
15609                 this.hiddenValue !== undefined ? this.hiddenValue :
15610                 this.value !== undefined ? this.value : '';
15611
15612             // prevent input submission
15613             this.el.dom.removeAttribute('name');
15614             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15615              
15616              
15617         }
15618         //if(Roo.isGecko){
15619         //    this.el.dom.setAttribute('autocomplete', 'off');
15620         //}
15621         
15622         var cls = 'x-combo-list';
15623         
15624         //this.list = new Roo.Layer({
15625         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15626         //});
15627         
15628         var _this = this;
15629         
15630         (function(){
15631             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15632             _this.list.setWidth(lw);
15633         }).defer(100);
15634         
15635         this.list.on('mouseover', this.onViewOver, this);
15636         this.list.on('mousemove', this.onViewMove, this);
15637         this.list.on('scroll', this.onViewScroll, this);
15638         
15639         /*
15640         this.list.swallowEvent('mousewheel');
15641         this.assetHeight = 0;
15642
15643         if(this.title){
15644             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15645             this.assetHeight += this.header.getHeight();
15646         }
15647
15648         this.innerList = this.list.createChild({cls:cls+'-inner'});
15649         this.innerList.on('mouseover', this.onViewOver, this);
15650         this.innerList.on('mousemove', this.onViewMove, this);
15651         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15652         
15653         if(this.allowBlank && !this.pageSize && !this.disableClear){
15654             this.footer = this.list.createChild({cls:cls+'-ft'});
15655             this.pageTb = new Roo.Toolbar(this.footer);
15656            
15657         }
15658         if(this.pageSize){
15659             this.footer = this.list.createChild({cls:cls+'-ft'});
15660             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15661                     {pageSize: this.pageSize});
15662             
15663         }
15664         
15665         if (this.pageTb && this.allowBlank && !this.disableClear) {
15666             var _this = this;
15667             this.pageTb.add(new Roo.Toolbar.Fill(), {
15668                 cls: 'x-btn-icon x-btn-clear',
15669                 text: '&#160;',
15670                 handler: function()
15671                 {
15672                     _this.collapse();
15673                     _this.clearValue();
15674                     _this.onSelect(false, -1);
15675                 }
15676             });
15677         }
15678         if (this.footer) {
15679             this.assetHeight += this.footer.getHeight();
15680         }
15681         */
15682             
15683         if(!this.tpl){
15684             this.tpl = Roo.bootstrap.version == 4 ?
15685                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15686                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15687         }
15688
15689         this.view = new Roo.View(this.list, this.tpl, {
15690             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15691         });
15692         //this.view.wrapEl.setDisplayed(false);
15693         this.view.on('click', this.onViewClick, this);
15694         
15695         
15696         this.store.on('beforeload', this.onBeforeLoad, this);
15697         this.store.on('load', this.onLoad, this);
15698         this.store.on('loadexception', this.onLoadException, this);
15699         /*
15700         if(this.resizable){
15701             this.resizer = new Roo.Resizable(this.list,  {
15702                pinned:true, handles:'se'
15703             });
15704             this.resizer.on('resize', function(r, w, h){
15705                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15706                 this.listWidth = w;
15707                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15708                 this.restrictHeight();
15709             }, this);
15710             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15711         }
15712         */
15713         if(!this.editable){
15714             this.editable = true;
15715             this.setEditable(false);
15716         }
15717         
15718         /*
15719         
15720         if (typeof(this.events.add.listeners) != 'undefined') {
15721             
15722             this.addicon = this.wrap.createChild(
15723                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15724        
15725             this.addicon.on('click', function(e) {
15726                 this.fireEvent('add', this);
15727             }, this);
15728         }
15729         if (typeof(this.events.edit.listeners) != 'undefined') {
15730             
15731             this.editicon = this.wrap.createChild(
15732                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15733             if (this.addicon) {
15734                 this.editicon.setStyle('margin-left', '40px');
15735             }
15736             this.editicon.on('click', function(e) {
15737                 
15738                 // we fire even  if inothing is selected..
15739                 this.fireEvent('edit', this, this.lastData );
15740                 
15741             }, this);
15742         }
15743         */
15744         
15745         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15746             "up" : function(e){
15747                 this.inKeyMode = true;
15748                 this.selectPrev();
15749             },
15750
15751             "down" : function(e){
15752                 if(!this.isExpanded()){
15753                     this.onTriggerClick();
15754                 }else{
15755                     this.inKeyMode = true;
15756                     this.selectNext();
15757                 }
15758             },
15759
15760             "enter" : function(e){
15761 //                this.onViewClick();
15762                 //return true;
15763                 this.collapse();
15764                 
15765                 if(this.fireEvent("specialkey", this, e)){
15766                     this.onViewClick(false);
15767                 }
15768                 
15769                 return true;
15770             },
15771
15772             "esc" : function(e){
15773                 this.collapse();
15774             },
15775
15776             "tab" : function(e){
15777                 this.collapse();
15778                 
15779                 if(this.fireEvent("specialkey", this, e)){
15780                     this.onViewClick(false);
15781                 }
15782                 
15783                 return true;
15784             },
15785
15786             scope : this,
15787
15788             doRelay : function(foo, bar, hname){
15789                 if(hname == 'down' || this.scope.isExpanded()){
15790                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15791                 }
15792                 return true;
15793             },
15794
15795             forceKeyDown: true
15796         });
15797         
15798         
15799         this.queryDelay = Math.max(this.queryDelay || 10,
15800                 this.mode == 'local' ? 10 : 250);
15801         
15802         
15803         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15804         
15805         if(this.typeAhead){
15806             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15807         }
15808         if(this.editable !== false){
15809             this.inputEl().on("keyup", this.onKeyUp, this);
15810         }
15811         if(this.forceSelection){
15812             this.inputEl().on('blur', this.doForce, this);
15813         }
15814         
15815         if(this.multiple){
15816             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15817             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15818         }
15819     },
15820     
15821     initTickableEvents: function()
15822     {   
15823         this.createList();
15824         
15825         if(this.hiddenName){
15826             
15827             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15828             
15829             this.hiddenField.dom.value =
15830                 this.hiddenValue !== undefined ? this.hiddenValue :
15831                 this.value !== undefined ? this.value : '';
15832
15833             // prevent input submission
15834             this.el.dom.removeAttribute('name');
15835             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15836              
15837              
15838         }
15839         
15840 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15841         
15842         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15843         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15844         if(this.triggerList){
15845             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15846         }
15847          
15848         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15849         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15850         
15851         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15852         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15853         
15854         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15855         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15856         
15857         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15858         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15859         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15860         
15861         this.okBtn.hide();
15862         this.cancelBtn.hide();
15863         
15864         var _this = this;
15865         
15866         (function(){
15867             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15868             _this.list.setWidth(lw);
15869         }).defer(100);
15870         
15871         this.list.on('mouseover', this.onViewOver, this);
15872         this.list.on('mousemove', this.onViewMove, this);
15873         
15874         this.list.on('scroll', this.onViewScroll, this);
15875         
15876         if(!this.tpl){
15877             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15878                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15879         }
15880
15881         this.view = new Roo.View(this.list, this.tpl, {
15882             singleSelect:true,
15883             tickable:true,
15884             parent:this,
15885             store: this.store,
15886             selectedClass: this.selectedClass
15887         });
15888         
15889         //this.view.wrapEl.setDisplayed(false);
15890         this.view.on('click', this.onViewClick, this);
15891         
15892         
15893         
15894         this.store.on('beforeload', this.onBeforeLoad, this);
15895         this.store.on('load', this.onLoad, this);
15896         this.store.on('loadexception', this.onLoadException, this);
15897         
15898         if(this.editable){
15899             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15900                 "up" : function(e){
15901                     this.inKeyMode = true;
15902                     this.selectPrev();
15903                 },
15904
15905                 "down" : function(e){
15906                     this.inKeyMode = true;
15907                     this.selectNext();
15908                 },
15909
15910                 "enter" : function(e){
15911                     if(this.fireEvent("specialkey", this, e)){
15912                         this.onViewClick(false);
15913                     }
15914                     
15915                     return true;
15916                 },
15917
15918                 "esc" : function(e){
15919                     this.onTickableFooterButtonClick(e, false, false);
15920                 },
15921
15922                 "tab" : function(e){
15923                     this.fireEvent("specialkey", this, e);
15924                     
15925                     this.onTickableFooterButtonClick(e, false, false);
15926                     
15927                     return true;
15928                 },
15929
15930                 scope : this,
15931
15932                 doRelay : function(e, fn, key){
15933                     if(this.scope.isExpanded()){
15934                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15935                     }
15936                     return true;
15937                 },
15938
15939                 forceKeyDown: true
15940             });
15941         }
15942         
15943         this.queryDelay = Math.max(this.queryDelay || 10,
15944                 this.mode == 'local' ? 10 : 250);
15945         
15946         
15947         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15948         
15949         if(this.typeAhead){
15950             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15951         }
15952         
15953         if(this.editable !== false){
15954             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15955         }
15956         
15957         this.indicator = this.indicatorEl();
15958         
15959         if(this.indicator){
15960             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15961             this.indicator.hide();
15962         }
15963         
15964     },
15965
15966     onDestroy : function(){
15967         if(this.view){
15968             this.view.setStore(null);
15969             this.view.el.removeAllListeners();
15970             this.view.el.remove();
15971             this.view.purgeListeners();
15972         }
15973         if(this.list){
15974             this.list.dom.innerHTML  = '';
15975         }
15976         
15977         if(this.store){
15978             this.store.un('beforeload', this.onBeforeLoad, this);
15979             this.store.un('load', this.onLoad, this);
15980             this.store.un('loadexception', this.onLoadException, this);
15981         }
15982         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15983     },
15984
15985     // private
15986     fireKey : function(e){
15987         if(e.isNavKeyPress() && !this.list.isVisible()){
15988             this.fireEvent("specialkey", this, e);
15989         }
15990     },
15991
15992     // private
15993     onResize: function(w, h)
15994     {
15995         
15996         
15997 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15998 //        
15999 //        if(typeof w != 'number'){
16000 //            // we do not handle it!?!?
16001 //            return;
16002 //        }
16003 //        var tw = this.trigger.getWidth();
16004 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16005 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16006 //        var x = w - tw;
16007 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16008 //            
16009 //        //this.trigger.setStyle('left', x+'px');
16010 //        
16011 //        if(this.list && this.listWidth === undefined){
16012 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16013 //            this.list.setWidth(lw);
16014 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16015 //        }
16016         
16017     
16018         
16019     },
16020
16021     /**
16022      * Allow or prevent the user from directly editing the field text.  If false is passed,
16023      * the user will only be able to select from the items defined in the dropdown list.  This method
16024      * is the runtime equivalent of setting the 'editable' config option at config time.
16025      * @param {Boolean} value True to allow the user to directly edit the field text
16026      */
16027     setEditable : function(value){
16028         if(value == this.editable){
16029             return;
16030         }
16031         this.editable = value;
16032         if(!value){
16033             this.inputEl().dom.setAttribute('readOnly', true);
16034             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16035             this.inputEl().addClass('x-combo-noedit');
16036         }else{
16037             this.inputEl().dom.setAttribute('readOnly', false);
16038             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16039             this.inputEl().removeClass('x-combo-noedit');
16040         }
16041     },
16042
16043     // private
16044     
16045     onBeforeLoad : function(combo,opts){
16046         if(!this.hasFocus){
16047             return;
16048         }
16049          if (!opts.add) {
16050             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16051          }
16052         this.restrictHeight();
16053         this.selectedIndex = -1;
16054     },
16055
16056     // private
16057     onLoad : function(){
16058         
16059         this.hasQuery = false;
16060         
16061         if(!this.hasFocus){
16062             return;
16063         }
16064         
16065         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16066             this.loading.hide();
16067         }
16068         
16069         if(this.store.getCount() > 0){
16070             
16071             this.expand();
16072             this.restrictHeight();
16073             if(this.lastQuery == this.allQuery){
16074                 if(this.editable && !this.tickable){
16075                     this.inputEl().dom.select();
16076                 }
16077                 
16078                 if(
16079                     !this.selectByValue(this.value, true) &&
16080                     this.autoFocus && 
16081                     (
16082                         !this.store.lastOptions ||
16083                         typeof(this.store.lastOptions.add) == 'undefined' || 
16084                         this.store.lastOptions.add != true
16085                     )
16086                 ){
16087                     this.select(0, true);
16088                 }
16089             }else{
16090                 if(this.autoFocus){
16091                     this.selectNext();
16092                 }
16093                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16094                     this.taTask.delay(this.typeAheadDelay);
16095                 }
16096             }
16097         }else{
16098             this.onEmptyResults();
16099         }
16100         
16101         //this.el.focus();
16102     },
16103     // private
16104     onLoadException : function()
16105     {
16106         this.hasQuery = false;
16107         
16108         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16109             this.loading.hide();
16110         }
16111         
16112         if(this.tickable && this.editable){
16113             return;
16114         }
16115         
16116         this.collapse();
16117         // only causes errors at present
16118         //Roo.log(this.store.reader.jsonData);
16119         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16120             // fixme
16121             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16122         //}
16123         
16124         
16125     },
16126     // private
16127     onTypeAhead : function(){
16128         if(this.store.getCount() > 0){
16129             var r = this.store.getAt(0);
16130             var newValue = r.data[this.displayField];
16131             var len = newValue.length;
16132             var selStart = this.getRawValue().length;
16133             
16134             if(selStart != len){
16135                 this.setRawValue(newValue);
16136                 this.selectText(selStart, newValue.length);
16137             }
16138         }
16139     },
16140
16141     // private
16142     onSelect : function(record, index){
16143         
16144         if(this.fireEvent('beforeselect', this, record, index) !== false){
16145         
16146             this.setFromData(index > -1 ? record.data : false);
16147             
16148             this.collapse();
16149             this.fireEvent('select', this, record, index);
16150         }
16151     },
16152
16153     /**
16154      * Returns the currently selected field value or empty string if no value is set.
16155      * @return {String} value The selected value
16156      */
16157     getValue : function()
16158     {
16159         if(Roo.isIOS && this.useNativeIOS){
16160             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16161         }
16162         
16163         if(this.multiple){
16164             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16165         }
16166         
16167         if(this.valueField){
16168             return typeof this.value != 'undefined' ? this.value : '';
16169         }else{
16170             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16171         }
16172     },
16173     
16174     getRawValue : function()
16175     {
16176         if(Roo.isIOS && this.useNativeIOS){
16177             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16178         }
16179         
16180         var v = this.inputEl().getValue();
16181         
16182         return v;
16183     },
16184
16185     /**
16186      * Clears any text/value currently set in the field
16187      */
16188     clearValue : function(){
16189         
16190         if(this.hiddenField){
16191             this.hiddenField.dom.value = '';
16192         }
16193         this.value = '';
16194         this.setRawValue('');
16195         this.lastSelectionText = '';
16196         this.lastData = false;
16197         
16198         var close = this.closeTriggerEl();
16199         
16200         if(close){
16201             close.hide();
16202         }
16203         
16204         this.validate();
16205         
16206     },
16207
16208     /**
16209      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16210      * will be displayed in the field.  If the value does not match the data value of an existing item,
16211      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16212      * Otherwise the field will be blank (although the value will still be set).
16213      * @param {String} value The value to match
16214      */
16215     setValue : function(v)
16216     {
16217         if(Roo.isIOS && this.useNativeIOS){
16218             this.setIOSValue(v);
16219             return;
16220         }
16221         
16222         if(this.multiple){
16223             this.syncValue();
16224             return;
16225         }
16226         
16227         var text = v;
16228         if(this.valueField){
16229             var r = this.findRecord(this.valueField, v);
16230             if(r){
16231                 text = r.data[this.displayField];
16232             }else if(this.valueNotFoundText !== undefined){
16233                 text = this.valueNotFoundText;
16234             }
16235         }
16236         this.lastSelectionText = text;
16237         if(this.hiddenField){
16238             this.hiddenField.dom.value = v;
16239         }
16240         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16241         this.value = v;
16242         
16243         var close = this.closeTriggerEl();
16244         
16245         if(close){
16246             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16247         }
16248         
16249         this.validate();
16250     },
16251     /**
16252      * @property {Object} the last set data for the element
16253      */
16254     
16255     lastData : false,
16256     /**
16257      * Sets the value of the field based on a object which is related to the record format for the store.
16258      * @param {Object} value the value to set as. or false on reset?
16259      */
16260     setFromData : function(o){
16261         
16262         if(this.multiple){
16263             this.addItem(o);
16264             return;
16265         }
16266             
16267         var dv = ''; // display value
16268         var vv = ''; // value value..
16269         this.lastData = o;
16270         if (this.displayField) {
16271             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16272         } else {
16273             // this is an error condition!!!
16274             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16275         }
16276         
16277         if(this.valueField){
16278             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16279         }
16280         
16281         var close = this.closeTriggerEl();
16282         
16283         if(close){
16284             if(dv.length || vv * 1 > 0){
16285                 close.show() ;
16286                 this.blockFocus=true;
16287             } else {
16288                 close.hide();
16289             }             
16290         }
16291         
16292         if(this.hiddenField){
16293             this.hiddenField.dom.value = vv;
16294             
16295             this.lastSelectionText = dv;
16296             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16297             this.value = vv;
16298             return;
16299         }
16300         // no hidden field.. - we store the value in 'value', but still display
16301         // display field!!!!
16302         this.lastSelectionText = dv;
16303         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16304         this.value = vv;
16305         
16306         
16307         
16308     },
16309     // private
16310     reset : function(){
16311         // overridden so that last data is reset..
16312         
16313         if(this.multiple){
16314             this.clearItem();
16315             return;
16316         }
16317         
16318         this.setValue(this.originalValue);
16319         //this.clearInvalid();
16320         this.lastData = false;
16321         if (this.view) {
16322             this.view.clearSelections();
16323         }
16324         
16325         this.validate();
16326     },
16327     // private
16328     findRecord : function(prop, value){
16329         var record;
16330         if(this.store.getCount() > 0){
16331             this.store.each(function(r){
16332                 if(r.data[prop] == value){
16333                     record = r;
16334                     return false;
16335                 }
16336                 return true;
16337             });
16338         }
16339         return record;
16340     },
16341     
16342     getName: function()
16343     {
16344         // returns hidden if it's set..
16345         if (!this.rendered) {return ''};
16346         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16347         
16348     },
16349     // private
16350     onViewMove : function(e, t){
16351         this.inKeyMode = false;
16352     },
16353
16354     // private
16355     onViewOver : function(e, t){
16356         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16357             return;
16358         }
16359         var item = this.view.findItemFromChild(t);
16360         
16361         if(item){
16362             var index = this.view.indexOf(item);
16363             this.select(index, false);
16364         }
16365     },
16366
16367     // private
16368     onViewClick : function(view, doFocus, el, e)
16369     {
16370         var index = this.view.getSelectedIndexes()[0];
16371         
16372         var r = this.store.getAt(index);
16373         
16374         if(this.tickable){
16375             
16376             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16377                 return;
16378             }
16379             
16380             var rm = false;
16381             var _this = this;
16382             
16383             Roo.each(this.tickItems, function(v,k){
16384                 
16385                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16386                     Roo.log(v);
16387                     _this.tickItems.splice(k, 1);
16388                     
16389                     if(typeof(e) == 'undefined' && view == false){
16390                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16391                     }
16392                     
16393                     rm = true;
16394                     return;
16395                 }
16396             });
16397             
16398             if(rm){
16399                 return;
16400             }
16401             
16402             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16403                 this.tickItems.push(r.data);
16404             }
16405             
16406             if(typeof(e) == 'undefined' && view == false){
16407                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16408             }
16409                     
16410             return;
16411         }
16412         
16413         if(r){
16414             this.onSelect(r, index);
16415         }
16416         if(doFocus !== false && !this.blockFocus){
16417             this.inputEl().focus();
16418         }
16419     },
16420
16421     // private
16422     restrictHeight : function(){
16423         //this.innerList.dom.style.height = '';
16424         //var inner = this.innerList.dom;
16425         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16426         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16427         //this.list.beginUpdate();
16428         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16429         this.list.alignTo(this.inputEl(), this.listAlign);
16430         this.list.alignTo(this.inputEl(), this.listAlign);
16431         //this.list.endUpdate();
16432     },
16433
16434     // private
16435     onEmptyResults : function(){
16436         
16437         if(this.tickable && this.editable){
16438             this.hasFocus = false;
16439             this.restrictHeight();
16440             return;
16441         }
16442         
16443         this.collapse();
16444     },
16445
16446     /**
16447      * Returns true if the dropdown list is expanded, else false.
16448      */
16449     isExpanded : function(){
16450         return this.list.isVisible();
16451     },
16452
16453     /**
16454      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16455      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16456      * @param {String} value The data value of the item to select
16457      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16458      * selected item if it is not currently in view (defaults to true)
16459      * @return {Boolean} True if the value matched an item in the list, else false
16460      */
16461     selectByValue : function(v, scrollIntoView){
16462         if(v !== undefined && v !== null){
16463             var r = this.findRecord(this.valueField || this.displayField, v);
16464             if(r){
16465                 this.select(this.store.indexOf(r), scrollIntoView);
16466                 return true;
16467             }
16468         }
16469         return false;
16470     },
16471
16472     /**
16473      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16474      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16475      * @param {Number} index The zero-based index of the list item to select
16476      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16477      * selected item if it is not currently in view (defaults to true)
16478      */
16479     select : function(index, scrollIntoView){
16480         this.selectedIndex = index;
16481         this.view.select(index);
16482         if(scrollIntoView !== false){
16483             var el = this.view.getNode(index);
16484             /*
16485              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16486              */
16487             if(el){
16488                 this.list.scrollChildIntoView(el, false);
16489             }
16490         }
16491     },
16492
16493     // private
16494     selectNext : function(){
16495         var ct = this.store.getCount();
16496         if(ct > 0){
16497             if(this.selectedIndex == -1){
16498                 this.select(0);
16499             }else if(this.selectedIndex < ct-1){
16500                 this.select(this.selectedIndex+1);
16501             }
16502         }
16503     },
16504
16505     // private
16506     selectPrev : function(){
16507         var ct = this.store.getCount();
16508         if(ct > 0){
16509             if(this.selectedIndex == -1){
16510                 this.select(0);
16511             }else if(this.selectedIndex != 0){
16512                 this.select(this.selectedIndex-1);
16513             }
16514         }
16515     },
16516
16517     // private
16518     onKeyUp : function(e){
16519         if(this.editable !== false && !e.isSpecialKey()){
16520             this.lastKey = e.getKey();
16521             this.dqTask.delay(this.queryDelay);
16522         }
16523     },
16524
16525     // private
16526     validateBlur : function(){
16527         return !this.list || !this.list.isVisible();   
16528     },
16529
16530     // private
16531     initQuery : function(){
16532         
16533         var v = this.getRawValue();
16534         
16535         if(this.tickable && this.editable){
16536             v = this.tickableInputEl().getValue();
16537         }
16538         
16539         this.doQuery(v);
16540     },
16541
16542     // private
16543     doForce : function(){
16544         if(this.inputEl().dom.value.length > 0){
16545             this.inputEl().dom.value =
16546                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16547              
16548         }
16549     },
16550
16551     /**
16552      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16553      * query allowing the query action to be canceled if needed.
16554      * @param {String} query The SQL query to execute
16555      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16556      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16557      * saved in the current store (defaults to false)
16558      */
16559     doQuery : function(q, forceAll){
16560         
16561         if(q === undefined || q === null){
16562             q = '';
16563         }
16564         var qe = {
16565             query: q,
16566             forceAll: forceAll,
16567             combo: this,
16568             cancel:false
16569         };
16570         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16571             return false;
16572         }
16573         q = qe.query;
16574         
16575         forceAll = qe.forceAll;
16576         if(forceAll === true || (q.length >= this.minChars)){
16577             
16578             this.hasQuery = true;
16579             
16580             if(this.lastQuery != q || this.alwaysQuery){
16581                 this.lastQuery = q;
16582                 if(this.mode == 'local'){
16583                     this.selectedIndex = -1;
16584                     if(forceAll){
16585                         this.store.clearFilter();
16586                     }else{
16587                         
16588                         if(this.specialFilter){
16589                             this.fireEvent('specialfilter', this);
16590                             this.onLoad();
16591                             return;
16592                         }
16593                         
16594                         this.store.filter(this.displayField, q);
16595                     }
16596                     
16597                     this.store.fireEvent("datachanged", this.store);
16598                     
16599                     this.onLoad();
16600                     
16601                     
16602                 }else{
16603                     
16604                     this.store.baseParams[this.queryParam] = q;
16605                     
16606                     var options = {params : this.getParams(q)};
16607                     
16608                     if(this.loadNext){
16609                         options.add = true;
16610                         options.params.start = this.page * this.pageSize;
16611                     }
16612                     
16613                     this.store.load(options);
16614                     
16615                     /*
16616                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16617                      *  we should expand the list on onLoad
16618                      *  so command out it
16619                      */
16620 //                    this.expand();
16621                 }
16622             }else{
16623                 this.selectedIndex = -1;
16624                 this.onLoad();   
16625             }
16626         }
16627         
16628         this.loadNext = false;
16629     },
16630     
16631     // private
16632     getParams : function(q){
16633         var p = {};
16634         //p[this.queryParam] = q;
16635         
16636         if(this.pageSize){
16637             p.start = 0;
16638             p.limit = this.pageSize;
16639         }
16640         return p;
16641     },
16642
16643     /**
16644      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16645      */
16646     collapse : function(){
16647         if(!this.isExpanded()){
16648             return;
16649         }
16650         
16651         this.list.hide();
16652         
16653         this.hasFocus = false;
16654         
16655         if(this.tickable){
16656             this.okBtn.hide();
16657             this.cancelBtn.hide();
16658             this.trigger.show();
16659             
16660             if(this.editable){
16661                 this.tickableInputEl().dom.value = '';
16662                 this.tickableInputEl().blur();
16663             }
16664             
16665         }
16666         
16667         Roo.get(document).un('mousedown', this.collapseIf, this);
16668         Roo.get(document).un('mousewheel', this.collapseIf, this);
16669         if (!this.editable) {
16670             Roo.get(document).un('keydown', this.listKeyPress, this);
16671         }
16672         this.fireEvent('collapse', this);
16673         
16674         this.validate();
16675     },
16676
16677     // private
16678     collapseIf : function(e){
16679         var in_combo  = e.within(this.el);
16680         var in_list =  e.within(this.list);
16681         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16682         
16683         if (in_combo || in_list || is_list) {
16684             //e.stopPropagation();
16685             return;
16686         }
16687         
16688         if(this.tickable){
16689             this.onTickableFooterButtonClick(e, false, false);
16690         }
16691
16692         this.collapse();
16693         
16694     },
16695
16696     /**
16697      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16698      */
16699     expand : function(){
16700        
16701         if(this.isExpanded() || !this.hasFocus){
16702             return;
16703         }
16704         
16705         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16706         this.list.setWidth(lw);
16707         
16708         Roo.log('expand');
16709         
16710         this.list.show();
16711         
16712         this.restrictHeight();
16713         
16714         if(this.tickable){
16715             
16716             this.tickItems = Roo.apply([], this.item);
16717             
16718             this.okBtn.show();
16719             this.cancelBtn.show();
16720             this.trigger.hide();
16721             
16722             if(this.editable){
16723                 this.tickableInputEl().focus();
16724             }
16725             
16726         }
16727         
16728         Roo.get(document).on('mousedown', this.collapseIf, this);
16729         Roo.get(document).on('mousewheel', this.collapseIf, this);
16730         if (!this.editable) {
16731             Roo.get(document).on('keydown', this.listKeyPress, this);
16732         }
16733         
16734         this.fireEvent('expand', this);
16735     },
16736
16737     // private
16738     // Implements the default empty TriggerField.onTriggerClick function
16739     onTriggerClick : function(e)
16740     {
16741         Roo.log('trigger click');
16742         
16743         if(this.disabled || !this.triggerList){
16744             return;
16745         }
16746         
16747         this.page = 0;
16748         this.loadNext = false;
16749         
16750         if(this.isExpanded()){
16751             this.collapse();
16752             if (!this.blockFocus) {
16753                 this.inputEl().focus();
16754             }
16755             
16756         }else {
16757             this.hasFocus = true;
16758             if(this.triggerAction == 'all') {
16759                 this.doQuery(this.allQuery, true);
16760             } else {
16761                 this.doQuery(this.getRawValue());
16762             }
16763             if (!this.blockFocus) {
16764                 this.inputEl().focus();
16765             }
16766         }
16767     },
16768     
16769     onTickableTriggerClick : function(e)
16770     {
16771         if(this.disabled){
16772             return;
16773         }
16774         
16775         this.page = 0;
16776         this.loadNext = false;
16777         this.hasFocus = true;
16778         
16779         if(this.triggerAction == 'all') {
16780             this.doQuery(this.allQuery, true);
16781         } else {
16782             this.doQuery(this.getRawValue());
16783         }
16784     },
16785     
16786     onSearchFieldClick : function(e)
16787     {
16788         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16789             this.onTickableFooterButtonClick(e, false, false);
16790             return;
16791         }
16792         
16793         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16794             return;
16795         }
16796         
16797         this.page = 0;
16798         this.loadNext = false;
16799         this.hasFocus = true;
16800         
16801         if(this.triggerAction == 'all') {
16802             this.doQuery(this.allQuery, true);
16803         } else {
16804             this.doQuery(this.getRawValue());
16805         }
16806     },
16807     
16808     listKeyPress : function(e)
16809     {
16810         //Roo.log('listkeypress');
16811         // scroll to first matching element based on key pres..
16812         if (e.isSpecialKey()) {
16813             return false;
16814         }
16815         var k = String.fromCharCode(e.getKey()).toUpperCase();
16816         //Roo.log(k);
16817         var match  = false;
16818         var csel = this.view.getSelectedNodes();
16819         var cselitem = false;
16820         if (csel.length) {
16821             var ix = this.view.indexOf(csel[0]);
16822             cselitem  = this.store.getAt(ix);
16823             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16824                 cselitem = false;
16825             }
16826             
16827         }
16828         
16829         this.store.each(function(v) { 
16830             if (cselitem) {
16831                 // start at existing selection.
16832                 if (cselitem.id == v.id) {
16833                     cselitem = false;
16834                 }
16835                 return true;
16836             }
16837                 
16838             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16839                 match = this.store.indexOf(v);
16840                 return false;
16841             }
16842             return true;
16843         }, this);
16844         
16845         if (match === false) {
16846             return true; // no more action?
16847         }
16848         // scroll to?
16849         this.view.select(match);
16850         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16851         sn.scrollIntoView(sn.dom.parentNode, false);
16852     },
16853     
16854     onViewScroll : function(e, t){
16855         
16856         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){
16857             return;
16858         }
16859         
16860         this.hasQuery = true;
16861         
16862         this.loading = this.list.select('.loading', true).first();
16863         
16864         if(this.loading === null){
16865             this.list.createChild({
16866                 tag: 'div',
16867                 cls: 'loading roo-select2-more-results roo-select2-active',
16868                 html: 'Loading more results...'
16869             });
16870             
16871             this.loading = this.list.select('.loading', true).first();
16872             
16873             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16874             
16875             this.loading.hide();
16876         }
16877         
16878         this.loading.show();
16879         
16880         var _combo = this;
16881         
16882         this.page++;
16883         this.loadNext = true;
16884         
16885         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16886         
16887         return;
16888     },
16889     
16890     addItem : function(o)
16891     {   
16892         var dv = ''; // display value
16893         
16894         if (this.displayField) {
16895             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16896         } else {
16897             // this is an error condition!!!
16898             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16899         }
16900         
16901         if(!dv.length){
16902             return;
16903         }
16904         
16905         var choice = this.choices.createChild({
16906             tag: 'li',
16907             cls: 'roo-select2-search-choice',
16908             cn: [
16909                 {
16910                     tag: 'div',
16911                     html: dv
16912                 },
16913                 {
16914                     tag: 'a',
16915                     href: '#',
16916                     cls: 'roo-select2-search-choice-close fa fa-times',
16917                     tabindex: '-1'
16918                 }
16919             ]
16920             
16921         }, this.searchField);
16922         
16923         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16924         
16925         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16926         
16927         this.item.push(o);
16928         
16929         this.lastData = o;
16930         
16931         this.syncValue();
16932         
16933         this.inputEl().dom.value = '';
16934         
16935         this.validate();
16936     },
16937     
16938     onRemoveItem : function(e, _self, o)
16939     {
16940         e.preventDefault();
16941         
16942         this.lastItem = Roo.apply([], this.item);
16943         
16944         var index = this.item.indexOf(o.data) * 1;
16945         
16946         if( index < 0){
16947             Roo.log('not this item?!');
16948             return;
16949         }
16950         
16951         this.item.splice(index, 1);
16952         o.item.remove();
16953         
16954         this.syncValue();
16955         
16956         this.fireEvent('remove', this, e);
16957         
16958         this.validate();
16959         
16960     },
16961     
16962     syncValue : function()
16963     {
16964         if(!this.item.length){
16965             this.clearValue();
16966             return;
16967         }
16968             
16969         var value = [];
16970         var _this = this;
16971         Roo.each(this.item, function(i){
16972             if(_this.valueField){
16973                 value.push(i[_this.valueField]);
16974                 return;
16975             }
16976
16977             value.push(i);
16978         });
16979
16980         this.value = value.join(',');
16981
16982         if(this.hiddenField){
16983             this.hiddenField.dom.value = this.value;
16984         }
16985         
16986         this.store.fireEvent("datachanged", this.store);
16987         
16988         this.validate();
16989     },
16990     
16991     clearItem : function()
16992     {
16993         if(!this.multiple){
16994             return;
16995         }
16996         
16997         this.item = [];
16998         
16999         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17000            c.remove();
17001         });
17002         
17003         this.syncValue();
17004         
17005         this.validate();
17006         
17007         if(this.tickable && !Roo.isTouch){
17008             this.view.refresh();
17009         }
17010     },
17011     
17012     inputEl: function ()
17013     {
17014         if(Roo.isIOS && this.useNativeIOS){
17015             return this.el.select('select.roo-ios-select', true).first();
17016         }
17017         
17018         if(Roo.isTouch && this.mobileTouchView){
17019             return this.el.select('input.form-control',true).first();
17020         }
17021         
17022         if(this.tickable){
17023             return this.searchField;
17024         }
17025         
17026         return this.el.select('input.form-control',true).first();
17027     },
17028     
17029     onTickableFooterButtonClick : function(e, btn, el)
17030     {
17031         e.preventDefault();
17032         
17033         this.lastItem = Roo.apply([], this.item);
17034         
17035         if(btn && btn.name == 'cancel'){
17036             this.tickItems = Roo.apply([], this.item);
17037             this.collapse();
17038             return;
17039         }
17040         
17041         this.clearItem();
17042         
17043         var _this = this;
17044         
17045         Roo.each(this.tickItems, function(o){
17046             _this.addItem(o);
17047         });
17048         
17049         this.collapse();
17050         
17051     },
17052     
17053     validate : function()
17054     {
17055         if(this.getVisibilityEl().hasClass('hidden')){
17056             return true;
17057         }
17058         
17059         var v = this.getRawValue();
17060         
17061         if(this.multiple){
17062             v = this.getValue();
17063         }
17064         
17065         if(this.disabled || this.allowBlank || v.length){
17066             this.markValid();
17067             return true;
17068         }
17069         
17070         this.markInvalid();
17071         return false;
17072     },
17073     
17074     tickableInputEl : function()
17075     {
17076         if(!this.tickable || !this.editable){
17077             return this.inputEl();
17078         }
17079         
17080         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17081     },
17082     
17083     
17084     getAutoCreateTouchView : function()
17085     {
17086         var id = Roo.id();
17087         
17088         var cfg = {
17089             cls: 'form-group' //input-group
17090         };
17091         
17092         var input =  {
17093             tag: 'input',
17094             id : id,
17095             type : this.inputType,
17096             cls : 'form-control x-combo-noedit',
17097             autocomplete: 'new-password',
17098             placeholder : this.placeholder || '',
17099             readonly : true
17100         };
17101         
17102         if (this.name) {
17103             input.name = this.name;
17104         }
17105         
17106         if (this.size) {
17107             input.cls += ' input-' + this.size;
17108         }
17109         
17110         if (this.disabled) {
17111             input.disabled = true;
17112         }
17113         
17114         var inputblock = {
17115             cls : 'roo-combobox-wrap',
17116             cn : [
17117                 input
17118             ]
17119         };
17120         
17121         if(this.before){
17122             inputblock.cls += ' input-group';
17123             
17124             inputblock.cn.unshift({
17125                 tag :'span',
17126                 cls : 'input-group-addon input-group-prepend input-group-text',
17127                 html : this.before
17128             });
17129         }
17130         
17131         if(this.removable && !this.multiple){
17132             inputblock.cls += ' roo-removable';
17133             
17134             inputblock.cn.push({
17135                 tag: 'button',
17136                 html : 'x',
17137                 cls : 'roo-combo-removable-btn close'
17138             });
17139         }
17140
17141         if(this.hasFeedback && !this.allowBlank){
17142             
17143             inputblock.cls += ' has-feedback';
17144             
17145             inputblock.cn.push({
17146                 tag: 'span',
17147                 cls: 'glyphicon form-control-feedback'
17148             });
17149             
17150         }
17151         
17152         if (this.after) {
17153             
17154             inputblock.cls += (this.before) ? '' : ' input-group';
17155             
17156             inputblock.cn.push({
17157                 tag :'span',
17158                 cls : 'input-group-addon input-group-append input-group-text',
17159                 html : this.after
17160             });
17161         }
17162
17163         
17164         var ibwrap = inputblock;
17165         
17166         if(this.multiple){
17167             ibwrap = {
17168                 tag: 'ul',
17169                 cls: 'roo-select2-choices',
17170                 cn:[
17171                     {
17172                         tag: 'li',
17173                         cls: 'roo-select2-search-field',
17174                         cn: [
17175
17176                             inputblock
17177                         ]
17178                     }
17179                 ]
17180             };
17181         
17182             
17183         }
17184         
17185         var combobox = {
17186             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17187             cn: [
17188                 {
17189                     tag: 'input',
17190                     type : 'hidden',
17191                     cls: 'form-hidden-field'
17192                 },
17193                 ibwrap
17194             ]
17195         };
17196         
17197         if(!this.multiple && this.showToggleBtn){
17198             
17199             var caret = {
17200                 cls: 'caret'
17201             };
17202             
17203             if (this.caret != false) {
17204                 caret = {
17205                      tag: 'i',
17206                      cls: 'fa fa-' + this.caret
17207                 };
17208                 
17209             }
17210             
17211             combobox.cn.push({
17212                 tag :'span',
17213                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17214                 cn : [
17215                     Roo.bootstrap.version == 3 ? caret : '',
17216                     {
17217                         tag: 'span',
17218                         cls: 'combobox-clear',
17219                         cn  : [
17220                             {
17221                                 tag : 'i',
17222                                 cls: 'icon-remove'
17223                             }
17224                         ]
17225                     }
17226                 ]
17227
17228             })
17229         }
17230         
17231         if(this.multiple){
17232             combobox.cls += ' roo-select2-container-multi';
17233         }
17234         
17235         var align = this.labelAlign || this.parentLabelAlign();
17236         
17237         if (align ==='left' && this.fieldLabel.length) {
17238
17239             cfg.cn = [
17240                 {
17241                    tag : 'i',
17242                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17243                    tooltip : 'This field is required'
17244                 },
17245                 {
17246                     tag: 'label',
17247                     cls : 'control-label col-form-label',
17248                     html : this.fieldLabel
17249
17250                 },
17251                 {
17252                     cls : 'roo-combobox-wrap ', 
17253                     cn: [
17254                         combobox
17255                     ]
17256                 }
17257             ];
17258             
17259             var labelCfg = cfg.cn[1];
17260             var contentCfg = cfg.cn[2];
17261             
17262
17263             if(this.indicatorpos == 'right'){
17264                 cfg.cn = [
17265                     {
17266                         tag: 'label',
17267                         'for' :  id,
17268                         cls : 'control-label col-form-label',
17269                         cn : [
17270                             {
17271                                 tag : 'span',
17272                                 html : this.fieldLabel
17273                             },
17274                             {
17275                                 tag : 'i',
17276                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17277                                 tooltip : 'This field is required'
17278                             }
17279                         ]
17280                     },
17281                     {
17282                         cls : "roo-combobox-wrap ",
17283                         cn: [
17284                             combobox
17285                         ]
17286                     }
17287
17288                 ];
17289                 
17290                 labelCfg = cfg.cn[0];
17291                 contentCfg = cfg.cn[1];
17292             }
17293             
17294            
17295             
17296             if(this.labelWidth > 12){
17297                 labelCfg.style = "width: " + this.labelWidth + 'px';
17298             }
17299            
17300             if(this.labelWidth < 13 && this.labelmd == 0){
17301                 this.labelmd = this.labelWidth;
17302             }
17303             
17304             if(this.labellg > 0){
17305                 labelCfg.cls += ' col-lg-' + this.labellg;
17306                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17307             }
17308             
17309             if(this.labelmd > 0){
17310                 labelCfg.cls += ' col-md-' + this.labelmd;
17311                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17312             }
17313             
17314             if(this.labelsm > 0){
17315                 labelCfg.cls += ' col-sm-' + this.labelsm;
17316                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17317             }
17318             
17319             if(this.labelxs > 0){
17320                 labelCfg.cls += ' col-xs-' + this.labelxs;
17321                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17322             }
17323                 
17324                 
17325         } else if ( this.fieldLabel.length) {
17326             cfg.cn = [
17327                 {
17328                    tag : 'i',
17329                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17330                    tooltip : 'This field is required'
17331                 },
17332                 {
17333                     tag: 'label',
17334                     cls : 'control-label',
17335                     html : this.fieldLabel
17336
17337                 },
17338                 {
17339                     cls : '', 
17340                     cn: [
17341                         combobox
17342                     ]
17343                 }
17344             ];
17345             
17346             if(this.indicatorpos == 'right'){
17347                 cfg.cn = [
17348                     {
17349                         tag: 'label',
17350                         cls : 'control-label',
17351                         html : this.fieldLabel,
17352                         cn : [
17353                             {
17354                                tag : 'i',
17355                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17356                                tooltip : 'This field is required'
17357                             }
17358                         ]
17359                     },
17360                     {
17361                         cls : '', 
17362                         cn: [
17363                             combobox
17364                         ]
17365                     }
17366                 ];
17367             }
17368         } else {
17369             cfg.cn = combobox;    
17370         }
17371         
17372         
17373         var settings = this;
17374         
17375         ['xs','sm','md','lg'].map(function(size){
17376             if (settings[size]) {
17377                 cfg.cls += ' col-' + size + '-' + settings[size];
17378             }
17379         });
17380         
17381         return cfg;
17382     },
17383     
17384     initTouchView : function()
17385     {
17386         this.renderTouchView();
17387         
17388         this.touchViewEl.on('scroll', function(){
17389             this.el.dom.scrollTop = 0;
17390         }, this);
17391         
17392         this.originalValue = this.getValue();
17393         
17394         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17395         
17396         this.inputEl().on("click", this.showTouchView, this);
17397         if (this.triggerEl) {
17398             this.triggerEl.on("click", this.showTouchView, this);
17399         }
17400         
17401         
17402         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17403         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17404         
17405         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17406         
17407         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17408         this.store.on('load', this.onTouchViewLoad, this);
17409         this.store.on('loadexception', this.onTouchViewLoadException, this);
17410         
17411         if(this.hiddenName){
17412             
17413             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17414             
17415             this.hiddenField.dom.value =
17416                 this.hiddenValue !== undefined ? this.hiddenValue :
17417                 this.value !== undefined ? this.value : '';
17418         
17419             this.el.dom.removeAttribute('name');
17420             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17421         }
17422         
17423         if(this.multiple){
17424             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17425             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17426         }
17427         
17428         if(this.removable && !this.multiple){
17429             var close = this.closeTriggerEl();
17430             if(close){
17431                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17432                 close.on('click', this.removeBtnClick, this, close);
17433             }
17434         }
17435         /*
17436          * fix the bug in Safari iOS8
17437          */
17438         this.inputEl().on("focus", function(e){
17439             document.activeElement.blur();
17440         }, this);
17441         
17442         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17443         
17444         return;
17445         
17446         
17447     },
17448     
17449     renderTouchView : function()
17450     {
17451         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17452         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17453         
17454         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17455         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17456         
17457         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17458         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17459         this.touchViewBodyEl.setStyle('overflow', 'auto');
17460         
17461         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17462         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17463         
17464         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17465         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17466         
17467     },
17468     
17469     showTouchView : function()
17470     {
17471         if(this.disabled){
17472             return;
17473         }
17474         
17475         this.touchViewHeaderEl.hide();
17476
17477         if(this.modalTitle.length){
17478             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17479             this.touchViewHeaderEl.show();
17480         }
17481
17482         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17483         this.touchViewEl.show();
17484
17485         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17486         
17487         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17488         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17489
17490         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17491
17492         if(this.modalTitle.length){
17493             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17494         }
17495         
17496         this.touchViewBodyEl.setHeight(bodyHeight);
17497
17498         if(this.animate){
17499             var _this = this;
17500             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17501         }else{
17502             this.touchViewEl.addClass(['in','show']);
17503         }
17504         
17505         if(this._touchViewMask){
17506             Roo.get(document.body).addClass("x-body-masked");
17507             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17508             this._touchViewMask.setStyle('z-index', 10000);
17509             this._touchViewMask.addClass('show');
17510         }
17511         
17512         this.doTouchViewQuery();
17513         
17514     },
17515     
17516     hideTouchView : function()
17517     {
17518         this.touchViewEl.removeClass(['in','show']);
17519
17520         if(this.animate){
17521             var _this = this;
17522             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17523         }else{
17524             this.touchViewEl.setStyle('display', 'none');
17525         }
17526         
17527         if(this._touchViewMask){
17528             this._touchViewMask.removeClass('show');
17529             Roo.get(document.body).removeClass("x-body-masked");
17530         }
17531     },
17532     
17533     setTouchViewValue : function()
17534     {
17535         if(this.multiple){
17536             this.clearItem();
17537         
17538             var _this = this;
17539
17540             Roo.each(this.tickItems, function(o){
17541                 this.addItem(o);
17542             }, this);
17543         }
17544         
17545         this.hideTouchView();
17546     },
17547     
17548     doTouchViewQuery : function()
17549     {
17550         var qe = {
17551             query: '',
17552             forceAll: true,
17553             combo: this,
17554             cancel:false
17555         };
17556         
17557         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17558             return false;
17559         }
17560         
17561         if(!this.alwaysQuery || this.mode == 'local'){
17562             this.onTouchViewLoad();
17563             return;
17564         }
17565         
17566         this.store.load();
17567     },
17568     
17569     onTouchViewBeforeLoad : function(combo,opts)
17570     {
17571         return;
17572     },
17573
17574     // private
17575     onTouchViewLoad : function()
17576     {
17577         if(this.store.getCount() < 1){
17578             this.onTouchViewEmptyResults();
17579             return;
17580         }
17581         
17582         this.clearTouchView();
17583         
17584         var rawValue = this.getRawValue();
17585         
17586         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17587         
17588         this.tickItems = [];
17589         
17590         this.store.data.each(function(d, rowIndex){
17591             var row = this.touchViewListGroup.createChild(template);
17592             
17593             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17594                 row.addClass(d.data.cls);
17595             }
17596             
17597             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17598                 var cfg = {
17599                     data : d.data,
17600                     html : d.data[this.displayField]
17601                 };
17602                 
17603                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17604                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17605                 }
17606             }
17607             row.removeClass('selected');
17608             if(!this.multiple && this.valueField &&
17609                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17610             {
17611                 // radio buttons..
17612                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17613                 row.addClass('selected');
17614             }
17615             
17616             if(this.multiple && this.valueField &&
17617                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17618             {
17619                 
17620                 // checkboxes...
17621                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17622                 this.tickItems.push(d.data);
17623             }
17624             
17625             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17626             
17627         }, this);
17628         
17629         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17630         
17631         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17632
17633         if(this.modalTitle.length){
17634             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17635         }
17636
17637         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17638         
17639         if(this.mobile_restrict_height && listHeight < bodyHeight){
17640             this.touchViewBodyEl.setHeight(listHeight);
17641         }
17642         
17643         var _this = this;
17644         
17645         if(firstChecked && listHeight > bodyHeight){
17646             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17647         }
17648         
17649     },
17650     
17651     onTouchViewLoadException : function()
17652     {
17653         this.hideTouchView();
17654     },
17655     
17656     onTouchViewEmptyResults : function()
17657     {
17658         this.clearTouchView();
17659         
17660         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17661         
17662         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17663         
17664     },
17665     
17666     clearTouchView : function()
17667     {
17668         this.touchViewListGroup.dom.innerHTML = '';
17669     },
17670     
17671     onTouchViewClick : function(e, el, o)
17672     {
17673         e.preventDefault();
17674         
17675         var row = o.row;
17676         var rowIndex = o.rowIndex;
17677         
17678         var r = this.store.getAt(rowIndex);
17679         
17680         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17681             
17682             if(!this.multiple){
17683                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17684                     c.dom.removeAttribute('checked');
17685                 }, this);
17686
17687                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17688
17689                 this.setFromData(r.data);
17690
17691                 var close = this.closeTriggerEl();
17692
17693                 if(close){
17694                     close.show();
17695                 }
17696
17697                 this.hideTouchView();
17698
17699                 this.fireEvent('select', this, r, rowIndex);
17700
17701                 return;
17702             }
17703
17704             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17705                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17706                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17707                 return;
17708             }
17709
17710             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17711             this.addItem(r.data);
17712             this.tickItems.push(r.data);
17713         }
17714     },
17715     
17716     getAutoCreateNativeIOS : function()
17717     {
17718         var cfg = {
17719             cls: 'form-group' //input-group,
17720         };
17721         
17722         var combobox =  {
17723             tag: 'select',
17724             cls : 'roo-ios-select'
17725         };
17726         
17727         if (this.name) {
17728             combobox.name = this.name;
17729         }
17730         
17731         if (this.disabled) {
17732             combobox.disabled = true;
17733         }
17734         
17735         var settings = this;
17736         
17737         ['xs','sm','md','lg'].map(function(size){
17738             if (settings[size]) {
17739                 cfg.cls += ' col-' + size + '-' + settings[size];
17740             }
17741         });
17742         
17743         cfg.cn = combobox;
17744         
17745         return cfg;
17746         
17747     },
17748     
17749     initIOSView : function()
17750     {
17751         this.store.on('load', this.onIOSViewLoad, this);
17752         
17753         return;
17754     },
17755     
17756     onIOSViewLoad : function()
17757     {
17758         if(this.store.getCount() < 1){
17759             return;
17760         }
17761         
17762         this.clearIOSView();
17763         
17764         if(this.allowBlank) {
17765             
17766             var default_text = '-- SELECT --';
17767             
17768             if(this.placeholder.length){
17769                 default_text = this.placeholder;
17770             }
17771             
17772             if(this.emptyTitle.length){
17773                 default_text += ' - ' + this.emptyTitle + ' -';
17774             }
17775             
17776             var opt = this.inputEl().createChild({
17777                 tag: 'option',
17778                 value : 0,
17779                 html : default_text
17780             });
17781             
17782             var o = {};
17783             o[this.valueField] = 0;
17784             o[this.displayField] = default_text;
17785             
17786             this.ios_options.push({
17787                 data : o,
17788                 el : opt
17789             });
17790             
17791         }
17792         
17793         this.store.data.each(function(d, rowIndex){
17794             
17795             var html = '';
17796             
17797             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17798                 html = d.data[this.displayField];
17799             }
17800             
17801             var value = '';
17802             
17803             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17804                 value = d.data[this.valueField];
17805             }
17806             
17807             var option = {
17808                 tag: 'option',
17809                 value : value,
17810                 html : html
17811             };
17812             
17813             if(this.value == d.data[this.valueField]){
17814                 option['selected'] = true;
17815             }
17816             
17817             var opt = this.inputEl().createChild(option);
17818             
17819             this.ios_options.push({
17820                 data : d.data,
17821                 el : opt
17822             });
17823             
17824         }, this);
17825         
17826         this.inputEl().on('change', function(){
17827            this.fireEvent('select', this);
17828         }, this);
17829         
17830     },
17831     
17832     clearIOSView: function()
17833     {
17834         this.inputEl().dom.innerHTML = '';
17835         
17836         this.ios_options = [];
17837     },
17838     
17839     setIOSValue: function(v)
17840     {
17841         this.value = v;
17842         
17843         if(!this.ios_options){
17844             return;
17845         }
17846         
17847         Roo.each(this.ios_options, function(opts){
17848            
17849            opts.el.dom.removeAttribute('selected');
17850            
17851            if(opts.data[this.valueField] != v){
17852                return;
17853            }
17854            
17855            opts.el.dom.setAttribute('selected', true);
17856            
17857         }, this);
17858     }
17859
17860     /** 
17861     * @cfg {Boolean} grow 
17862     * @hide 
17863     */
17864     /** 
17865     * @cfg {Number} growMin 
17866     * @hide 
17867     */
17868     /** 
17869     * @cfg {Number} growMax 
17870     * @hide 
17871     */
17872     /**
17873      * @hide
17874      * @method autoSize
17875      */
17876 });
17877
17878 Roo.apply(Roo.bootstrap.ComboBox,  {
17879     
17880     header : {
17881         tag: 'div',
17882         cls: 'modal-header',
17883         cn: [
17884             {
17885                 tag: 'h4',
17886                 cls: 'modal-title'
17887             }
17888         ]
17889     },
17890     
17891     body : {
17892         tag: 'div',
17893         cls: 'modal-body',
17894         cn: [
17895             {
17896                 tag: 'ul',
17897                 cls: 'list-group'
17898             }
17899         ]
17900     },
17901     
17902     listItemRadio : {
17903         tag: 'li',
17904         cls: 'list-group-item',
17905         cn: [
17906             {
17907                 tag: 'span',
17908                 cls: 'roo-combobox-list-group-item-value'
17909             },
17910             {
17911                 tag: 'div',
17912                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17913                 cn: [
17914                     {
17915                         tag: 'input',
17916                         type: 'radio'
17917                     },
17918                     {
17919                         tag: 'label'
17920                     }
17921                 ]
17922             }
17923         ]
17924     },
17925     
17926     listItemCheckbox : {
17927         tag: 'li',
17928         cls: 'list-group-item',
17929         cn: [
17930             {
17931                 tag: 'span',
17932                 cls: 'roo-combobox-list-group-item-value'
17933             },
17934             {
17935                 tag: 'div',
17936                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17937                 cn: [
17938                     {
17939                         tag: 'input',
17940                         type: 'checkbox'
17941                     },
17942                     {
17943                         tag: 'label'
17944                     }
17945                 ]
17946             }
17947         ]
17948     },
17949     
17950     emptyResult : {
17951         tag: 'div',
17952         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17953     },
17954     
17955     footer : {
17956         tag: 'div',
17957         cls: 'modal-footer',
17958         cn: [
17959             {
17960                 tag: 'div',
17961                 cls: 'row',
17962                 cn: [
17963                     {
17964                         tag: 'div',
17965                         cls: 'col-xs-6 text-left',
17966                         cn: {
17967                             tag: 'button',
17968                             cls: 'btn btn-danger roo-touch-view-cancel',
17969                             html: 'Cancel'
17970                         }
17971                     },
17972                     {
17973                         tag: 'div',
17974                         cls: 'col-xs-6 text-right',
17975                         cn: {
17976                             tag: 'button',
17977                             cls: 'btn btn-success roo-touch-view-ok',
17978                             html: 'OK'
17979                         }
17980                     }
17981                 ]
17982             }
17983         ]
17984         
17985     }
17986 });
17987
17988 Roo.apply(Roo.bootstrap.ComboBox,  {
17989     
17990     touchViewTemplate : {
17991         tag: 'div',
17992         cls: 'modal fade roo-combobox-touch-view',
17993         cn: [
17994             {
17995                 tag: 'div',
17996                 cls: 'modal-dialog',
17997                 style : 'position:fixed', // we have to fix position....
17998                 cn: [
17999                     {
18000                         tag: 'div',
18001                         cls: 'modal-content',
18002                         cn: [
18003                             Roo.bootstrap.ComboBox.header,
18004                             Roo.bootstrap.ComboBox.body,
18005                             Roo.bootstrap.ComboBox.footer
18006                         ]
18007                     }
18008                 ]
18009             }
18010         ]
18011     }
18012 });/*
18013  * Based on:
18014  * Ext JS Library 1.1.1
18015  * Copyright(c) 2006-2007, Ext JS, LLC.
18016  *
18017  * Originally Released Under LGPL - original licence link has changed is not relivant.
18018  *
18019  * Fork - LGPL
18020  * <script type="text/javascript">
18021  */
18022
18023 /**
18024  * @class Roo.View
18025  * @extends Roo.util.Observable
18026  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18027  * This class also supports single and multi selection modes. <br>
18028  * Create a data model bound view:
18029  <pre><code>
18030  var store = new Roo.data.Store(...);
18031
18032  var view = new Roo.View({
18033     el : "my-element",
18034     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18035  
18036     singleSelect: true,
18037     selectedClass: "ydataview-selected",
18038     store: store
18039  });
18040
18041  // listen for node click?
18042  view.on("click", function(vw, index, node, e){
18043  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18044  });
18045
18046  // load XML data
18047  dataModel.load("foobar.xml");
18048  </code></pre>
18049  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18050  * <br><br>
18051  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18052  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18053  * 
18054  * Note: old style constructor is still suported (container, template, config)
18055  * 
18056  * @constructor
18057  * Create a new View
18058  * @param {Object} config The config object
18059  * 
18060  */
18061 Roo.View = function(config, depreciated_tpl, depreciated_config){
18062     
18063     this.parent = false;
18064     
18065     if (typeof(depreciated_tpl) == 'undefined') {
18066         // new way.. - universal constructor.
18067         Roo.apply(this, config);
18068         this.el  = Roo.get(this.el);
18069     } else {
18070         // old format..
18071         this.el  = Roo.get(config);
18072         this.tpl = depreciated_tpl;
18073         Roo.apply(this, depreciated_config);
18074     }
18075     this.wrapEl  = this.el.wrap().wrap();
18076     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18077     
18078     
18079     if(typeof(this.tpl) == "string"){
18080         this.tpl = new Roo.Template(this.tpl);
18081     } else {
18082         // support xtype ctors..
18083         this.tpl = new Roo.factory(this.tpl, Roo);
18084     }
18085     
18086     
18087     this.tpl.compile();
18088     
18089     /** @private */
18090     this.addEvents({
18091         /**
18092          * @event beforeclick
18093          * Fires before a click is processed. Returns false to cancel the default action.
18094          * @param {Roo.View} this
18095          * @param {Number} index The index of the target node
18096          * @param {HTMLElement} node The target node
18097          * @param {Roo.EventObject} e The raw event object
18098          */
18099             "beforeclick" : true,
18100         /**
18101          * @event click
18102          * Fires when a template node is clicked.
18103          * @param {Roo.View} this
18104          * @param {Number} index The index of the target node
18105          * @param {HTMLElement} node The target node
18106          * @param {Roo.EventObject} e The raw event object
18107          */
18108             "click" : true,
18109         /**
18110          * @event dblclick
18111          * Fires when a template node is double clicked.
18112          * @param {Roo.View} this
18113          * @param {Number} index The index of the target node
18114          * @param {HTMLElement} node The target node
18115          * @param {Roo.EventObject} e The raw event object
18116          */
18117             "dblclick" : true,
18118         /**
18119          * @event contextmenu
18120          * Fires when a template node is right clicked.
18121          * @param {Roo.View} this
18122          * @param {Number} index The index of the target node
18123          * @param {HTMLElement} node The target node
18124          * @param {Roo.EventObject} e The raw event object
18125          */
18126             "contextmenu" : true,
18127         /**
18128          * @event selectionchange
18129          * Fires when the selected nodes change.
18130          * @param {Roo.View} this
18131          * @param {Array} selections Array of the selected nodes
18132          */
18133             "selectionchange" : true,
18134     
18135         /**
18136          * @event beforeselect
18137          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18138          * @param {Roo.View} this
18139          * @param {HTMLElement} node The node to be selected
18140          * @param {Array} selections Array of currently selected nodes
18141          */
18142             "beforeselect" : true,
18143         /**
18144          * @event preparedata
18145          * Fires on every row to render, to allow you to change the data.
18146          * @param {Roo.View} this
18147          * @param {Object} data to be rendered (change this)
18148          */
18149           "preparedata" : true
18150           
18151           
18152         });
18153
18154
18155
18156     this.el.on({
18157         "click": this.onClick,
18158         "dblclick": this.onDblClick,
18159         "contextmenu": this.onContextMenu,
18160         scope:this
18161     });
18162
18163     this.selections = [];
18164     this.nodes = [];
18165     this.cmp = new Roo.CompositeElementLite([]);
18166     if(this.store){
18167         this.store = Roo.factory(this.store, Roo.data);
18168         this.setStore(this.store, true);
18169     }
18170     
18171     if ( this.footer && this.footer.xtype) {
18172            
18173          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18174         
18175         this.footer.dataSource = this.store;
18176         this.footer.container = fctr;
18177         this.footer = Roo.factory(this.footer, Roo);
18178         fctr.insertFirst(this.el);
18179         
18180         // this is a bit insane - as the paging toolbar seems to detach the el..
18181 //        dom.parentNode.parentNode.parentNode
18182          // they get detached?
18183     }
18184     
18185     
18186     Roo.View.superclass.constructor.call(this);
18187     
18188     
18189 };
18190
18191 Roo.extend(Roo.View, Roo.util.Observable, {
18192     
18193      /**
18194      * @cfg {Roo.data.Store} store Data store to load data from.
18195      */
18196     store : false,
18197     
18198     /**
18199      * @cfg {String|Roo.Element} el The container element.
18200      */
18201     el : '',
18202     
18203     /**
18204      * @cfg {String|Roo.Template} tpl The template used by this View 
18205      */
18206     tpl : false,
18207     /**
18208      * @cfg {String} dataName the named area of the template to use as the data area
18209      *                          Works with domtemplates roo-name="name"
18210      */
18211     dataName: false,
18212     /**
18213      * @cfg {String} selectedClass The css class to add to selected nodes
18214      */
18215     selectedClass : "x-view-selected",
18216      /**
18217      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18218      */
18219     emptyText : "",
18220     
18221     /**
18222      * @cfg {String} text to display on mask (default Loading)
18223      */
18224     mask : false,
18225     /**
18226      * @cfg {Boolean} multiSelect Allow multiple selection
18227      */
18228     multiSelect : false,
18229     /**
18230      * @cfg {Boolean} singleSelect Allow single selection
18231      */
18232     singleSelect:  false,
18233     
18234     /**
18235      * @cfg {Boolean} toggleSelect - selecting 
18236      */
18237     toggleSelect : false,
18238     
18239     /**
18240      * @cfg {Boolean} tickable - selecting 
18241      */
18242     tickable : false,
18243     
18244     /**
18245      * Returns the element this view is bound to.
18246      * @return {Roo.Element}
18247      */
18248     getEl : function(){
18249         return this.wrapEl;
18250     },
18251     
18252     
18253
18254     /**
18255      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18256      */
18257     refresh : function(){
18258         //Roo.log('refresh');
18259         var t = this.tpl;
18260         
18261         // if we are using something like 'domtemplate', then
18262         // the what gets used is:
18263         // t.applySubtemplate(NAME, data, wrapping data..)
18264         // the outer template then get' applied with
18265         //     the store 'extra data'
18266         // and the body get's added to the
18267         //      roo-name="data" node?
18268         //      <span class='roo-tpl-{name}'></span> ?????
18269         
18270         
18271         
18272         this.clearSelections();
18273         this.el.update("");
18274         var html = [];
18275         var records = this.store.getRange();
18276         if(records.length < 1) {
18277             
18278             // is this valid??  = should it render a template??
18279             
18280             this.el.update(this.emptyText);
18281             return;
18282         }
18283         var el = this.el;
18284         if (this.dataName) {
18285             this.el.update(t.apply(this.store.meta)); //????
18286             el = this.el.child('.roo-tpl-' + this.dataName);
18287         }
18288         
18289         for(var i = 0, len = records.length; i < len; i++){
18290             var data = this.prepareData(records[i].data, i, records[i]);
18291             this.fireEvent("preparedata", this, data, i, records[i]);
18292             
18293             var d = Roo.apply({}, data);
18294             
18295             if(this.tickable){
18296                 Roo.apply(d, {'roo-id' : Roo.id()});
18297                 
18298                 var _this = this;
18299             
18300                 Roo.each(this.parent.item, function(item){
18301                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18302                         return;
18303                     }
18304                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18305                 });
18306             }
18307             
18308             html[html.length] = Roo.util.Format.trim(
18309                 this.dataName ?
18310                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18311                     t.apply(d)
18312             );
18313         }
18314         
18315         
18316         
18317         el.update(html.join(""));
18318         this.nodes = el.dom.childNodes;
18319         this.updateIndexes(0);
18320     },
18321     
18322
18323     /**
18324      * Function to override to reformat the data that is sent to
18325      * the template for each node.
18326      * DEPRICATED - use the preparedata event handler.
18327      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18328      * a JSON object for an UpdateManager bound view).
18329      */
18330     prepareData : function(data, index, record)
18331     {
18332         this.fireEvent("preparedata", this, data, index, record);
18333         return data;
18334     },
18335
18336     onUpdate : function(ds, record){
18337         // Roo.log('on update');   
18338         this.clearSelections();
18339         var index = this.store.indexOf(record);
18340         var n = this.nodes[index];
18341         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18342         n.parentNode.removeChild(n);
18343         this.updateIndexes(index, index);
18344     },
18345
18346     
18347     
18348 // --------- FIXME     
18349     onAdd : function(ds, records, index)
18350     {
18351         //Roo.log(['on Add', ds, records, index] );        
18352         this.clearSelections();
18353         if(this.nodes.length == 0){
18354             this.refresh();
18355             return;
18356         }
18357         var n = this.nodes[index];
18358         for(var i = 0, len = records.length; i < len; i++){
18359             var d = this.prepareData(records[i].data, i, records[i]);
18360             if(n){
18361                 this.tpl.insertBefore(n, d);
18362             }else{
18363                 
18364                 this.tpl.append(this.el, d);
18365             }
18366         }
18367         this.updateIndexes(index);
18368     },
18369
18370     onRemove : function(ds, record, index){
18371        // Roo.log('onRemove');
18372         this.clearSelections();
18373         var el = this.dataName  ?
18374             this.el.child('.roo-tpl-' + this.dataName) :
18375             this.el; 
18376         
18377         el.dom.removeChild(this.nodes[index]);
18378         this.updateIndexes(index);
18379     },
18380
18381     /**
18382      * Refresh an individual node.
18383      * @param {Number} index
18384      */
18385     refreshNode : function(index){
18386         this.onUpdate(this.store, this.store.getAt(index));
18387     },
18388
18389     updateIndexes : function(startIndex, endIndex){
18390         var ns = this.nodes;
18391         startIndex = startIndex || 0;
18392         endIndex = endIndex || ns.length - 1;
18393         for(var i = startIndex; i <= endIndex; i++){
18394             ns[i].nodeIndex = i;
18395         }
18396     },
18397
18398     /**
18399      * Changes the data store this view uses and refresh the view.
18400      * @param {Store} store
18401      */
18402     setStore : function(store, initial){
18403         if(!initial && this.store){
18404             this.store.un("datachanged", this.refresh);
18405             this.store.un("add", this.onAdd);
18406             this.store.un("remove", this.onRemove);
18407             this.store.un("update", this.onUpdate);
18408             this.store.un("clear", this.refresh);
18409             this.store.un("beforeload", this.onBeforeLoad);
18410             this.store.un("load", this.onLoad);
18411             this.store.un("loadexception", this.onLoad);
18412         }
18413         if(store){
18414           
18415             store.on("datachanged", this.refresh, this);
18416             store.on("add", this.onAdd, this);
18417             store.on("remove", this.onRemove, this);
18418             store.on("update", this.onUpdate, this);
18419             store.on("clear", this.refresh, this);
18420             store.on("beforeload", this.onBeforeLoad, this);
18421             store.on("load", this.onLoad, this);
18422             store.on("loadexception", this.onLoad, this);
18423         }
18424         
18425         if(store){
18426             this.refresh();
18427         }
18428     },
18429     /**
18430      * onbeforeLoad - masks the loading area.
18431      *
18432      */
18433     onBeforeLoad : function(store,opts)
18434     {
18435          //Roo.log('onBeforeLoad');   
18436         if (!opts.add) {
18437             this.el.update("");
18438         }
18439         this.el.mask(this.mask ? this.mask : "Loading" ); 
18440     },
18441     onLoad : function ()
18442     {
18443         this.el.unmask();
18444     },
18445     
18446
18447     /**
18448      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18449      * @param {HTMLElement} node
18450      * @return {HTMLElement} The template node
18451      */
18452     findItemFromChild : function(node){
18453         var el = this.dataName  ?
18454             this.el.child('.roo-tpl-' + this.dataName,true) :
18455             this.el.dom; 
18456         
18457         if(!node || node.parentNode == el){
18458                     return node;
18459             }
18460             var p = node.parentNode;
18461             while(p && p != el){
18462             if(p.parentNode == el){
18463                 return p;
18464             }
18465             p = p.parentNode;
18466         }
18467             return null;
18468     },
18469
18470     /** @ignore */
18471     onClick : function(e){
18472         var item = this.findItemFromChild(e.getTarget());
18473         if(item){
18474             var index = this.indexOf(item);
18475             if(this.onItemClick(item, index, e) !== false){
18476                 this.fireEvent("click", this, index, item, e);
18477             }
18478         }else{
18479             this.clearSelections();
18480         }
18481     },
18482
18483     /** @ignore */
18484     onContextMenu : function(e){
18485         var item = this.findItemFromChild(e.getTarget());
18486         if(item){
18487             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18488         }
18489     },
18490
18491     /** @ignore */
18492     onDblClick : function(e){
18493         var item = this.findItemFromChild(e.getTarget());
18494         if(item){
18495             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18496         }
18497     },
18498
18499     onItemClick : function(item, index, e)
18500     {
18501         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18502             return false;
18503         }
18504         if (this.toggleSelect) {
18505             var m = this.isSelected(item) ? 'unselect' : 'select';
18506             //Roo.log(m);
18507             var _t = this;
18508             _t[m](item, true, false);
18509             return true;
18510         }
18511         if(this.multiSelect || this.singleSelect){
18512             if(this.multiSelect && e.shiftKey && this.lastSelection){
18513                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18514             }else{
18515                 this.select(item, this.multiSelect && e.ctrlKey);
18516                 this.lastSelection = item;
18517             }
18518             
18519             if(!this.tickable){
18520                 e.preventDefault();
18521             }
18522             
18523         }
18524         return true;
18525     },
18526
18527     /**
18528      * Get the number of selected nodes.
18529      * @return {Number}
18530      */
18531     getSelectionCount : function(){
18532         return this.selections.length;
18533     },
18534
18535     /**
18536      * Get the currently selected nodes.
18537      * @return {Array} An array of HTMLElements
18538      */
18539     getSelectedNodes : function(){
18540         return this.selections;
18541     },
18542
18543     /**
18544      * Get the indexes of the selected nodes.
18545      * @return {Array}
18546      */
18547     getSelectedIndexes : function(){
18548         var indexes = [], s = this.selections;
18549         for(var i = 0, len = s.length; i < len; i++){
18550             indexes.push(s[i].nodeIndex);
18551         }
18552         return indexes;
18553     },
18554
18555     /**
18556      * Clear all selections
18557      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18558      */
18559     clearSelections : function(suppressEvent){
18560         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18561             this.cmp.elements = this.selections;
18562             this.cmp.removeClass(this.selectedClass);
18563             this.selections = [];
18564             if(!suppressEvent){
18565                 this.fireEvent("selectionchange", this, this.selections);
18566             }
18567         }
18568     },
18569
18570     /**
18571      * Returns true if the passed node is selected
18572      * @param {HTMLElement/Number} node The node or node index
18573      * @return {Boolean}
18574      */
18575     isSelected : function(node){
18576         var s = this.selections;
18577         if(s.length < 1){
18578             return false;
18579         }
18580         node = this.getNode(node);
18581         return s.indexOf(node) !== -1;
18582     },
18583
18584     /**
18585      * Selects nodes.
18586      * @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
18587      * @param {Boolean} keepExisting (optional) true to keep existing selections
18588      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18589      */
18590     select : function(nodeInfo, keepExisting, suppressEvent){
18591         if(nodeInfo instanceof Array){
18592             if(!keepExisting){
18593                 this.clearSelections(true);
18594             }
18595             for(var i = 0, len = nodeInfo.length; i < len; i++){
18596                 this.select(nodeInfo[i], true, true);
18597             }
18598             return;
18599         } 
18600         var node = this.getNode(nodeInfo);
18601         if(!node || this.isSelected(node)){
18602             return; // already selected.
18603         }
18604         if(!keepExisting){
18605             this.clearSelections(true);
18606         }
18607         
18608         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18609             Roo.fly(node).addClass(this.selectedClass);
18610             this.selections.push(node);
18611             if(!suppressEvent){
18612                 this.fireEvent("selectionchange", this, this.selections);
18613             }
18614         }
18615         
18616         
18617     },
18618       /**
18619      * Unselects nodes.
18620      * @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
18621      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18623      */
18624     unselect : function(nodeInfo, keepExisting, suppressEvent)
18625     {
18626         if(nodeInfo instanceof Array){
18627             Roo.each(this.selections, function(s) {
18628                 this.unselect(s, nodeInfo);
18629             }, this);
18630             return;
18631         }
18632         var node = this.getNode(nodeInfo);
18633         if(!node || !this.isSelected(node)){
18634             //Roo.log("not selected");
18635             return; // not selected.
18636         }
18637         // fireevent???
18638         var ns = [];
18639         Roo.each(this.selections, function(s) {
18640             if (s == node ) {
18641                 Roo.fly(node).removeClass(this.selectedClass);
18642
18643                 return;
18644             }
18645             ns.push(s);
18646         },this);
18647         
18648         this.selections= ns;
18649         this.fireEvent("selectionchange", this, this.selections);
18650     },
18651
18652     /**
18653      * Gets a template node.
18654      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18655      * @return {HTMLElement} The node or null if it wasn't found
18656      */
18657     getNode : function(nodeInfo){
18658         if(typeof nodeInfo == "string"){
18659             return document.getElementById(nodeInfo);
18660         }else if(typeof nodeInfo == "number"){
18661             return this.nodes[nodeInfo];
18662         }
18663         return nodeInfo;
18664     },
18665
18666     /**
18667      * Gets a range template nodes.
18668      * @param {Number} startIndex
18669      * @param {Number} endIndex
18670      * @return {Array} An array of nodes
18671      */
18672     getNodes : function(start, end){
18673         var ns = this.nodes;
18674         start = start || 0;
18675         end = typeof end == "undefined" ? ns.length - 1 : end;
18676         var nodes = [];
18677         if(start <= end){
18678             for(var i = start; i <= end; i++){
18679                 nodes.push(ns[i]);
18680             }
18681         } else{
18682             for(var i = start; i >= end; i--){
18683                 nodes.push(ns[i]);
18684             }
18685         }
18686         return nodes;
18687     },
18688
18689     /**
18690      * Finds the index of the passed node
18691      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18692      * @return {Number} The index of the node or -1
18693      */
18694     indexOf : function(node){
18695         node = this.getNode(node);
18696         if(typeof node.nodeIndex == "number"){
18697             return node.nodeIndex;
18698         }
18699         var ns = this.nodes;
18700         for(var i = 0, len = ns.length; i < len; i++){
18701             if(ns[i] == node){
18702                 return i;
18703             }
18704         }
18705         return -1;
18706     }
18707 });
18708 /*
18709  * - LGPL
18710  *
18711  * based on jquery fullcalendar
18712  * 
18713  */
18714
18715 Roo.bootstrap = Roo.bootstrap || {};
18716 /**
18717  * @class Roo.bootstrap.Calendar
18718  * @extends Roo.bootstrap.Component
18719  * Bootstrap Calendar class
18720  * @cfg {Boolean} loadMask (true|false) default false
18721  * @cfg {Object} header generate the user specific header of the calendar, default false
18722
18723  * @constructor
18724  * Create a new Container
18725  * @param {Object} config The config object
18726  */
18727
18728
18729
18730 Roo.bootstrap.Calendar = function(config){
18731     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18732      this.addEvents({
18733         /**
18734              * @event select
18735              * Fires when a date is selected
18736              * @param {DatePicker} this
18737              * @param {Date} date The selected date
18738              */
18739         'select': true,
18740         /**
18741              * @event monthchange
18742              * Fires when the displayed month changes 
18743              * @param {DatePicker} this
18744              * @param {Date} date The selected month
18745              */
18746         'monthchange': true,
18747         /**
18748              * @event evententer
18749              * Fires when mouse over an event
18750              * @param {Calendar} this
18751              * @param {event} Event
18752              */
18753         'evententer': true,
18754         /**
18755              * @event eventleave
18756              * Fires when the mouse leaves an
18757              * @param {Calendar} this
18758              * @param {event}
18759              */
18760         'eventleave': true,
18761         /**
18762              * @event eventclick
18763              * Fires when the mouse click an
18764              * @param {Calendar} this
18765              * @param {event}
18766              */
18767         'eventclick': true
18768         
18769     });
18770
18771 };
18772
18773 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18774     
18775      /**
18776      * @cfg {Number} startDay
18777      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18778      */
18779     startDay : 0,
18780     
18781     loadMask : false,
18782     
18783     header : false,
18784       
18785     getAutoCreate : function(){
18786         
18787         
18788         var fc_button = function(name, corner, style, content ) {
18789             return Roo.apply({},{
18790                 tag : 'span',
18791                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18792                          (corner.length ?
18793                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18794                             ''
18795                         ),
18796                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18797                 unselectable: 'on'
18798             });
18799         };
18800         
18801         var header = {};
18802         
18803         if(!this.header){
18804             header = {
18805                 tag : 'table',
18806                 cls : 'fc-header',
18807                 style : 'width:100%',
18808                 cn : [
18809                     {
18810                         tag: 'tr',
18811                         cn : [
18812                             {
18813                                 tag : 'td',
18814                                 cls : 'fc-header-left',
18815                                 cn : [
18816                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18817                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18818                                     { tag: 'span', cls: 'fc-header-space' },
18819                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18820
18821
18822                                 ]
18823                             },
18824
18825                             {
18826                                 tag : 'td',
18827                                 cls : 'fc-header-center',
18828                                 cn : [
18829                                     {
18830                                         tag: 'span',
18831                                         cls: 'fc-header-title',
18832                                         cn : {
18833                                             tag: 'H2',
18834                                             html : 'month / year'
18835                                         }
18836                                     }
18837
18838                                 ]
18839                             },
18840                             {
18841                                 tag : 'td',
18842                                 cls : 'fc-header-right',
18843                                 cn : [
18844                               /*      fc_button('month', 'left', '', 'month' ),
18845                                     fc_button('week', '', '', 'week' ),
18846                                     fc_button('day', 'right', '', 'day' )
18847                                 */    
18848
18849                                 ]
18850                             }
18851
18852                         ]
18853                     }
18854                 ]
18855             };
18856         }
18857         
18858         header = this.header;
18859         
18860        
18861         var cal_heads = function() {
18862             var ret = [];
18863             // fixme - handle this.
18864             
18865             for (var i =0; i < Date.dayNames.length; i++) {
18866                 var d = Date.dayNames[i];
18867                 ret.push({
18868                     tag: 'th',
18869                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18870                     html : d.substring(0,3)
18871                 });
18872                 
18873             }
18874             ret[0].cls += ' fc-first';
18875             ret[6].cls += ' fc-last';
18876             return ret;
18877         };
18878         var cal_cell = function(n) {
18879             return  {
18880                 tag: 'td',
18881                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18882                 cn : [
18883                     {
18884                         cn : [
18885                             {
18886                                 cls: 'fc-day-number',
18887                                 html: 'D'
18888                             },
18889                             {
18890                                 cls: 'fc-day-content',
18891                              
18892                                 cn : [
18893                                      {
18894                                         style: 'position: relative;' // height: 17px;
18895                                     }
18896                                 ]
18897                             }
18898                             
18899                             
18900                         ]
18901                     }
18902                 ]
18903                 
18904             }
18905         };
18906         var cal_rows = function() {
18907             
18908             var ret = [];
18909             for (var r = 0; r < 6; r++) {
18910                 var row= {
18911                     tag : 'tr',
18912                     cls : 'fc-week',
18913                     cn : []
18914                 };
18915                 
18916                 for (var i =0; i < Date.dayNames.length; i++) {
18917                     var d = Date.dayNames[i];
18918                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18919
18920                 }
18921                 row.cn[0].cls+=' fc-first';
18922                 row.cn[0].cn[0].style = 'min-height:90px';
18923                 row.cn[6].cls+=' fc-last';
18924                 ret.push(row);
18925                 
18926             }
18927             ret[0].cls += ' fc-first';
18928             ret[4].cls += ' fc-prev-last';
18929             ret[5].cls += ' fc-last';
18930             return ret;
18931             
18932         };
18933         
18934         var cal_table = {
18935             tag: 'table',
18936             cls: 'fc-border-separate',
18937             style : 'width:100%',
18938             cellspacing  : 0,
18939             cn : [
18940                 { 
18941                     tag: 'thead',
18942                     cn : [
18943                         { 
18944                             tag: 'tr',
18945                             cls : 'fc-first fc-last',
18946                             cn : cal_heads()
18947                         }
18948                     ]
18949                 },
18950                 { 
18951                     tag: 'tbody',
18952                     cn : cal_rows()
18953                 }
18954                   
18955             ]
18956         };
18957          
18958          var cfg = {
18959             cls : 'fc fc-ltr',
18960             cn : [
18961                 header,
18962                 {
18963                     cls : 'fc-content',
18964                     style : "position: relative;",
18965                     cn : [
18966                         {
18967                             cls : 'fc-view fc-view-month fc-grid',
18968                             style : 'position: relative',
18969                             unselectable : 'on',
18970                             cn : [
18971                                 {
18972                                     cls : 'fc-event-container',
18973                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18974                                 },
18975                                 cal_table
18976                             ]
18977                         }
18978                     ]
18979     
18980                 }
18981            ] 
18982             
18983         };
18984         
18985          
18986         
18987         return cfg;
18988     },
18989     
18990     
18991     initEvents : function()
18992     {
18993         if(!this.store){
18994             throw "can not find store for calendar";
18995         }
18996         
18997         var mark = {
18998             tag: "div",
18999             cls:"x-dlg-mask",
19000             style: "text-align:center",
19001             cn: [
19002                 {
19003                     tag: "div",
19004                     style: "background-color:white;width:50%;margin:250 auto",
19005                     cn: [
19006                         {
19007                             tag: "img",
19008                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19009                         },
19010                         {
19011                             tag: "span",
19012                             html: "Loading"
19013                         }
19014                         
19015                     ]
19016                 }
19017             ]
19018         };
19019         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19020         
19021         var size = this.el.select('.fc-content', true).first().getSize();
19022         this.maskEl.setSize(size.width, size.height);
19023         this.maskEl.enableDisplayMode("block");
19024         if(!this.loadMask){
19025             this.maskEl.hide();
19026         }
19027         
19028         this.store = Roo.factory(this.store, Roo.data);
19029         this.store.on('load', this.onLoad, this);
19030         this.store.on('beforeload', this.onBeforeLoad, this);
19031         
19032         this.resize();
19033         
19034         this.cells = this.el.select('.fc-day',true);
19035         //Roo.log(this.cells);
19036         this.textNodes = this.el.query('.fc-day-number');
19037         this.cells.addClassOnOver('fc-state-hover');
19038         
19039         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19040         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19041         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19042         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19043         
19044         this.on('monthchange', this.onMonthChange, this);
19045         
19046         this.update(new Date().clearTime());
19047     },
19048     
19049     resize : function() {
19050         var sz  = this.el.getSize();
19051         
19052         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19053         this.el.select('.fc-day-content div',true).setHeight(34);
19054     },
19055     
19056     
19057     // private
19058     showPrevMonth : function(e){
19059         this.update(this.activeDate.add("mo", -1));
19060     },
19061     showToday : function(e){
19062         this.update(new Date().clearTime());
19063     },
19064     // private
19065     showNextMonth : function(e){
19066         this.update(this.activeDate.add("mo", 1));
19067     },
19068
19069     // private
19070     showPrevYear : function(){
19071         this.update(this.activeDate.add("y", -1));
19072     },
19073
19074     // private
19075     showNextYear : function(){
19076         this.update(this.activeDate.add("y", 1));
19077     },
19078
19079     
19080    // private
19081     update : function(date)
19082     {
19083         var vd = this.activeDate;
19084         this.activeDate = date;
19085 //        if(vd && this.el){
19086 //            var t = date.getTime();
19087 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19088 //                Roo.log('using add remove');
19089 //                
19090 //                this.fireEvent('monthchange', this, date);
19091 //                
19092 //                this.cells.removeClass("fc-state-highlight");
19093 //                this.cells.each(function(c){
19094 //                   if(c.dateValue == t){
19095 //                       c.addClass("fc-state-highlight");
19096 //                       setTimeout(function(){
19097 //                            try{c.dom.firstChild.focus();}catch(e){}
19098 //                       }, 50);
19099 //                       return false;
19100 //                   }
19101 //                   return true;
19102 //                });
19103 //                return;
19104 //            }
19105 //        }
19106         
19107         var days = date.getDaysInMonth();
19108         
19109         var firstOfMonth = date.getFirstDateOfMonth();
19110         var startingPos = firstOfMonth.getDay()-this.startDay;
19111         
19112         if(startingPos < this.startDay){
19113             startingPos += 7;
19114         }
19115         
19116         var pm = date.add(Date.MONTH, -1);
19117         var prevStart = pm.getDaysInMonth()-startingPos;
19118 //        
19119         this.cells = this.el.select('.fc-day',true);
19120         this.textNodes = this.el.query('.fc-day-number');
19121         this.cells.addClassOnOver('fc-state-hover');
19122         
19123         var cells = this.cells.elements;
19124         var textEls = this.textNodes;
19125         
19126         Roo.each(cells, function(cell){
19127             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19128         });
19129         
19130         days += startingPos;
19131
19132         // convert everything to numbers so it's fast
19133         var day = 86400000;
19134         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19135         //Roo.log(d);
19136         //Roo.log(pm);
19137         //Roo.log(prevStart);
19138         
19139         var today = new Date().clearTime().getTime();
19140         var sel = date.clearTime().getTime();
19141         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19142         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19143         var ddMatch = this.disabledDatesRE;
19144         var ddText = this.disabledDatesText;
19145         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19146         var ddaysText = this.disabledDaysText;
19147         var format = this.format;
19148         
19149         var setCellClass = function(cal, cell){
19150             cell.row = 0;
19151             cell.events = [];
19152             cell.more = [];
19153             //Roo.log('set Cell Class');
19154             cell.title = "";
19155             var t = d.getTime();
19156             
19157             //Roo.log(d);
19158             
19159             cell.dateValue = t;
19160             if(t == today){
19161                 cell.className += " fc-today";
19162                 cell.className += " fc-state-highlight";
19163                 cell.title = cal.todayText;
19164             }
19165             if(t == sel){
19166                 // disable highlight in other month..
19167                 //cell.className += " fc-state-highlight";
19168                 
19169             }
19170             // disabling
19171             if(t < min) {
19172                 cell.className = " fc-state-disabled";
19173                 cell.title = cal.minText;
19174                 return;
19175             }
19176             if(t > max) {
19177                 cell.className = " fc-state-disabled";
19178                 cell.title = cal.maxText;
19179                 return;
19180             }
19181             if(ddays){
19182                 if(ddays.indexOf(d.getDay()) != -1){
19183                     cell.title = ddaysText;
19184                     cell.className = " fc-state-disabled";
19185                 }
19186             }
19187             if(ddMatch && format){
19188                 var fvalue = d.dateFormat(format);
19189                 if(ddMatch.test(fvalue)){
19190                     cell.title = ddText.replace("%0", fvalue);
19191                     cell.className = " fc-state-disabled";
19192                 }
19193             }
19194             
19195             if (!cell.initialClassName) {
19196                 cell.initialClassName = cell.dom.className;
19197             }
19198             
19199             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19200         };
19201
19202         var i = 0;
19203         
19204         for(; i < startingPos; i++) {
19205             textEls[i].innerHTML = (++prevStart);
19206             d.setDate(d.getDate()+1);
19207             
19208             cells[i].className = "fc-past fc-other-month";
19209             setCellClass(this, cells[i]);
19210         }
19211         
19212         var intDay = 0;
19213         
19214         for(; i < days; i++){
19215             intDay = i - startingPos + 1;
19216             textEls[i].innerHTML = (intDay);
19217             d.setDate(d.getDate()+1);
19218             
19219             cells[i].className = ''; // "x-date-active";
19220             setCellClass(this, cells[i]);
19221         }
19222         var extraDays = 0;
19223         
19224         for(; i < 42; i++) {
19225             textEls[i].innerHTML = (++extraDays);
19226             d.setDate(d.getDate()+1);
19227             
19228             cells[i].className = "fc-future fc-other-month";
19229             setCellClass(this, cells[i]);
19230         }
19231         
19232         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19233         
19234         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19235         
19236         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19237         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19238         
19239         if(totalRows != 6){
19240             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19241             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19242         }
19243         
19244         this.fireEvent('monthchange', this, date);
19245         
19246         
19247         /*
19248         if(!this.internalRender){
19249             var main = this.el.dom.firstChild;
19250             var w = main.offsetWidth;
19251             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19252             Roo.fly(main).setWidth(w);
19253             this.internalRender = true;
19254             // opera does not respect the auto grow header center column
19255             // then, after it gets a width opera refuses to recalculate
19256             // without a second pass
19257             if(Roo.isOpera && !this.secondPass){
19258                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19259                 this.secondPass = true;
19260                 this.update.defer(10, this, [date]);
19261             }
19262         }
19263         */
19264         
19265     },
19266     
19267     findCell : function(dt) {
19268         dt = dt.clearTime().getTime();
19269         var ret = false;
19270         this.cells.each(function(c){
19271             //Roo.log("check " +c.dateValue + '?=' + dt);
19272             if(c.dateValue == dt){
19273                 ret = c;
19274                 return false;
19275             }
19276             return true;
19277         });
19278         
19279         return ret;
19280     },
19281     
19282     findCells : function(ev) {
19283         var s = ev.start.clone().clearTime().getTime();
19284        // Roo.log(s);
19285         var e= ev.end.clone().clearTime().getTime();
19286        // Roo.log(e);
19287         var ret = [];
19288         this.cells.each(function(c){
19289              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19290             
19291             if(c.dateValue > e){
19292                 return ;
19293             }
19294             if(c.dateValue < s){
19295                 return ;
19296             }
19297             ret.push(c);
19298         });
19299         
19300         return ret;    
19301     },
19302     
19303 //    findBestRow: function(cells)
19304 //    {
19305 //        var ret = 0;
19306 //        
19307 //        for (var i =0 ; i < cells.length;i++) {
19308 //            ret  = Math.max(cells[i].rows || 0,ret);
19309 //        }
19310 //        return ret;
19311 //        
19312 //    },
19313     
19314     
19315     addItem : function(ev)
19316     {
19317         // look for vertical location slot in
19318         var cells = this.findCells(ev);
19319         
19320 //        ev.row = this.findBestRow(cells);
19321         
19322         // work out the location.
19323         
19324         var crow = false;
19325         var rows = [];
19326         for(var i =0; i < cells.length; i++) {
19327             
19328             cells[i].row = cells[0].row;
19329             
19330             if(i == 0){
19331                 cells[i].row = cells[i].row + 1;
19332             }
19333             
19334             if (!crow) {
19335                 crow = {
19336                     start : cells[i],
19337                     end :  cells[i]
19338                 };
19339                 continue;
19340             }
19341             if (crow.start.getY() == cells[i].getY()) {
19342                 // on same row.
19343                 crow.end = cells[i];
19344                 continue;
19345             }
19346             // different row.
19347             rows.push(crow);
19348             crow = {
19349                 start: cells[i],
19350                 end : cells[i]
19351             };
19352             
19353         }
19354         
19355         rows.push(crow);
19356         ev.els = [];
19357         ev.rows = rows;
19358         ev.cells = cells;
19359         
19360         cells[0].events.push(ev);
19361         
19362         this.calevents.push(ev);
19363     },
19364     
19365     clearEvents: function() {
19366         
19367         if(!this.calevents){
19368             return;
19369         }
19370         
19371         Roo.each(this.cells.elements, function(c){
19372             c.row = 0;
19373             c.events = [];
19374             c.more = [];
19375         });
19376         
19377         Roo.each(this.calevents, function(e) {
19378             Roo.each(e.els, function(el) {
19379                 el.un('mouseenter' ,this.onEventEnter, this);
19380                 el.un('mouseleave' ,this.onEventLeave, this);
19381                 el.remove();
19382             },this);
19383         },this);
19384         
19385         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19386             e.remove();
19387         });
19388         
19389     },
19390     
19391     renderEvents: function()
19392     {   
19393         var _this = this;
19394         
19395         this.cells.each(function(c) {
19396             
19397             if(c.row < 5){
19398                 return;
19399             }
19400             
19401             var ev = c.events;
19402             
19403             var r = 4;
19404             if(c.row != c.events.length){
19405                 r = 4 - (4 - (c.row - c.events.length));
19406             }
19407             
19408             c.events = ev.slice(0, r);
19409             c.more = ev.slice(r);
19410             
19411             if(c.more.length && c.more.length == 1){
19412                 c.events.push(c.more.pop());
19413             }
19414             
19415             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19416             
19417         });
19418             
19419         this.cells.each(function(c) {
19420             
19421             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19422             
19423             
19424             for (var e = 0; e < c.events.length; e++){
19425                 var ev = c.events[e];
19426                 var rows = ev.rows;
19427                 
19428                 for(var i = 0; i < rows.length; i++) {
19429                 
19430                     // how many rows should it span..
19431
19432                     var  cfg = {
19433                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19434                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19435
19436                         unselectable : "on",
19437                         cn : [
19438                             {
19439                                 cls: 'fc-event-inner',
19440                                 cn : [
19441     //                                {
19442     //                                  tag:'span',
19443     //                                  cls: 'fc-event-time',
19444     //                                  html : cells.length > 1 ? '' : ev.time
19445     //                                },
19446                                     {
19447                                       tag:'span',
19448                                       cls: 'fc-event-title',
19449                                       html : String.format('{0}', ev.title)
19450                                     }
19451
19452
19453                                 ]
19454                             },
19455                             {
19456                                 cls: 'ui-resizable-handle ui-resizable-e',
19457                                 html : '&nbsp;&nbsp;&nbsp'
19458                             }
19459
19460                         ]
19461                     };
19462
19463                     if (i == 0) {
19464                         cfg.cls += ' fc-event-start';
19465                     }
19466                     if ((i+1) == rows.length) {
19467                         cfg.cls += ' fc-event-end';
19468                     }
19469
19470                     var ctr = _this.el.select('.fc-event-container',true).first();
19471                     var cg = ctr.createChild(cfg);
19472
19473                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19474                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19475
19476                     var r = (c.more.length) ? 1 : 0;
19477                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19478                     cg.setWidth(ebox.right - sbox.x -2);
19479
19480                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19481                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19482                     cg.on('click', _this.onEventClick, _this, ev);
19483
19484                     ev.els.push(cg);
19485                     
19486                 }
19487                 
19488             }
19489             
19490             
19491             if(c.more.length){
19492                 var  cfg = {
19493                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19494                     style : 'position: absolute',
19495                     unselectable : "on",
19496                     cn : [
19497                         {
19498                             cls: 'fc-event-inner',
19499                             cn : [
19500                                 {
19501                                   tag:'span',
19502                                   cls: 'fc-event-title',
19503                                   html : 'More'
19504                                 }
19505
19506
19507                             ]
19508                         },
19509                         {
19510                             cls: 'ui-resizable-handle ui-resizable-e',
19511                             html : '&nbsp;&nbsp;&nbsp'
19512                         }
19513
19514                     ]
19515                 };
19516
19517                 var ctr = _this.el.select('.fc-event-container',true).first();
19518                 var cg = ctr.createChild(cfg);
19519
19520                 var sbox = c.select('.fc-day-content',true).first().getBox();
19521                 var ebox = c.select('.fc-day-content',true).first().getBox();
19522                 //Roo.log(cg);
19523                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19524                 cg.setWidth(ebox.right - sbox.x -2);
19525
19526                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19527                 
19528             }
19529             
19530         });
19531         
19532         
19533         
19534     },
19535     
19536     onEventEnter: function (e, el,event,d) {
19537         this.fireEvent('evententer', this, el, event);
19538     },
19539     
19540     onEventLeave: function (e, el,event,d) {
19541         this.fireEvent('eventleave', this, el, event);
19542     },
19543     
19544     onEventClick: function (e, el,event,d) {
19545         this.fireEvent('eventclick', this, el, event);
19546     },
19547     
19548     onMonthChange: function () {
19549         this.store.load();
19550     },
19551     
19552     onMoreEventClick: function(e, el, more)
19553     {
19554         var _this = this;
19555         
19556         this.calpopover.placement = 'right';
19557         this.calpopover.setTitle('More');
19558         
19559         this.calpopover.setContent('');
19560         
19561         var ctr = this.calpopover.el.select('.popover-content', true).first();
19562         
19563         Roo.each(more, function(m){
19564             var cfg = {
19565                 cls : 'fc-event-hori fc-event-draggable',
19566                 html : m.title
19567             };
19568             var cg = ctr.createChild(cfg);
19569             
19570             cg.on('click', _this.onEventClick, _this, m);
19571         });
19572         
19573         this.calpopover.show(el);
19574         
19575         
19576     },
19577     
19578     onLoad: function () 
19579     {   
19580         this.calevents = [];
19581         var cal = this;
19582         
19583         if(this.store.getCount() > 0){
19584             this.store.data.each(function(d){
19585                cal.addItem({
19586                     id : d.data.id,
19587                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19588                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19589                     time : d.data.start_time,
19590                     title : d.data.title,
19591                     description : d.data.description,
19592                     venue : d.data.venue
19593                 });
19594             });
19595         }
19596         
19597         this.renderEvents();
19598         
19599         if(this.calevents.length && this.loadMask){
19600             this.maskEl.hide();
19601         }
19602     },
19603     
19604     onBeforeLoad: function()
19605     {
19606         this.clearEvents();
19607         if(this.loadMask){
19608             this.maskEl.show();
19609         }
19610     }
19611 });
19612
19613  
19614  /*
19615  * - LGPL
19616  *
19617  * element
19618  * 
19619  */
19620
19621 /**
19622  * @class Roo.bootstrap.Popover
19623  * @extends Roo.bootstrap.Component
19624  * Bootstrap Popover class
19625  * @cfg {String} html contents of the popover   (or false to use children..)
19626  * @cfg {String} title of popover (or false to hide)
19627  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19628  * @cfg {String} trigger click || hover (or false to trigger manually)
19629  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19630  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19631  *      - if false and it has a 'parent' then it will be automatically added to that element
19632  *      - if string - Roo.get  will be called 
19633  * @cfg {Number} delay - delay before showing
19634  
19635  * @constructor
19636  * Create a new Popover
19637  * @param {Object} config The config object
19638  */
19639
19640 Roo.bootstrap.Popover = function(config){
19641     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19642     
19643     this.addEvents({
19644         // raw events
19645          /**
19646          * @event show
19647          * After the popover show
19648          * 
19649          * @param {Roo.bootstrap.Popover} this
19650          */
19651         "show" : true,
19652         /**
19653          * @event hide
19654          * After the popover hide
19655          * 
19656          * @param {Roo.bootstrap.Popover} this
19657          */
19658         "hide" : true
19659     });
19660 };
19661
19662 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19663     
19664     title: false,
19665     html: false,
19666     
19667     placement : 'right',
19668     trigger : 'hover', // hover
19669     modal : false,
19670     delay : 0,
19671     
19672     over: false,
19673     
19674     can_build_overlaid : false,
19675     
19676     maskEl : false, // the mask element
19677     headerEl : false,
19678     contentEl : false,
19679     alignEl : false, // when show is called with an element - this get's stored.
19680     
19681     getChildContainer : function()
19682     {
19683         return this.contentEl;
19684         
19685     },
19686     getPopoverHeader : function()
19687     {
19688         this.title = true; // flag not to hide it..
19689         this.headerEl.addClass('p-0');
19690         return this.headerEl
19691     },
19692     
19693     
19694     getAutoCreate : function(){
19695          
19696         var cfg = {
19697            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19698            style: 'display:block',
19699            cn : [
19700                 {
19701                     cls : 'arrow'
19702                 },
19703                 {
19704                     cls : 'popover-inner ',
19705                     cn : [
19706                         {
19707                             tag: 'h3',
19708                             cls: 'popover-title popover-header',
19709                             html : this.title === false ? '' : this.title
19710                         },
19711                         {
19712                             cls : 'popover-content popover-body '  + (this.cls || ''),
19713                             html : this.html || ''
19714                         }
19715                     ]
19716                     
19717                 }
19718            ]
19719         };
19720         
19721         return cfg;
19722     },
19723     /**
19724      * @param {string} the title
19725      */
19726     setTitle: function(str)
19727     {
19728         this.title = str;
19729         if (this.el) {
19730             this.headerEl.dom.innerHTML = str;
19731         }
19732         
19733     },
19734     /**
19735      * @param {string} the body content
19736      */
19737     setContent: function(str)
19738     {
19739         this.html = str;
19740         if (this.contentEl) {
19741             this.contentEl.dom.innerHTML = str;
19742         }
19743         
19744     },
19745     // as it get's added to the bottom of the page.
19746     onRender : function(ct, position)
19747     {
19748         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19749         
19750         
19751         
19752         if(!this.el){
19753             var cfg = Roo.apply({},  this.getAutoCreate());
19754             cfg.id = Roo.id();
19755             
19756             if (this.cls) {
19757                 cfg.cls += ' ' + this.cls;
19758             }
19759             if (this.style) {
19760                 cfg.style = this.style;
19761             }
19762             //Roo.log("adding to ");
19763             this.el = Roo.get(document.body).createChild(cfg, position);
19764 //            Roo.log(this.el);
19765         }
19766         
19767         this.contentEl = this.el.select('.popover-content',true).first();
19768         this.headerEl =  this.el.select('.popover-title',true).first();
19769         
19770         var nitems = [];
19771         if(typeof(this.items) != 'undefined'){
19772             var items = this.items;
19773             delete this.items;
19774
19775             for(var i =0;i < items.length;i++) {
19776                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19777             }
19778         }
19779
19780         this.items = nitems;
19781         
19782         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19783         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19784         
19785         
19786         
19787         this.initEvents();
19788     },
19789     
19790     resizeMask : function()
19791     {
19792         this.maskEl.setSize(
19793             Roo.lib.Dom.getViewWidth(true),
19794             Roo.lib.Dom.getViewHeight(true)
19795         );
19796     },
19797     
19798     initEvents : function()
19799     {
19800         
19801         if (!this.modal) { 
19802             Roo.bootstrap.Popover.register(this);
19803         }
19804          
19805         this.arrowEl = this.el.select('.arrow',true).first();
19806         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19807         this.el.enableDisplayMode('block');
19808         this.el.hide();
19809  
19810         
19811         if (this.over === false && !this.parent()) {
19812             return; 
19813         }
19814         if (this.triggers === false) {
19815             return;
19816         }
19817          
19818         // support parent
19819         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19820         var triggers = this.trigger ? this.trigger.split(' ') : [];
19821         Roo.each(triggers, function(trigger) {
19822         
19823             if (trigger == 'click') {
19824                 on_el.on('click', this.toggle, this);
19825             } else if (trigger != 'manual') {
19826                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19827                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19828       
19829                 on_el.on(eventIn  ,this.enter, this);
19830                 on_el.on(eventOut, this.leave, this);
19831             }
19832         }, this);
19833     },
19834     
19835     
19836     // private
19837     timeout : null,
19838     hoverState : null,
19839     
19840     toggle : function () {
19841         this.hoverState == 'in' ? this.leave() : this.enter();
19842     },
19843     
19844     enter : function () {
19845         
19846         clearTimeout(this.timeout);
19847     
19848         this.hoverState = 'in';
19849     
19850         if (!this.delay || !this.delay.show) {
19851             this.show();
19852             return;
19853         }
19854         var _t = this;
19855         this.timeout = setTimeout(function () {
19856             if (_t.hoverState == 'in') {
19857                 _t.show();
19858             }
19859         }, this.delay.show)
19860     },
19861     
19862     leave : function() {
19863         clearTimeout(this.timeout);
19864     
19865         this.hoverState = 'out';
19866     
19867         if (!this.delay || !this.delay.hide) {
19868             this.hide();
19869             return;
19870         }
19871         var _t = this;
19872         this.timeout = setTimeout(function () {
19873             if (_t.hoverState == 'out') {
19874                 _t.hide();
19875             }
19876         }, this.delay.hide)
19877     },
19878     /**
19879      * Show the popover
19880      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19881      * @param {string} (left|right|top|bottom) position
19882      */
19883     show : function (on_el, placement)
19884     {
19885         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19886         on_el = on_el || false; // default to false
19887          
19888         if (!on_el) {
19889             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19890                 on_el = this.parent().el;
19891             } else if (this.over) {
19892                 Roo.get(this.over);
19893             }
19894             
19895         }
19896         
19897         this.alignEl = Roo.get( on_el );
19898
19899         if (!this.el) {
19900             this.render(document.body);
19901         }
19902         
19903         
19904          
19905         
19906         if (this.title === false) {
19907             this.headerEl.hide();
19908         }
19909         
19910        
19911         this.el.show();
19912         this.el.dom.style.display = 'block';
19913          
19914  
19915         if (this.alignEl) {
19916             this.updatePosition(this.placement, true);
19917              
19918         } else {
19919             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19920             var es = this.el.getSize();
19921             var x = Roo.lib.Dom.getViewWidth()/2;
19922             var y = Roo.lib.Dom.getViewHeight()/2;
19923             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19924             
19925         }
19926
19927         
19928         //var arrow = this.el.select('.arrow',true).first();
19929         //arrow.set(align[2], 
19930         
19931         this.el.addClass('in');
19932         
19933          
19934         
19935         this.hoverState = 'in';
19936         
19937         if (this.modal) {
19938             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19939             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19940             this.maskEl.dom.style.display = 'block';
19941             this.maskEl.addClass('show');
19942         }
19943         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19944  
19945         this.fireEvent('show', this);
19946         
19947     },
19948     /**
19949      * fire this manually after loading a grid in the table for example
19950      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19951      * @param {Boolean} try and move it if we cant get right position.
19952      */
19953     updatePosition : function(placement, try_move)
19954     {
19955         // allow for calling with no parameters
19956         placement = placement   ? placement :  this.placement;
19957         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19958         
19959         this.el.removeClass([
19960             'fade','top','bottom', 'left', 'right','in',
19961             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19962         ]);
19963         this.el.addClass(placement + ' bs-popover-' + placement);
19964         
19965         if (!this.alignEl ) {
19966             return false;
19967         }
19968         
19969         switch (placement) {
19970             case 'right':
19971                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19972                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19973                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19974                     //normal display... or moved up/down.
19975                     this.el.setXY(offset);
19976                     var xy = this.alignEl.getAnchorXY('tr', false);
19977                     xy[0]+=2;xy[1]+=5;
19978                     this.arrowEl.setXY(xy);
19979                     return true;
19980                 }
19981                 // continue through...
19982                 return this.updatePosition('left', false);
19983                 
19984             
19985             case 'left':
19986                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19987                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19988                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19989                     //normal display... or moved up/down.
19990                     this.el.setXY(offset);
19991                     var xy = this.alignEl.getAnchorXY('tl', false);
19992                     xy[0]-=10;xy[1]+=5; // << fix me
19993                     this.arrowEl.setXY(xy);
19994                     return true;
19995                 }
19996                 // call self...
19997                 return this.updatePosition('right', false);
19998             
19999             case 'top':
20000                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20001                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20002                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20003                     //normal display... or moved up/down.
20004                     this.el.setXY(offset);
20005                     var xy = this.alignEl.getAnchorXY('t', false);
20006                     xy[1]-=10; // << fix me
20007                     this.arrowEl.setXY(xy);
20008                     return true;
20009                 }
20010                 // fall through
20011                return this.updatePosition('bottom', false);
20012             
20013             case 'bottom':
20014                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20015                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20016                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20017                     //normal display... or moved up/down.
20018                     this.el.setXY(offset);
20019                     var xy = this.alignEl.getAnchorXY('b', false);
20020                      xy[1]+=2; // << fix me
20021                     this.arrowEl.setXY(xy);
20022                     return true;
20023                 }
20024                 // fall through
20025                 return this.updatePosition('top', false);
20026                 
20027             
20028         }
20029         
20030         
20031         return false;
20032     },
20033     
20034     hide : function()
20035     {
20036         this.el.setXY([0,0]);
20037         this.el.removeClass('in');
20038         this.el.hide();
20039         this.hoverState = null;
20040         this.maskEl.hide(); // always..
20041         this.fireEvent('hide', this);
20042     }
20043     
20044 });
20045
20046
20047 Roo.apply(Roo.bootstrap.Popover, {
20048
20049     alignment : {
20050         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20051         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20052         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20053         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20054     },
20055     
20056     zIndex : 20001,
20057
20058     clickHander : false,
20059     
20060
20061     onMouseDown : function(e)
20062     {
20063         if (!e.getTarget(".roo-popover")) {
20064             this.hideAll();
20065         }
20066          
20067     },
20068     
20069     popups : [],
20070     
20071     register : function(popup)
20072     {
20073         if (!Roo.bootstrap.Popover.clickHandler) {
20074             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20075         }
20076         // hide other popups.
20077         this.hideAll();
20078         this.popups.push(popup);
20079     },
20080     hideAll : function()
20081     {
20082         this.popups.forEach(function(p) {
20083             p.hide();
20084         });
20085     }
20086
20087 });/*
20088  * - LGPL
20089  *
20090  * Card header - holder for the card header elements.
20091  * 
20092  */
20093
20094 /**
20095  * @class Roo.bootstrap.PopoverNav
20096  * @extends Roo.bootstrap.NavGroup
20097  * Bootstrap Popover header navigation class
20098  * @constructor
20099  * Create a new Popover Header Navigation 
20100  * @param {Object} config The config object
20101  */
20102
20103 Roo.bootstrap.PopoverNav = function(config){
20104     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20105 };
20106
20107 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20108     
20109     
20110     container_method : 'getPopoverHeader' 
20111     
20112      
20113     
20114     
20115    
20116 });
20117
20118  
20119
20120  /*
20121  * - LGPL
20122  *
20123  * Progress
20124  * 
20125  */
20126
20127 /**
20128  * @class Roo.bootstrap.Progress
20129  * @extends Roo.bootstrap.Component
20130  * Bootstrap Progress class
20131  * @cfg {Boolean} striped striped of the progress bar
20132  * @cfg {Boolean} active animated of the progress bar
20133  * 
20134  * 
20135  * @constructor
20136  * Create a new Progress
20137  * @param {Object} config The config object
20138  */
20139
20140 Roo.bootstrap.Progress = function(config){
20141     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20142 };
20143
20144 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20145     
20146     striped : false,
20147     active: false,
20148     
20149     getAutoCreate : function(){
20150         var cfg = {
20151             tag: 'div',
20152             cls: 'progress'
20153         };
20154         
20155         
20156         if(this.striped){
20157             cfg.cls += ' progress-striped';
20158         }
20159       
20160         if(this.active){
20161             cfg.cls += ' active';
20162         }
20163         
20164         
20165         return cfg;
20166     }
20167    
20168 });
20169
20170  
20171
20172  /*
20173  * - LGPL
20174  *
20175  * ProgressBar
20176  * 
20177  */
20178
20179 /**
20180  * @class Roo.bootstrap.ProgressBar
20181  * @extends Roo.bootstrap.Component
20182  * Bootstrap ProgressBar class
20183  * @cfg {Number} aria_valuenow aria-value now
20184  * @cfg {Number} aria_valuemin aria-value min
20185  * @cfg {Number} aria_valuemax aria-value max
20186  * @cfg {String} label label for the progress bar
20187  * @cfg {String} panel (success | info | warning | danger )
20188  * @cfg {String} role role of the progress bar
20189  * @cfg {String} sr_only text
20190  * 
20191  * 
20192  * @constructor
20193  * Create a new ProgressBar
20194  * @param {Object} config The config object
20195  */
20196
20197 Roo.bootstrap.ProgressBar = function(config){
20198     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20199 };
20200
20201 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20202     
20203     aria_valuenow : 0,
20204     aria_valuemin : 0,
20205     aria_valuemax : 100,
20206     label : false,
20207     panel : false,
20208     role : false,
20209     sr_only: false,
20210     
20211     getAutoCreate : function()
20212     {
20213         
20214         var cfg = {
20215             tag: 'div',
20216             cls: 'progress-bar',
20217             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20218         };
20219         
20220         if(this.sr_only){
20221             cfg.cn = {
20222                 tag: 'span',
20223                 cls: 'sr-only',
20224                 html: this.sr_only
20225             }
20226         }
20227         
20228         if(this.role){
20229             cfg.role = this.role;
20230         }
20231         
20232         if(this.aria_valuenow){
20233             cfg['aria-valuenow'] = this.aria_valuenow;
20234         }
20235         
20236         if(this.aria_valuemin){
20237             cfg['aria-valuemin'] = this.aria_valuemin;
20238         }
20239         
20240         if(this.aria_valuemax){
20241             cfg['aria-valuemax'] = this.aria_valuemax;
20242         }
20243         
20244         if(this.label && !this.sr_only){
20245             cfg.html = this.label;
20246         }
20247         
20248         if(this.panel){
20249             cfg.cls += ' progress-bar-' + this.panel;
20250         }
20251         
20252         return cfg;
20253     },
20254     
20255     update : function(aria_valuenow)
20256     {
20257         this.aria_valuenow = aria_valuenow;
20258         
20259         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20260     }
20261    
20262 });
20263
20264  
20265
20266  /*
20267  * - LGPL
20268  *
20269  * column
20270  * 
20271  */
20272
20273 /**
20274  * @class Roo.bootstrap.TabGroup
20275  * @extends Roo.bootstrap.Column
20276  * Bootstrap Column class
20277  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20278  * @cfg {Boolean} carousel true to make the group behave like a carousel
20279  * @cfg {Boolean} bullets show bullets for the panels
20280  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20281  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20282  * @cfg {Boolean} showarrow (true|false) show arrow default true
20283  * 
20284  * @constructor
20285  * Create a new TabGroup
20286  * @param {Object} config The config object
20287  */
20288
20289 Roo.bootstrap.TabGroup = function(config){
20290     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20291     if (!this.navId) {
20292         this.navId = Roo.id();
20293     }
20294     this.tabs = [];
20295     Roo.bootstrap.TabGroup.register(this);
20296     
20297 };
20298
20299 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20300     
20301     carousel : false,
20302     transition : false,
20303     bullets : 0,
20304     timer : 0,
20305     autoslide : false,
20306     slideFn : false,
20307     slideOnTouch : false,
20308     showarrow : true,
20309     
20310     getAutoCreate : function()
20311     {
20312         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20313         
20314         cfg.cls += ' tab-content';
20315         
20316         if (this.carousel) {
20317             cfg.cls += ' carousel slide';
20318             
20319             cfg.cn = [{
20320                cls : 'carousel-inner',
20321                cn : []
20322             }];
20323         
20324             if(this.bullets  && !Roo.isTouch){
20325                 
20326                 var bullets = {
20327                     cls : 'carousel-bullets',
20328                     cn : []
20329                 };
20330                
20331                 if(this.bullets_cls){
20332                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20333                 }
20334                 
20335                 bullets.cn.push({
20336                     cls : 'clear'
20337                 });
20338                 
20339                 cfg.cn[0].cn.push(bullets);
20340             }
20341             
20342             if(this.showarrow){
20343                 cfg.cn[0].cn.push({
20344                     tag : 'div',
20345                     class : 'carousel-arrow',
20346                     cn : [
20347                         {
20348                             tag : 'div',
20349                             class : 'carousel-prev',
20350                             cn : [
20351                                 {
20352                                     tag : 'i',
20353                                     class : 'fa fa-chevron-left'
20354                                 }
20355                             ]
20356                         },
20357                         {
20358                             tag : 'div',
20359                             class : 'carousel-next',
20360                             cn : [
20361                                 {
20362                                     tag : 'i',
20363                                     class : 'fa fa-chevron-right'
20364                                 }
20365                             ]
20366                         }
20367                     ]
20368                 });
20369             }
20370             
20371         }
20372         
20373         return cfg;
20374     },
20375     
20376     initEvents:  function()
20377     {
20378 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20379 //            this.el.on("touchstart", this.onTouchStart, this);
20380 //        }
20381         
20382         if(this.autoslide){
20383             var _this = this;
20384             
20385             this.slideFn = window.setInterval(function() {
20386                 _this.showPanelNext();
20387             }, this.timer);
20388         }
20389         
20390         if(this.showarrow){
20391             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20392             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20393         }
20394         
20395         
20396     },
20397     
20398 //    onTouchStart : function(e, el, o)
20399 //    {
20400 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20401 //            return;
20402 //        }
20403 //        
20404 //        this.showPanelNext();
20405 //    },
20406     
20407     
20408     getChildContainer : function()
20409     {
20410         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20411     },
20412     
20413     /**
20414     * register a Navigation item
20415     * @param {Roo.bootstrap.NavItem} the navitem to add
20416     */
20417     register : function(item)
20418     {
20419         this.tabs.push( item);
20420         item.navId = this.navId; // not really needed..
20421         this.addBullet();
20422     
20423     },
20424     
20425     getActivePanel : function()
20426     {
20427         var r = false;
20428         Roo.each(this.tabs, function(t) {
20429             if (t.active) {
20430                 r = t;
20431                 return false;
20432             }
20433             return null;
20434         });
20435         return r;
20436         
20437     },
20438     getPanelByName : function(n)
20439     {
20440         var r = false;
20441         Roo.each(this.tabs, function(t) {
20442             if (t.tabId == n) {
20443                 r = t;
20444                 return false;
20445             }
20446             return null;
20447         });
20448         return r;
20449     },
20450     indexOfPanel : function(p)
20451     {
20452         var r = false;
20453         Roo.each(this.tabs, function(t,i) {
20454             if (t.tabId == p.tabId) {
20455                 r = i;
20456                 return false;
20457             }
20458             return null;
20459         });
20460         return r;
20461     },
20462     /**
20463      * show a specific panel
20464      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20465      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20466      */
20467     showPanel : function (pan)
20468     {
20469         if(this.transition || typeof(pan) == 'undefined'){
20470             Roo.log("waiting for the transitionend");
20471             return false;
20472         }
20473         
20474         if (typeof(pan) == 'number') {
20475             pan = this.tabs[pan];
20476         }
20477         
20478         if (typeof(pan) == 'string') {
20479             pan = this.getPanelByName(pan);
20480         }
20481         
20482         var cur = this.getActivePanel();
20483         
20484         if(!pan || !cur){
20485             Roo.log('pan or acitve pan is undefined');
20486             return false;
20487         }
20488         
20489         if (pan.tabId == this.getActivePanel().tabId) {
20490             return true;
20491         }
20492         
20493         if (false === cur.fireEvent('beforedeactivate')) {
20494             return false;
20495         }
20496         
20497         if(this.bullets > 0 && !Roo.isTouch){
20498             this.setActiveBullet(this.indexOfPanel(pan));
20499         }
20500         
20501         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20502             
20503             //class="carousel-item carousel-item-next carousel-item-left"
20504             
20505             this.transition = true;
20506             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20507             var lr = dir == 'next' ? 'left' : 'right';
20508             pan.el.addClass(dir); // or prev
20509             pan.el.addClass('carousel-item-' + dir); // or prev
20510             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20511             cur.el.addClass(lr); // or right
20512             pan.el.addClass(lr);
20513             cur.el.addClass('carousel-item-' +lr); // or right
20514             pan.el.addClass('carousel-item-' +lr);
20515             
20516             
20517             var _this = this;
20518             cur.el.on('transitionend', function() {
20519                 Roo.log("trans end?");
20520                 
20521                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20522                 pan.setActive(true);
20523                 
20524                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20525                 cur.setActive(false);
20526                 
20527                 _this.transition = false;
20528                 
20529             }, this, { single:  true } );
20530             
20531             return true;
20532         }
20533         
20534         cur.setActive(false);
20535         pan.setActive(true);
20536         
20537         return true;
20538         
20539     },
20540     showPanelNext : function()
20541     {
20542         var i = this.indexOfPanel(this.getActivePanel());
20543         
20544         if (i >= this.tabs.length - 1 && !this.autoslide) {
20545             return;
20546         }
20547         
20548         if (i >= this.tabs.length - 1 && this.autoslide) {
20549             i = -1;
20550         }
20551         
20552         this.showPanel(this.tabs[i+1]);
20553     },
20554     
20555     showPanelPrev : function()
20556     {
20557         var i = this.indexOfPanel(this.getActivePanel());
20558         
20559         if (i  < 1 && !this.autoslide) {
20560             return;
20561         }
20562         
20563         if (i < 1 && this.autoslide) {
20564             i = this.tabs.length;
20565         }
20566         
20567         this.showPanel(this.tabs[i-1]);
20568     },
20569     
20570     
20571     addBullet: function()
20572     {
20573         if(!this.bullets || Roo.isTouch){
20574             return;
20575         }
20576         var ctr = this.el.select('.carousel-bullets',true).first();
20577         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20578         var bullet = ctr.createChild({
20579             cls : 'bullet bullet-' + i
20580         },ctr.dom.lastChild);
20581         
20582         
20583         var _this = this;
20584         
20585         bullet.on('click', (function(e, el, o, ii, t){
20586
20587             e.preventDefault();
20588
20589             this.showPanel(ii);
20590
20591             if(this.autoslide && this.slideFn){
20592                 clearInterval(this.slideFn);
20593                 this.slideFn = window.setInterval(function() {
20594                     _this.showPanelNext();
20595                 }, this.timer);
20596             }
20597
20598         }).createDelegate(this, [i, bullet], true));
20599                 
20600         
20601     },
20602      
20603     setActiveBullet : function(i)
20604     {
20605         if(Roo.isTouch){
20606             return;
20607         }
20608         
20609         Roo.each(this.el.select('.bullet', true).elements, function(el){
20610             el.removeClass('selected');
20611         });
20612
20613         var bullet = this.el.select('.bullet-' + i, true).first();
20614         
20615         if(!bullet){
20616             return;
20617         }
20618         
20619         bullet.addClass('selected');
20620     }
20621     
20622     
20623   
20624 });
20625
20626  
20627
20628  
20629  
20630 Roo.apply(Roo.bootstrap.TabGroup, {
20631     
20632     groups: {},
20633      /**
20634     * register a Navigation Group
20635     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20636     */
20637     register : function(navgrp)
20638     {
20639         this.groups[navgrp.navId] = navgrp;
20640         
20641     },
20642     /**
20643     * fetch a Navigation Group based on the navigation ID
20644     * if one does not exist , it will get created.
20645     * @param {string} the navgroup to add
20646     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20647     */
20648     get: function(navId) {
20649         if (typeof(this.groups[navId]) == 'undefined') {
20650             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20651         }
20652         return this.groups[navId] ;
20653     }
20654     
20655     
20656     
20657 });
20658
20659  /*
20660  * - LGPL
20661  *
20662  * TabPanel
20663  * 
20664  */
20665
20666 /**
20667  * @class Roo.bootstrap.TabPanel
20668  * @extends Roo.bootstrap.Component
20669  * Bootstrap TabPanel class
20670  * @cfg {Boolean} active panel active
20671  * @cfg {String} html panel content
20672  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20673  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20674  * @cfg {String} href click to link..
20675  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20676  * 
20677  * 
20678  * @constructor
20679  * Create a new TabPanel
20680  * @param {Object} config The config object
20681  */
20682
20683 Roo.bootstrap.TabPanel = function(config){
20684     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20685     this.addEvents({
20686         /**
20687              * @event changed
20688              * Fires when the active status changes
20689              * @param {Roo.bootstrap.TabPanel} this
20690              * @param {Boolean} state the new state
20691             
20692          */
20693         'changed': true,
20694         /**
20695              * @event beforedeactivate
20696              * Fires before a tab is de-activated - can be used to do validation on a form.
20697              * @param {Roo.bootstrap.TabPanel} this
20698              * @return {Boolean} false if there is an error
20699             
20700          */
20701         'beforedeactivate': true
20702      });
20703     
20704     this.tabId = this.tabId || Roo.id();
20705   
20706 };
20707
20708 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20709     
20710     active: false,
20711     html: false,
20712     tabId: false,
20713     navId : false,
20714     href : '',
20715     touchSlide : false,
20716     getAutoCreate : function(){
20717         
20718         
20719         var cfg = {
20720             tag: 'div',
20721             // item is needed for carousel - not sure if it has any effect otherwise
20722             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20723             html: this.html || ''
20724         };
20725         
20726         if(this.active){
20727             cfg.cls += ' active';
20728         }
20729         
20730         if(this.tabId){
20731             cfg.tabId = this.tabId;
20732         }
20733         
20734         
20735         
20736         return cfg;
20737     },
20738     
20739     initEvents:  function()
20740     {
20741         var p = this.parent();
20742         
20743         this.navId = this.navId || p.navId;
20744         
20745         if (typeof(this.navId) != 'undefined') {
20746             // not really needed.. but just in case.. parent should be a NavGroup.
20747             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20748             
20749             tg.register(this);
20750             
20751             var i = tg.tabs.length - 1;
20752             
20753             if(this.active && tg.bullets > 0 && i < tg.bullets){
20754                 tg.setActiveBullet(i);
20755             }
20756         }
20757         
20758         this.el.on('click', this.onClick, this);
20759         
20760         if(Roo.isTouch && this.touchSlide){
20761             this.el.on("touchstart", this.onTouchStart, this);
20762             this.el.on("touchmove", this.onTouchMove, this);
20763             this.el.on("touchend", this.onTouchEnd, this);
20764         }
20765         
20766     },
20767     
20768     onRender : function(ct, position)
20769     {
20770         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20771     },
20772     
20773     setActive : function(state)
20774     {
20775         Roo.log("panel - set active " + this.tabId + "=" + state);
20776         
20777         this.active = state;
20778         if (!state) {
20779             this.el.removeClass('active');
20780             
20781         } else  if (!this.el.hasClass('active')) {
20782             this.el.addClass('active');
20783         }
20784         
20785         this.fireEvent('changed', this, state);
20786     },
20787     
20788     onClick : function(e)
20789     {
20790         e.preventDefault();
20791         
20792         if(!this.href.length){
20793             return;
20794         }
20795         
20796         window.location.href = this.href;
20797     },
20798     
20799     startX : 0,
20800     startY : 0,
20801     endX : 0,
20802     endY : 0,
20803     swiping : false,
20804     
20805     onTouchStart : function(e)
20806     {
20807         this.swiping = false;
20808         
20809         this.startX = e.browserEvent.touches[0].clientX;
20810         this.startY = e.browserEvent.touches[0].clientY;
20811     },
20812     
20813     onTouchMove : function(e)
20814     {
20815         this.swiping = true;
20816         
20817         this.endX = e.browserEvent.touches[0].clientX;
20818         this.endY = e.browserEvent.touches[0].clientY;
20819     },
20820     
20821     onTouchEnd : function(e)
20822     {
20823         if(!this.swiping){
20824             this.onClick(e);
20825             return;
20826         }
20827         
20828         var tabGroup = this.parent();
20829         
20830         if(this.endX > this.startX){ // swiping right
20831             tabGroup.showPanelPrev();
20832             return;
20833         }
20834         
20835         if(this.startX > this.endX){ // swiping left
20836             tabGroup.showPanelNext();
20837             return;
20838         }
20839     }
20840     
20841     
20842 });
20843  
20844
20845  
20846
20847  /*
20848  * - LGPL
20849  *
20850  * DateField
20851  * 
20852  */
20853
20854 /**
20855  * @class Roo.bootstrap.DateField
20856  * @extends Roo.bootstrap.Input
20857  * Bootstrap DateField class
20858  * @cfg {Number} weekStart default 0
20859  * @cfg {String} viewMode default empty, (months|years)
20860  * @cfg {String} minViewMode default empty, (months|years)
20861  * @cfg {Number} startDate default -Infinity
20862  * @cfg {Number} endDate default Infinity
20863  * @cfg {Boolean} todayHighlight default false
20864  * @cfg {Boolean} todayBtn default false
20865  * @cfg {Boolean} calendarWeeks default false
20866  * @cfg {Object} daysOfWeekDisabled default empty
20867  * @cfg {Boolean} singleMode default false (true | false)
20868  * 
20869  * @cfg {Boolean} keyboardNavigation default true
20870  * @cfg {String} language default en
20871  * 
20872  * @constructor
20873  * Create a new DateField
20874  * @param {Object} config The config object
20875  */
20876
20877 Roo.bootstrap.DateField = function(config){
20878     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20879      this.addEvents({
20880             /**
20881              * @event show
20882              * Fires when this field show.
20883              * @param {Roo.bootstrap.DateField} this
20884              * @param {Mixed} date The date value
20885              */
20886             show : true,
20887             /**
20888              * @event show
20889              * Fires when this field hide.
20890              * @param {Roo.bootstrap.DateField} this
20891              * @param {Mixed} date The date value
20892              */
20893             hide : true,
20894             /**
20895              * @event select
20896              * Fires when select a date.
20897              * @param {Roo.bootstrap.DateField} this
20898              * @param {Mixed} date The date value
20899              */
20900             select : true,
20901             /**
20902              * @event beforeselect
20903              * Fires when before select a date.
20904              * @param {Roo.bootstrap.DateField} this
20905              * @param {Mixed} date The date value
20906              */
20907             beforeselect : true
20908         });
20909 };
20910
20911 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20912     
20913     /**
20914      * @cfg {String} format
20915      * The default date format string which can be overriden for localization support.  The format must be
20916      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20917      */
20918     format : "m/d/y",
20919     /**
20920      * @cfg {String} altFormats
20921      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20922      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20923      */
20924     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20925     
20926     weekStart : 0,
20927     
20928     viewMode : '',
20929     
20930     minViewMode : '',
20931     
20932     todayHighlight : false,
20933     
20934     todayBtn: false,
20935     
20936     language: 'en',
20937     
20938     keyboardNavigation: true,
20939     
20940     calendarWeeks: false,
20941     
20942     startDate: -Infinity,
20943     
20944     endDate: Infinity,
20945     
20946     daysOfWeekDisabled: [],
20947     
20948     _events: [],
20949     
20950     singleMode : false,
20951     
20952     UTCDate: function()
20953     {
20954         return new Date(Date.UTC.apply(Date, arguments));
20955     },
20956     
20957     UTCToday: function()
20958     {
20959         var today = new Date();
20960         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20961     },
20962     
20963     getDate: function() {
20964             var d = this.getUTCDate();
20965             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20966     },
20967     
20968     getUTCDate: function() {
20969             return this.date;
20970     },
20971     
20972     setDate: function(d) {
20973             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20974     },
20975     
20976     setUTCDate: function(d) {
20977             this.date = d;
20978             this.setValue(this.formatDate(this.date));
20979     },
20980         
20981     onRender: function(ct, position)
20982     {
20983         
20984         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20985         
20986         this.language = this.language || 'en';
20987         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20988         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20989         
20990         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20991         this.format = this.format || 'm/d/y';
20992         this.isInline = false;
20993         this.isInput = true;
20994         this.component = this.el.select('.add-on', true).first() || false;
20995         this.component = (this.component && this.component.length === 0) ? false : this.component;
20996         this.hasInput = this.component && this.inputEl().length;
20997         
20998         if (typeof(this.minViewMode === 'string')) {
20999             switch (this.minViewMode) {
21000                 case 'months':
21001                     this.minViewMode = 1;
21002                     break;
21003                 case 'years':
21004                     this.minViewMode = 2;
21005                     break;
21006                 default:
21007                     this.minViewMode = 0;
21008                     break;
21009             }
21010         }
21011         
21012         if (typeof(this.viewMode === 'string')) {
21013             switch (this.viewMode) {
21014                 case 'months':
21015                     this.viewMode = 1;
21016                     break;
21017                 case 'years':
21018                     this.viewMode = 2;
21019                     break;
21020                 default:
21021                     this.viewMode = 0;
21022                     break;
21023             }
21024         }
21025                 
21026         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21027         
21028 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21029         
21030         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21031         
21032         this.picker().on('mousedown', this.onMousedown, this);
21033         this.picker().on('click', this.onClick, this);
21034         
21035         this.picker().addClass('datepicker-dropdown');
21036         
21037         this.startViewMode = this.viewMode;
21038         
21039         if(this.singleMode){
21040             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21041                 v.setVisibilityMode(Roo.Element.DISPLAY);
21042                 v.hide();
21043             });
21044             
21045             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21046                 v.setStyle('width', '189px');
21047             });
21048         }
21049         
21050         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21051             if(!this.calendarWeeks){
21052                 v.remove();
21053                 return;
21054             }
21055             
21056             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21057             v.attr('colspan', function(i, val){
21058                 return parseInt(val) + 1;
21059             });
21060         });
21061                         
21062         
21063         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21064         
21065         this.setStartDate(this.startDate);
21066         this.setEndDate(this.endDate);
21067         
21068         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21069         
21070         this.fillDow();
21071         this.fillMonths();
21072         this.update();
21073         this.showMode();
21074         
21075         if(this.isInline) {
21076             this.showPopup();
21077         }
21078     },
21079     
21080     picker : function()
21081     {
21082         return this.pickerEl;
21083 //        return this.el.select('.datepicker', true).first();
21084     },
21085     
21086     fillDow: function()
21087     {
21088         var dowCnt = this.weekStart;
21089         
21090         var dow = {
21091             tag: 'tr',
21092             cn: [
21093                 
21094             ]
21095         };
21096         
21097         if(this.calendarWeeks){
21098             dow.cn.push({
21099                 tag: 'th',
21100                 cls: 'cw',
21101                 html: '&nbsp;'
21102             })
21103         }
21104         
21105         while (dowCnt < this.weekStart + 7) {
21106             dow.cn.push({
21107                 tag: 'th',
21108                 cls: 'dow',
21109                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21110             });
21111         }
21112         
21113         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21114     },
21115     
21116     fillMonths: function()
21117     {    
21118         var i = 0;
21119         var months = this.picker().select('>.datepicker-months td', true).first();
21120         
21121         months.dom.innerHTML = '';
21122         
21123         while (i < 12) {
21124             var month = {
21125                 tag: 'span',
21126                 cls: 'month',
21127                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21128             };
21129             
21130             months.createChild(month);
21131         }
21132         
21133     },
21134     
21135     update: function()
21136     {
21137         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;
21138         
21139         if (this.date < this.startDate) {
21140             this.viewDate = new Date(this.startDate);
21141         } else if (this.date > this.endDate) {
21142             this.viewDate = new Date(this.endDate);
21143         } else {
21144             this.viewDate = new Date(this.date);
21145         }
21146         
21147         this.fill();
21148     },
21149     
21150     fill: function() 
21151     {
21152         var d = new Date(this.viewDate),
21153                 year = d.getUTCFullYear(),
21154                 month = d.getUTCMonth(),
21155                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21156                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21157                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21158                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21159                 currentDate = this.date && this.date.valueOf(),
21160                 today = this.UTCToday();
21161         
21162         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21163         
21164 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21165         
21166 //        this.picker.select('>tfoot th.today').
21167 //                                              .text(dates[this.language].today)
21168 //                                              .toggle(this.todayBtn !== false);
21169     
21170         this.updateNavArrows();
21171         this.fillMonths();
21172                                                 
21173         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21174         
21175         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21176          
21177         prevMonth.setUTCDate(day);
21178         
21179         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21180         
21181         var nextMonth = new Date(prevMonth);
21182         
21183         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21184         
21185         nextMonth = nextMonth.valueOf();
21186         
21187         var fillMonths = false;
21188         
21189         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21190         
21191         while(prevMonth.valueOf() <= nextMonth) {
21192             var clsName = '';
21193             
21194             if (prevMonth.getUTCDay() === this.weekStart) {
21195                 if(fillMonths){
21196                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21197                 }
21198                     
21199                 fillMonths = {
21200                     tag: 'tr',
21201                     cn: []
21202                 };
21203                 
21204                 if(this.calendarWeeks){
21205                     // ISO 8601: First week contains first thursday.
21206                     // ISO also states week starts on Monday, but we can be more abstract here.
21207                     var
21208                     // Start of current week: based on weekstart/current date
21209                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21210                     // Thursday of this week
21211                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21212                     // First Thursday of year, year from thursday
21213                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21214                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21215                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21216                     
21217                     fillMonths.cn.push({
21218                         tag: 'td',
21219                         cls: 'cw',
21220                         html: calWeek
21221                     });
21222                 }
21223             }
21224             
21225             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21226                 clsName += ' old';
21227             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21228                 clsName += ' new';
21229             }
21230             if (this.todayHighlight &&
21231                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21232                 prevMonth.getUTCMonth() == today.getMonth() &&
21233                 prevMonth.getUTCDate() == today.getDate()) {
21234                 clsName += ' today';
21235             }
21236             
21237             if (currentDate && prevMonth.valueOf() === currentDate) {
21238                 clsName += ' active';
21239             }
21240             
21241             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21242                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21243                     clsName += ' disabled';
21244             }
21245             
21246             fillMonths.cn.push({
21247                 tag: 'td',
21248                 cls: 'day ' + clsName,
21249                 html: prevMonth.getDate()
21250             });
21251             
21252             prevMonth.setDate(prevMonth.getDate()+1);
21253         }
21254           
21255         var currentYear = this.date && this.date.getUTCFullYear();
21256         var currentMonth = this.date && this.date.getUTCMonth();
21257         
21258         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21259         
21260         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21261             v.removeClass('active');
21262             
21263             if(currentYear === year && k === currentMonth){
21264                 v.addClass('active');
21265             }
21266             
21267             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21268                 v.addClass('disabled');
21269             }
21270             
21271         });
21272         
21273         
21274         year = parseInt(year/10, 10) * 10;
21275         
21276         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21277         
21278         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21279         
21280         year -= 1;
21281         for (var i = -1; i < 11; i++) {
21282             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21283                 tag: 'span',
21284                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21285                 html: year
21286             });
21287             
21288             year += 1;
21289         }
21290     },
21291     
21292     showMode: function(dir) 
21293     {
21294         if (dir) {
21295             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21296         }
21297         
21298         Roo.each(this.picker().select('>div',true).elements, function(v){
21299             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21300             v.hide();
21301         });
21302         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21303     },
21304     
21305     place: function()
21306     {
21307         if(this.isInline) {
21308             return;
21309         }
21310         
21311         this.picker().removeClass(['bottom', 'top']);
21312         
21313         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21314             /*
21315              * place to the top of element!
21316              *
21317              */
21318             
21319             this.picker().addClass('top');
21320             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21321             
21322             return;
21323         }
21324         
21325         this.picker().addClass('bottom');
21326         
21327         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21328     },
21329     
21330     parseDate : function(value)
21331     {
21332         if(!value || value instanceof Date){
21333             return value;
21334         }
21335         var v = Date.parseDate(value, this.format);
21336         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21337             v = Date.parseDate(value, 'Y-m-d');
21338         }
21339         if(!v && this.altFormats){
21340             if(!this.altFormatsArray){
21341                 this.altFormatsArray = this.altFormats.split("|");
21342             }
21343             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21344                 v = Date.parseDate(value, this.altFormatsArray[i]);
21345             }
21346         }
21347         return v;
21348     },
21349     
21350     formatDate : function(date, fmt)
21351     {   
21352         return (!date || !(date instanceof Date)) ?
21353         date : date.dateFormat(fmt || this.format);
21354     },
21355     
21356     onFocus : function()
21357     {
21358         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21359         this.showPopup();
21360     },
21361     
21362     onBlur : function()
21363     {
21364         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21365         
21366         var d = this.inputEl().getValue();
21367         
21368         this.setValue(d);
21369                 
21370         this.hidePopup();
21371     },
21372     
21373     showPopup : function()
21374     {
21375         this.picker().show();
21376         this.update();
21377         this.place();
21378         
21379         this.fireEvent('showpopup', this, this.date);
21380     },
21381     
21382     hidePopup : function()
21383     {
21384         if(this.isInline) {
21385             return;
21386         }
21387         this.picker().hide();
21388         this.viewMode = this.startViewMode;
21389         this.showMode();
21390         
21391         this.fireEvent('hidepopup', this, this.date);
21392         
21393     },
21394     
21395     onMousedown: function(e)
21396     {
21397         e.stopPropagation();
21398         e.preventDefault();
21399     },
21400     
21401     keyup: function(e)
21402     {
21403         Roo.bootstrap.DateField.superclass.keyup.call(this);
21404         this.update();
21405     },
21406
21407     setValue: function(v)
21408     {
21409         if(this.fireEvent('beforeselect', this, v) !== false){
21410             var d = new Date(this.parseDate(v) ).clearTime();
21411         
21412             if(isNaN(d.getTime())){
21413                 this.date = this.viewDate = '';
21414                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21415                 return;
21416             }
21417
21418             v = this.formatDate(d);
21419
21420             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21421
21422             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21423
21424             this.update();
21425
21426             this.fireEvent('select', this, this.date);
21427         }
21428     },
21429     
21430     getValue: function()
21431     {
21432         return this.formatDate(this.date);
21433     },
21434     
21435     fireKey: function(e)
21436     {
21437         if (!this.picker().isVisible()){
21438             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21439                 this.showPopup();
21440             }
21441             return;
21442         }
21443         
21444         var dateChanged = false,
21445         dir, day, month,
21446         newDate, newViewDate;
21447         
21448         switch(e.keyCode){
21449             case 27: // escape
21450                 this.hidePopup();
21451                 e.preventDefault();
21452                 break;
21453             case 37: // left
21454             case 39: // right
21455                 if (!this.keyboardNavigation) {
21456                     break;
21457                 }
21458                 dir = e.keyCode == 37 ? -1 : 1;
21459                 
21460                 if (e.ctrlKey){
21461                     newDate = this.moveYear(this.date, dir);
21462                     newViewDate = this.moveYear(this.viewDate, dir);
21463                 } else if (e.shiftKey){
21464                     newDate = this.moveMonth(this.date, dir);
21465                     newViewDate = this.moveMonth(this.viewDate, dir);
21466                 } else {
21467                     newDate = new Date(this.date);
21468                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21469                     newViewDate = new Date(this.viewDate);
21470                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21471                 }
21472                 if (this.dateWithinRange(newDate)){
21473                     this.date = newDate;
21474                     this.viewDate = newViewDate;
21475                     this.setValue(this.formatDate(this.date));
21476 //                    this.update();
21477                     e.preventDefault();
21478                     dateChanged = true;
21479                 }
21480                 break;
21481             case 38: // up
21482             case 40: // down
21483                 if (!this.keyboardNavigation) {
21484                     break;
21485                 }
21486                 dir = e.keyCode == 38 ? -1 : 1;
21487                 if (e.ctrlKey){
21488                     newDate = this.moveYear(this.date, dir);
21489                     newViewDate = this.moveYear(this.viewDate, dir);
21490                 } else if (e.shiftKey){
21491                     newDate = this.moveMonth(this.date, dir);
21492                     newViewDate = this.moveMonth(this.viewDate, dir);
21493                 } else {
21494                     newDate = new Date(this.date);
21495                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21496                     newViewDate = new Date(this.viewDate);
21497                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21498                 }
21499                 if (this.dateWithinRange(newDate)){
21500                     this.date = newDate;
21501                     this.viewDate = newViewDate;
21502                     this.setValue(this.formatDate(this.date));
21503 //                    this.update();
21504                     e.preventDefault();
21505                     dateChanged = true;
21506                 }
21507                 break;
21508             case 13: // enter
21509                 this.setValue(this.formatDate(this.date));
21510                 this.hidePopup();
21511                 e.preventDefault();
21512                 break;
21513             case 9: // tab
21514                 this.setValue(this.formatDate(this.date));
21515                 this.hidePopup();
21516                 break;
21517             case 16: // shift
21518             case 17: // ctrl
21519             case 18: // alt
21520                 break;
21521             default :
21522                 this.hidePopup();
21523                 
21524         }
21525     },
21526     
21527     
21528     onClick: function(e) 
21529     {
21530         e.stopPropagation();
21531         e.preventDefault();
21532         
21533         var target = e.getTarget();
21534         
21535         if(target.nodeName.toLowerCase() === 'i'){
21536             target = Roo.get(target).dom.parentNode;
21537         }
21538         
21539         var nodeName = target.nodeName;
21540         var className = target.className;
21541         var html = target.innerHTML;
21542         //Roo.log(nodeName);
21543         
21544         switch(nodeName.toLowerCase()) {
21545             case 'th':
21546                 switch(className) {
21547                     case 'switch':
21548                         this.showMode(1);
21549                         break;
21550                     case 'prev':
21551                     case 'next':
21552                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21553                         switch(this.viewMode){
21554                                 case 0:
21555                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21556                                         break;
21557                                 case 1:
21558                                 case 2:
21559                                         this.viewDate = this.moveYear(this.viewDate, dir);
21560                                         break;
21561                         }
21562                         this.fill();
21563                         break;
21564                     case 'today':
21565                         var date = new Date();
21566                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21567 //                        this.fill()
21568                         this.setValue(this.formatDate(this.date));
21569                         
21570                         this.hidePopup();
21571                         break;
21572                 }
21573                 break;
21574             case 'span':
21575                 if (className.indexOf('disabled') < 0) {
21576                     this.viewDate.setUTCDate(1);
21577                     if (className.indexOf('month') > -1) {
21578                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21579                     } else {
21580                         var year = parseInt(html, 10) || 0;
21581                         this.viewDate.setUTCFullYear(year);
21582                         
21583                     }
21584                     
21585                     if(this.singleMode){
21586                         this.setValue(this.formatDate(this.viewDate));
21587                         this.hidePopup();
21588                         return;
21589                     }
21590                     
21591                     this.showMode(-1);
21592                     this.fill();
21593                 }
21594                 break;
21595                 
21596             case 'td':
21597                 //Roo.log(className);
21598                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21599                     var day = parseInt(html, 10) || 1;
21600                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21601                         month = (this.viewDate || new Date()).getUTCMonth();
21602
21603                     if (className.indexOf('old') > -1) {
21604                         if(month === 0 ){
21605                             month = 11;
21606                             year -= 1;
21607                         }else{
21608                             month -= 1;
21609                         }
21610                     } else if (className.indexOf('new') > -1) {
21611                         if (month == 11) {
21612                             month = 0;
21613                             year += 1;
21614                         } else {
21615                             month += 1;
21616                         }
21617                     }
21618                     //Roo.log([year,month,day]);
21619                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21620                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21621 //                    this.fill();
21622                     //Roo.log(this.formatDate(this.date));
21623                     this.setValue(this.formatDate(this.date));
21624                     this.hidePopup();
21625                 }
21626                 break;
21627         }
21628     },
21629     
21630     setStartDate: function(startDate)
21631     {
21632         this.startDate = startDate || -Infinity;
21633         if (this.startDate !== -Infinity) {
21634             this.startDate = this.parseDate(this.startDate);
21635         }
21636         this.update();
21637         this.updateNavArrows();
21638     },
21639
21640     setEndDate: function(endDate)
21641     {
21642         this.endDate = endDate || Infinity;
21643         if (this.endDate !== Infinity) {
21644             this.endDate = this.parseDate(this.endDate);
21645         }
21646         this.update();
21647         this.updateNavArrows();
21648     },
21649     
21650     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21651     {
21652         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21653         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21654             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21655         }
21656         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21657             return parseInt(d, 10);
21658         });
21659         this.update();
21660         this.updateNavArrows();
21661     },
21662     
21663     updateNavArrows: function() 
21664     {
21665         if(this.singleMode){
21666             return;
21667         }
21668         
21669         var d = new Date(this.viewDate),
21670         year = d.getUTCFullYear(),
21671         month = d.getUTCMonth();
21672         
21673         Roo.each(this.picker().select('.prev', true).elements, function(v){
21674             v.show();
21675             switch (this.viewMode) {
21676                 case 0:
21677
21678                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21679                         v.hide();
21680                     }
21681                     break;
21682                 case 1:
21683                 case 2:
21684                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21685                         v.hide();
21686                     }
21687                     break;
21688             }
21689         });
21690         
21691         Roo.each(this.picker().select('.next', true).elements, function(v){
21692             v.show();
21693             switch (this.viewMode) {
21694                 case 0:
21695
21696                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21697                         v.hide();
21698                     }
21699                     break;
21700                 case 1:
21701                 case 2:
21702                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21703                         v.hide();
21704                     }
21705                     break;
21706             }
21707         })
21708     },
21709     
21710     moveMonth: function(date, dir)
21711     {
21712         if (!dir) {
21713             return date;
21714         }
21715         var new_date = new Date(date.valueOf()),
21716         day = new_date.getUTCDate(),
21717         month = new_date.getUTCMonth(),
21718         mag = Math.abs(dir),
21719         new_month, test;
21720         dir = dir > 0 ? 1 : -1;
21721         if (mag == 1){
21722             test = dir == -1
21723             // If going back one month, make sure month is not current month
21724             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21725             ? function(){
21726                 return new_date.getUTCMonth() == month;
21727             }
21728             // If going forward one month, make sure month is as expected
21729             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21730             : function(){
21731                 return new_date.getUTCMonth() != new_month;
21732             };
21733             new_month = month + dir;
21734             new_date.setUTCMonth(new_month);
21735             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21736             if (new_month < 0 || new_month > 11) {
21737                 new_month = (new_month + 12) % 12;
21738             }
21739         } else {
21740             // For magnitudes >1, move one month at a time...
21741             for (var i=0; i<mag; i++) {
21742                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21743                 new_date = this.moveMonth(new_date, dir);
21744             }
21745             // ...then reset the day, keeping it in the new month
21746             new_month = new_date.getUTCMonth();
21747             new_date.setUTCDate(day);
21748             test = function(){
21749                 return new_month != new_date.getUTCMonth();
21750             };
21751         }
21752         // Common date-resetting loop -- if date is beyond end of month, make it
21753         // end of month
21754         while (test()){
21755             new_date.setUTCDate(--day);
21756             new_date.setUTCMonth(new_month);
21757         }
21758         return new_date;
21759     },
21760
21761     moveYear: function(date, dir)
21762     {
21763         return this.moveMonth(date, dir*12);
21764     },
21765
21766     dateWithinRange: function(date)
21767     {
21768         return date >= this.startDate && date <= this.endDate;
21769     },
21770
21771     
21772     remove: function() 
21773     {
21774         this.picker().remove();
21775     },
21776     
21777     validateValue : function(value)
21778     {
21779         if(this.getVisibilityEl().hasClass('hidden')){
21780             return true;
21781         }
21782         
21783         if(value.length < 1)  {
21784             if(this.allowBlank){
21785                 return true;
21786             }
21787             return false;
21788         }
21789         
21790         if(value.length < this.minLength){
21791             return false;
21792         }
21793         if(value.length > this.maxLength){
21794             return false;
21795         }
21796         if(this.vtype){
21797             var vt = Roo.form.VTypes;
21798             if(!vt[this.vtype](value, this)){
21799                 return false;
21800             }
21801         }
21802         if(typeof this.validator == "function"){
21803             var msg = this.validator(value);
21804             if(msg !== true){
21805                 return false;
21806             }
21807         }
21808         
21809         if(this.regex && !this.regex.test(value)){
21810             return false;
21811         }
21812         
21813         if(typeof(this.parseDate(value)) == 'undefined'){
21814             return false;
21815         }
21816         
21817         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21818             return false;
21819         }      
21820         
21821         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21822             return false;
21823         } 
21824         
21825         
21826         return true;
21827     },
21828     
21829     reset : function()
21830     {
21831         this.date = this.viewDate = '';
21832         
21833         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21834     }
21835    
21836 });
21837
21838 Roo.apply(Roo.bootstrap.DateField,  {
21839     
21840     head : {
21841         tag: 'thead',
21842         cn: [
21843         {
21844             tag: 'tr',
21845             cn: [
21846             {
21847                 tag: 'th',
21848                 cls: 'prev',
21849                 html: '<i class="fa fa-arrow-left"/>'
21850             },
21851             {
21852                 tag: 'th',
21853                 cls: 'switch',
21854                 colspan: '5'
21855             },
21856             {
21857                 tag: 'th',
21858                 cls: 'next',
21859                 html: '<i class="fa fa-arrow-right"/>'
21860             }
21861
21862             ]
21863         }
21864         ]
21865     },
21866     
21867     content : {
21868         tag: 'tbody',
21869         cn: [
21870         {
21871             tag: 'tr',
21872             cn: [
21873             {
21874                 tag: 'td',
21875                 colspan: '7'
21876             }
21877             ]
21878         }
21879         ]
21880     },
21881     
21882     footer : {
21883         tag: 'tfoot',
21884         cn: [
21885         {
21886             tag: 'tr',
21887             cn: [
21888             {
21889                 tag: 'th',
21890                 colspan: '7',
21891                 cls: 'today'
21892             }
21893                     
21894             ]
21895         }
21896         ]
21897     },
21898     
21899     dates:{
21900         en: {
21901             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21902             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21903             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21904             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21905             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21906             today: "Today"
21907         }
21908     },
21909     
21910     modes: [
21911     {
21912         clsName: 'days',
21913         navFnc: 'Month',
21914         navStep: 1
21915     },
21916     {
21917         clsName: 'months',
21918         navFnc: 'FullYear',
21919         navStep: 1
21920     },
21921     {
21922         clsName: 'years',
21923         navFnc: 'FullYear',
21924         navStep: 10
21925     }]
21926 });
21927
21928 Roo.apply(Roo.bootstrap.DateField,  {
21929   
21930     template : {
21931         tag: 'div',
21932         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21933         cn: [
21934         {
21935             tag: 'div',
21936             cls: 'datepicker-days',
21937             cn: [
21938             {
21939                 tag: 'table',
21940                 cls: 'table-condensed',
21941                 cn:[
21942                 Roo.bootstrap.DateField.head,
21943                 {
21944                     tag: 'tbody'
21945                 },
21946                 Roo.bootstrap.DateField.footer
21947                 ]
21948             }
21949             ]
21950         },
21951         {
21952             tag: 'div',
21953             cls: 'datepicker-months',
21954             cn: [
21955             {
21956                 tag: 'table',
21957                 cls: 'table-condensed',
21958                 cn:[
21959                 Roo.bootstrap.DateField.head,
21960                 Roo.bootstrap.DateField.content,
21961                 Roo.bootstrap.DateField.footer
21962                 ]
21963             }
21964             ]
21965         },
21966         {
21967             tag: 'div',
21968             cls: 'datepicker-years',
21969             cn: [
21970             {
21971                 tag: 'table',
21972                 cls: 'table-condensed',
21973                 cn:[
21974                 Roo.bootstrap.DateField.head,
21975                 Roo.bootstrap.DateField.content,
21976                 Roo.bootstrap.DateField.footer
21977                 ]
21978             }
21979             ]
21980         }
21981         ]
21982     }
21983 });
21984
21985  
21986
21987  /*
21988  * - LGPL
21989  *
21990  * TimeField
21991  * 
21992  */
21993
21994 /**
21995  * @class Roo.bootstrap.TimeField
21996  * @extends Roo.bootstrap.Input
21997  * Bootstrap DateField class
21998  * 
21999  * 
22000  * @constructor
22001  * Create a new TimeField
22002  * @param {Object} config The config object
22003  */
22004
22005 Roo.bootstrap.TimeField = function(config){
22006     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22007     this.addEvents({
22008             /**
22009              * @event show
22010              * Fires when this field show.
22011              * @param {Roo.bootstrap.DateField} thisthis
22012              * @param {Mixed} date The date value
22013              */
22014             show : true,
22015             /**
22016              * @event show
22017              * Fires when this field hide.
22018              * @param {Roo.bootstrap.DateField} this
22019              * @param {Mixed} date The date value
22020              */
22021             hide : true,
22022             /**
22023              * @event select
22024              * Fires when select a date.
22025              * @param {Roo.bootstrap.DateField} this
22026              * @param {Mixed} date The date value
22027              */
22028             select : true
22029         });
22030 };
22031
22032 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22033     
22034     /**
22035      * @cfg {String} format
22036      * The default time format string which can be overriden for localization support.  The format must be
22037      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22038      */
22039     format : "H:i",
22040
22041     getAutoCreate : function()
22042     {
22043         this.after = '<i class="fa far fa-clock"></i>';
22044         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22045         
22046          
22047     },
22048     onRender: function(ct, position)
22049     {
22050         
22051         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22052                 
22053         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22054         
22055         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22056         
22057         this.pop = this.picker().select('>.datepicker-time',true).first();
22058         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22059         
22060         this.picker().on('mousedown', this.onMousedown, this);
22061         this.picker().on('click', this.onClick, this);
22062         
22063         this.picker().addClass('datepicker-dropdown');
22064     
22065         this.fillTime();
22066         this.update();
22067             
22068         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22069         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22070         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22071         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22072         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22073         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22074
22075     },
22076     
22077     fireKey: function(e){
22078         if (!this.picker().isVisible()){
22079             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22080                 this.show();
22081             }
22082             return;
22083         }
22084
22085         e.preventDefault();
22086         
22087         switch(e.keyCode){
22088             case 27: // escape
22089                 this.hide();
22090                 break;
22091             case 37: // left
22092             case 39: // right
22093                 this.onTogglePeriod();
22094                 break;
22095             case 38: // up
22096                 this.onIncrementMinutes();
22097                 break;
22098             case 40: // down
22099                 this.onDecrementMinutes();
22100                 break;
22101             case 13: // enter
22102             case 9: // tab
22103                 this.setTime();
22104                 break;
22105         }
22106     },
22107     
22108     onClick: function(e) {
22109         e.stopPropagation();
22110         e.preventDefault();
22111     },
22112     
22113     picker : function()
22114     {
22115         return this.pickerEl;
22116     },
22117     
22118     fillTime: function()
22119     {    
22120         var time = this.pop.select('tbody', true).first();
22121         
22122         time.dom.innerHTML = '';
22123         
22124         time.createChild({
22125             tag: 'tr',
22126             cn: [
22127                 {
22128                     tag: 'td',
22129                     cn: [
22130                         {
22131                             tag: 'a',
22132                             href: '#',
22133                             cls: 'btn',
22134                             cn: [
22135                                 {
22136                                     tag: 'i',
22137                                     cls: 'hours-up fa fas fa-chevron-up'
22138                                 }
22139                             ]
22140                         } 
22141                     ]
22142                 },
22143                 {
22144                     tag: 'td',
22145                     cls: 'separator'
22146                 },
22147                 {
22148                     tag: 'td',
22149                     cn: [
22150                         {
22151                             tag: 'a',
22152                             href: '#',
22153                             cls: 'btn',
22154                             cn: [
22155                                 {
22156                                     tag: 'i',
22157                                     cls: 'minutes-up fa fas fa-chevron-up'
22158                                 }
22159                             ]
22160                         }
22161                     ]
22162                 },
22163                 {
22164                     tag: 'td',
22165                     cls: 'separator'
22166                 }
22167             ]
22168         });
22169         
22170         time.createChild({
22171             tag: 'tr',
22172             cn: [
22173                 {
22174                     tag: 'td',
22175                     cn: [
22176                         {
22177                             tag: 'span',
22178                             cls: 'timepicker-hour',
22179                             html: '00'
22180                         }  
22181                     ]
22182                 },
22183                 {
22184                     tag: 'td',
22185                     cls: 'separator',
22186                     html: ':'
22187                 },
22188                 {
22189                     tag: 'td',
22190                     cn: [
22191                         {
22192                             tag: 'span',
22193                             cls: 'timepicker-minute',
22194                             html: '00'
22195                         }  
22196                     ]
22197                 },
22198                 {
22199                     tag: 'td',
22200                     cls: 'separator'
22201                 },
22202                 {
22203                     tag: 'td',
22204                     cn: [
22205                         {
22206                             tag: 'button',
22207                             type: 'button',
22208                             cls: 'btn btn-primary period',
22209                             html: 'AM'
22210                             
22211                         }
22212                     ]
22213                 }
22214             ]
22215         });
22216         
22217         time.createChild({
22218             tag: 'tr',
22219             cn: [
22220                 {
22221                     tag: 'td',
22222                     cn: [
22223                         {
22224                             tag: 'a',
22225                             href: '#',
22226                             cls: 'btn',
22227                             cn: [
22228                                 {
22229                                     tag: 'span',
22230                                     cls: 'hours-down fa fas fa-chevron-down'
22231                                 }
22232                             ]
22233                         }
22234                     ]
22235                 },
22236                 {
22237                     tag: 'td',
22238                     cls: 'separator'
22239                 },
22240                 {
22241                     tag: 'td',
22242                     cn: [
22243                         {
22244                             tag: 'a',
22245                             href: '#',
22246                             cls: 'btn',
22247                             cn: [
22248                                 {
22249                                     tag: 'span',
22250                                     cls: 'minutes-down fa fas fa-chevron-down'
22251                                 }
22252                             ]
22253                         }
22254                     ]
22255                 },
22256                 {
22257                     tag: 'td',
22258                     cls: 'separator'
22259                 }
22260             ]
22261         });
22262         
22263     },
22264     
22265     update: function()
22266     {
22267         
22268         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22269         
22270         this.fill();
22271     },
22272     
22273     fill: function() 
22274     {
22275         var hours = this.time.getHours();
22276         var minutes = this.time.getMinutes();
22277         var period = 'AM';
22278         
22279         if(hours > 11){
22280             period = 'PM';
22281         }
22282         
22283         if(hours == 0){
22284             hours = 12;
22285         }
22286         
22287         
22288         if(hours > 12){
22289             hours = hours - 12;
22290         }
22291         
22292         if(hours < 10){
22293             hours = '0' + hours;
22294         }
22295         
22296         if(minutes < 10){
22297             minutes = '0' + minutes;
22298         }
22299         
22300         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22301         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22302         this.pop.select('button', true).first().dom.innerHTML = period;
22303         
22304     },
22305     
22306     place: function()
22307     {   
22308         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22309         
22310         var cls = ['bottom'];
22311         
22312         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22313             cls.pop();
22314             cls.push('top');
22315         }
22316         
22317         cls.push('right');
22318         
22319         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22320             cls.pop();
22321             cls.push('left');
22322         }
22323         //this.picker().setXY(20000,20000);
22324         this.picker().addClass(cls.join('-'));
22325         
22326         var _this = this;
22327         
22328         Roo.each(cls, function(c){
22329             if(c == 'bottom'){
22330                 (function() {
22331                  //  
22332                 }).defer(200);
22333                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22334                 //_this.picker().setTop(_this.inputEl().getHeight());
22335                 return;
22336             }
22337             if(c == 'top'){
22338                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22339                 
22340                 //_this.picker().setTop(0 - _this.picker().getHeight());
22341                 return;
22342             }
22343             /*
22344             if(c == 'left'){
22345                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22346                 return;
22347             }
22348             if(c == 'right'){
22349                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22350                 return;
22351             }
22352             */
22353         });
22354         
22355     },
22356   
22357     onFocus : function()
22358     {
22359         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22360         this.show();
22361     },
22362     
22363     onBlur : function()
22364     {
22365         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22366         this.hide();
22367     },
22368     
22369     show : function()
22370     {
22371         this.picker().show();
22372         this.pop.show();
22373         this.update();
22374         this.place();
22375         
22376         this.fireEvent('show', this, this.date);
22377     },
22378     
22379     hide : function()
22380     {
22381         this.picker().hide();
22382         this.pop.hide();
22383         
22384         this.fireEvent('hide', this, this.date);
22385     },
22386     
22387     setTime : function()
22388     {
22389         this.hide();
22390         this.setValue(this.time.format(this.format));
22391         
22392         this.fireEvent('select', this, this.date);
22393         
22394         
22395     },
22396     
22397     onMousedown: function(e){
22398         e.stopPropagation();
22399         e.preventDefault();
22400     },
22401     
22402     onIncrementHours: function()
22403     {
22404         Roo.log('onIncrementHours');
22405         this.time = this.time.add(Date.HOUR, 1);
22406         this.update();
22407         
22408     },
22409     
22410     onDecrementHours: function()
22411     {
22412         Roo.log('onDecrementHours');
22413         this.time = this.time.add(Date.HOUR, -1);
22414         this.update();
22415     },
22416     
22417     onIncrementMinutes: function()
22418     {
22419         Roo.log('onIncrementMinutes');
22420         this.time = this.time.add(Date.MINUTE, 1);
22421         this.update();
22422     },
22423     
22424     onDecrementMinutes: function()
22425     {
22426         Roo.log('onDecrementMinutes');
22427         this.time = this.time.add(Date.MINUTE, -1);
22428         this.update();
22429     },
22430     
22431     onTogglePeriod: function()
22432     {
22433         Roo.log('onTogglePeriod');
22434         this.time = this.time.add(Date.HOUR, 12);
22435         this.update();
22436     }
22437     
22438    
22439 });
22440  
22441
22442 Roo.apply(Roo.bootstrap.TimeField,  {
22443   
22444     template : {
22445         tag: 'div',
22446         cls: 'datepicker dropdown-menu',
22447         cn: [
22448             {
22449                 tag: 'div',
22450                 cls: 'datepicker-time',
22451                 cn: [
22452                 {
22453                     tag: 'table',
22454                     cls: 'table-condensed',
22455                     cn:[
22456                         {
22457                             tag: 'tbody',
22458                             cn: [
22459                                 {
22460                                     tag: 'tr',
22461                                     cn: [
22462                                     {
22463                                         tag: 'td',
22464                                         colspan: '7'
22465                                     }
22466                                     ]
22467                                 }
22468                             ]
22469                         },
22470                         {
22471                             tag: 'tfoot',
22472                             cn: [
22473                                 {
22474                                     tag: 'tr',
22475                                     cn: [
22476                                     {
22477                                         tag: 'th',
22478                                         colspan: '7',
22479                                         cls: '',
22480                                         cn: [
22481                                             {
22482                                                 tag: 'button',
22483                                                 cls: 'btn btn-info ok',
22484                                                 html: 'OK'
22485                                             }
22486                                         ]
22487                                     }
22488                     
22489                                     ]
22490                                 }
22491                             ]
22492                         }
22493                     ]
22494                 }
22495                 ]
22496             }
22497         ]
22498     }
22499 });
22500
22501  
22502
22503  /*
22504  * - LGPL
22505  *
22506  * MonthField
22507  * 
22508  */
22509
22510 /**
22511  * @class Roo.bootstrap.MonthField
22512  * @extends Roo.bootstrap.Input
22513  * Bootstrap MonthField class
22514  * 
22515  * @cfg {String} language default en
22516  * 
22517  * @constructor
22518  * Create a new MonthField
22519  * @param {Object} config The config object
22520  */
22521
22522 Roo.bootstrap.MonthField = function(config){
22523     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22524     
22525     this.addEvents({
22526         /**
22527          * @event show
22528          * Fires when this field show.
22529          * @param {Roo.bootstrap.MonthField} this
22530          * @param {Mixed} date The date value
22531          */
22532         show : true,
22533         /**
22534          * @event show
22535          * Fires when this field hide.
22536          * @param {Roo.bootstrap.MonthField} this
22537          * @param {Mixed} date The date value
22538          */
22539         hide : true,
22540         /**
22541          * @event select
22542          * Fires when select a date.
22543          * @param {Roo.bootstrap.MonthField} this
22544          * @param {String} oldvalue The old value
22545          * @param {String} newvalue The new value
22546          */
22547         select : true
22548     });
22549 };
22550
22551 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22552     
22553     onRender: function(ct, position)
22554     {
22555         
22556         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22557         
22558         this.language = this.language || 'en';
22559         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22560         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22561         
22562         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22563         this.isInline = false;
22564         this.isInput = true;
22565         this.component = this.el.select('.add-on', true).first() || false;
22566         this.component = (this.component && this.component.length === 0) ? false : this.component;
22567         this.hasInput = this.component && this.inputEL().length;
22568         
22569         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22570         
22571         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22572         
22573         this.picker().on('mousedown', this.onMousedown, this);
22574         this.picker().on('click', this.onClick, this);
22575         
22576         this.picker().addClass('datepicker-dropdown');
22577         
22578         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22579             v.setStyle('width', '189px');
22580         });
22581         
22582         this.fillMonths();
22583         
22584         this.update();
22585         
22586         if(this.isInline) {
22587             this.show();
22588         }
22589         
22590     },
22591     
22592     setValue: function(v, suppressEvent)
22593     {   
22594         var o = this.getValue();
22595         
22596         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22597         
22598         this.update();
22599
22600         if(suppressEvent !== true){
22601             this.fireEvent('select', this, o, v);
22602         }
22603         
22604     },
22605     
22606     getValue: function()
22607     {
22608         return this.value;
22609     },
22610     
22611     onClick: function(e) 
22612     {
22613         e.stopPropagation();
22614         e.preventDefault();
22615         
22616         var target = e.getTarget();
22617         
22618         if(target.nodeName.toLowerCase() === 'i'){
22619             target = Roo.get(target).dom.parentNode;
22620         }
22621         
22622         var nodeName = target.nodeName;
22623         var className = target.className;
22624         var html = target.innerHTML;
22625         
22626         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22627             return;
22628         }
22629         
22630         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22631         
22632         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22633         
22634         this.hide();
22635                         
22636     },
22637     
22638     picker : function()
22639     {
22640         return this.pickerEl;
22641     },
22642     
22643     fillMonths: function()
22644     {    
22645         var i = 0;
22646         var months = this.picker().select('>.datepicker-months td', true).first();
22647         
22648         months.dom.innerHTML = '';
22649         
22650         while (i < 12) {
22651             var month = {
22652                 tag: 'span',
22653                 cls: 'month',
22654                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22655             };
22656             
22657             months.createChild(month);
22658         }
22659         
22660     },
22661     
22662     update: function()
22663     {
22664         var _this = this;
22665         
22666         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22667             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22668         }
22669         
22670         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22671             e.removeClass('active');
22672             
22673             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22674                 e.addClass('active');
22675             }
22676         })
22677     },
22678     
22679     place: function()
22680     {
22681         if(this.isInline) {
22682             return;
22683         }
22684         
22685         this.picker().removeClass(['bottom', 'top']);
22686         
22687         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22688             /*
22689              * place to the top of element!
22690              *
22691              */
22692             
22693             this.picker().addClass('top');
22694             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22695             
22696             return;
22697         }
22698         
22699         this.picker().addClass('bottom');
22700         
22701         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22702     },
22703     
22704     onFocus : function()
22705     {
22706         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22707         this.show();
22708     },
22709     
22710     onBlur : function()
22711     {
22712         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22713         
22714         var d = this.inputEl().getValue();
22715         
22716         this.setValue(d);
22717                 
22718         this.hide();
22719     },
22720     
22721     show : function()
22722     {
22723         this.picker().show();
22724         this.picker().select('>.datepicker-months', true).first().show();
22725         this.update();
22726         this.place();
22727         
22728         this.fireEvent('show', this, this.date);
22729     },
22730     
22731     hide : function()
22732     {
22733         if(this.isInline) {
22734             return;
22735         }
22736         this.picker().hide();
22737         this.fireEvent('hide', this, this.date);
22738         
22739     },
22740     
22741     onMousedown: function(e)
22742     {
22743         e.stopPropagation();
22744         e.preventDefault();
22745     },
22746     
22747     keyup: function(e)
22748     {
22749         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22750         this.update();
22751     },
22752
22753     fireKey: function(e)
22754     {
22755         if (!this.picker().isVisible()){
22756             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22757                 this.show();
22758             }
22759             return;
22760         }
22761         
22762         var dir;
22763         
22764         switch(e.keyCode){
22765             case 27: // escape
22766                 this.hide();
22767                 e.preventDefault();
22768                 break;
22769             case 37: // left
22770             case 39: // right
22771                 dir = e.keyCode == 37 ? -1 : 1;
22772                 
22773                 this.vIndex = this.vIndex + dir;
22774                 
22775                 if(this.vIndex < 0){
22776                     this.vIndex = 0;
22777                 }
22778                 
22779                 if(this.vIndex > 11){
22780                     this.vIndex = 11;
22781                 }
22782                 
22783                 if(isNaN(this.vIndex)){
22784                     this.vIndex = 0;
22785                 }
22786                 
22787                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22788                 
22789                 break;
22790             case 38: // up
22791             case 40: // down
22792                 
22793                 dir = e.keyCode == 38 ? -1 : 1;
22794                 
22795                 this.vIndex = this.vIndex + dir * 4;
22796                 
22797                 if(this.vIndex < 0){
22798                     this.vIndex = 0;
22799                 }
22800                 
22801                 if(this.vIndex > 11){
22802                     this.vIndex = 11;
22803                 }
22804                 
22805                 if(isNaN(this.vIndex)){
22806                     this.vIndex = 0;
22807                 }
22808                 
22809                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22810                 break;
22811                 
22812             case 13: // enter
22813                 
22814                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22815                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22816                 }
22817                 
22818                 this.hide();
22819                 e.preventDefault();
22820                 break;
22821             case 9: // tab
22822                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22823                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22824                 }
22825                 this.hide();
22826                 break;
22827             case 16: // shift
22828             case 17: // ctrl
22829             case 18: // alt
22830                 break;
22831             default :
22832                 this.hide();
22833                 
22834         }
22835     },
22836     
22837     remove: function() 
22838     {
22839         this.picker().remove();
22840     }
22841    
22842 });
22843
22844 Roo.apply(Roo.bootstrap.MonthField,  {
22845     
22846     content : {
22847         tag: 'tbody',
22848         cn: [
22849         {
22850             tag: 'tr',
22851             cn: [
22852             {
22853                 tag: 'td',
22854                 colspan: '7'
22855             }
22856             ]
22857         }
22858         ]
22859     },
22860     
22861     dates:{
22862         en: {
22863             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22864             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22865         }
22866     }
22867 });
22868
22869 Roo.apply(Roo.bootstrap.MonthField,  {
22870   
22871     template : {
22872         tag: 'div',
22873         cls: 'datepicker dropdown-menu roo-dynamic',
22874         cn: [
22875             {
22876                 tag: 'div',
22877                 cls: 'datepicker-months',
22878                 cn: [
22879                 {
22880                     tag: 'table',
22881                     cls: 'table-condensed',
22882                     cn:[
22883                         Roo.bootstrap.DateField.content
22884                     ]
22885                 }
22886                 ]
22887             }
22888         ]
22889     }
22890 });
22891
22892  
22893
22894  
22895  /*
22896  * - LGPL
22897  *
22898  * CheckBox
22899  * 
22900  */
22901
22902 /**
22903  * @class Roo.bootstrap.CheckBox
22904  * @extends Roo.bootstrap.Input
22905  * Bootstrap CheckBox class
22906  * 
22907  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22908  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22909  * @cfg {String} boxLabel The text that appears beside the checkbox
22910  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22911  * @cfg {Boolean} checked initnal the element
22912  * @cfg {Boolean} inline inline the element (default false)
22913  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22914  * @cfg {String} tooltip label tooltip
22915  * 
22916  * @constructor
22917  * Create a new CheckBox
22918  * @param {Object} config The config object
22919  */
22920
22921 Roo.bootstrap.CheckBox = function(config){
22922     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22923    
22924     this.addEvents({
22925         /**
22926         * @event check
22927         * Fires when the element is checked or unchecked.
22928         * @param {Roo.bootstrap.CheckBox} this This input
22929         * @param {Boolean} checked The new checked value
22930         */
22931        check : true,
22932        /**
22933         * @event click
22934         * Fires when the element is click.
22935         * @param {Roo.bootstrap.CheckBox} this This input
22936         */
22937        click : true
22938     });
22939     
22940 };
22941
22942 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22943   
22944     inputType: 'checkbox',
22945     inputValue: 1,
22946     valueOff: 0,
22947     boxLabel: false,
22948     checked: false,
22949     weight : false,
22950     inline: false,
22951     tooltip : '',
22952     
22953     // checkbox success does not make any sense really.. 
22954     invalidClass : "",
22955     validClass : "",
22956     
22957     
22958     getAutoCreate : function()
22959     {
22960         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22961         
22962         var id = Roo.id();
22963         
22964         var cfg = {};
22965         
22966         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22967         
22968         if(this.inline){
22969             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22970         }
22971         
22972         var input =  {
22973             tag: 'input',
22974             id : id,
22975             type : this.inputType,
22976             value : this.inputValue,
22977             cls : 'roo-' + this.inputType, //'form-box',
22978             placeholder : this.placeholder || ''
22979             
22980         };
22981         
22982         if(this.inputType != 'radio'){
22983             var hidden =  {
22984                 tag: 'input',
22985                 type : 'hidden',
22986                 cls : 'roo-hidden-value',
22987                 value : this.checked ? this.inputValue : this.valueOff
22988             };
22989         }
22990         
22991             
22992         if (this.weight) { // Validity check?
22993             cfg.cls += " " + this.inputType + "-" + this.weight;
22994         }
22995         
22996         if (this.disabled) {
22997             input.disabled=true;
22998         }
22999         
23000         if(this.checked){
23001             input.checked = this.checked;
23002         }
23003         
23004         if (this.name) {
23005             
23006             input.name = this.name;
23007             
23008             if(this.inputType != 'radio'){
23009                 hidden.name = this.name;
23010                 input.name = '_hidden_' + this.name;
23011             }
23012         }
23013         
23014         if (this.size) {
23015             input.cls += ' input-' + this.size;
23016         }
23017         
23018         var settings=this;
23019         
23020         ['xs','sm','md','lg'].map(function(size){
23021             if (settings[size]) {
23022                 cfg.cls += ' col-' + size + '-' + settings[size];
23023             }
23024         });
23025         
23026         var inputblock = input;
23027          
23028         if (this.before || this.after) {
23029             
23030             inputblock = {
23031                 cls : 'input-group',
23032                 cn :  [] 
23033             };
23034             
23035             if (this.before) {
23036                 inputblock.cn.push({
23037                     tag :'span',
23038                     cls : 'input-group-addon',
23039                     html : this.before
23040                 });
23041             }
23042             
23043             inputblock.cn.push(input);
23044             
23045             if(this.inputType != 'radio'){
23046                 inputblock.cn.push(hidden);
23047             }
23048             
23049             if (this.after) {
23050                 inputblock.cn.push({
23051                     tag :'span',
23052                     cls : 'input-group-addon',
23053                     html : this.after
23054                 });
23055             }
23056             
23057         }
23058         var boxLabelCfg = false;
23059         
23060         if(this.boxLabel){
23061            
23062             boxLabelCfg = {
23063                 tag: 'label',
23064                 //'for': id, // box label is handled by onclick - so no for...
23065                 cls: 'box-label',
23066                 html: this.boxLabel
23067             };
23068             if(this.tooltip){
23069                 boxLabelCfg.tooltip = this.tooltip;
23070             }
23071              
23072         }
23073         
23074         
23075         if (align ==='left' && this.fieldLabel.length) {
23076 //                Roo.log("left and has label");
23077             cfg.cn = [
23078                 {
23079                     tag: 'label',
23080                     'for' :  id,
23081                     cls : 'control-label',
23082                     html : this.fieldLabel
23083                 },
23084                 {
23085                     cls : "", 
23086                     cn: [
23087                         inputblock
23088                     ]
23089                 }
23090             ];
23091             
23092             if (boxLabelCfg) {
23093                 cfg.cn[1].cn.push(boxLabelCfg);
23094             }
23095             
23096             if(this.labelWidth > 12){
23097                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23098             }
23099             
23100             if(this.labelWidth < 13 && this.labelmd == 0){
23101                 this.labelmd = this.labelWidth;
23102             }
23103             
23104             if(this.labellg > 0){
23105                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23106                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23107             }
23108             
23109             if(this.labelmd > 0){
23110                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23111                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23112             }
23113             
23114             if(this.labelsm > 0){
23115                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23116                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23117             }
23118             
23119             if(this.labelxs > 0){
23120                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23121                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23122             }
23123             
23124         } else if ( this.fieldLabel.length) {
23125 //                Roo.log(" label");
23126                 cfg.cn = [
23127                    
23128                     {
23129                         tag: this.boxLabel ? 'span' : 'label',
23130                         'for': id,
23131                         cls: 'control-label box-input-label',
23132                         //cls : 'input-group-addon',
23133                         html : this.fieldLabel
23134                     },
23135                     
23136                     inputblock
23137                     
23138                 ];
23139                 if (boxLabelCfg) {
23140                     cfg.cn.push(boxLabelCfg);
23141                 }
23142
23143         } else {
23144             
23145 //                Roo.log(" no label && no align");
23146                 cfg.cn = [  inputblock ] ;
23147                 if (boxLabelCfg) {
23148                     cfg.cn.push(boxLabelCfg);
23149                 }
23150
23151                 
23152         }
23153         
23154        
23155         
23156         if(this.inputType != 'radio'){
23157             cfg.cn.push(hidden);
23158         }
23159         
23160         return cfg;
23161         
23162     },
23163     
23164     /**
23165      * return the real input element.
23166      */
23167     inputEl: function ()
23168     {
23169         return this.el.select('input.roo-' + this.inputType,true).first();
23170     },
23171     hiddenEl: function ()
23172     {
23173         return this.el.select('input.roo-hidden-value',true).first();
23174     },
23175     
23176     labelEl: function()
23177     {
23178         return this.el.select('label.control-label',true).first();
23179     },
23180     /* depricated... */
23181     
23182     label: function()
23183     {
23184         return this.labelEl();
23185     },
23186     
23187     boxLabelEl: function()
23188     {
23189         return this.el.select('label.box-label',true).first();
23190     },
23191     
23192     initEvents : function()
23193     {
23194 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23195         
23196         this.inputEl().on('click', this.onClick,  this);
23197         
23198         if (this.boxLabel) { 
23199             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23200         }
23201         
23202         this.startValue = this.getValue();
23203         
23204         if(this.groupId){
23205             Roo.bootstrap.CheckBox.register(this);
23206         }
23207     },
23208     
23209     onClick : function(e)
23210     {   
23211         if(this.fireEvent('click', this, e) !== false){
23212             this.setChecked(!this.checked);
23213         }
23214         
23215     },
23216     
23217     setChecked : function(state,suppressEvent)
23218     {
23219         this.startValue = this.getValue();
23220
23221         if(this.inputType == 'radio'){
23222             
23223             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23224                 e.dom.checked = false;
23225             });
23226             
23227             this.inputEl().dom.checked = true;
23228             
23229             this.inputEl().dom.value = this.inputValue;
23230             
23231             if(suppressEvent !== true){
23232                 this.fireEvent('check', this, true);
23233             }
23234             
23235             this.validate();
23236             
23237             return;
23238         }
23239         
23240         this.checked = state;
23241         
23242         this.inputEl().dom.checked = state;
23243         
23244         
23245         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23246         
23247         if(suppressEvent !== true){
23248             this.fireEvent('check', this, state);
23249         }
23250         
23251         this.validate();
23252     },
23253     
23254     getValue : function()
23255     {
23256         if(this.inputType == 'radio'){
23257             return this.getGroupValue();
23258         }
23259         
23260         return this.hiddenEl().dom.value;
23261         
23262     },
23263     
23264     getGroupValue : function()
23265     {
23266         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23267             return '';
23268         }
23269         
23270         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23271     },
23272     
23273     setValue : function(v,suppressEvent)
23274     {
23275         if(this.inputType == 'radio'){
23276             this.setGroupValue(v, suppressEvent);
23277             return;
23278         }
23279         
23280         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23281         
23282         this.validate();
23283     },
23284     
23285     setGroupValue : function(v, suppressEvent)
23286     {
23287         this.startValue = this.getValue();
23288         
23289         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23290             e.dom.checked = false;
23291             
23292             if(e.dom.value == v){
23293                 e.dom.checked = true;
23294             }
23295         });
23296         
23297         if(suppressEvent !== true){
23298             this.fireEvent('check', this, true);
23299         }
23300
23301         this.validate();
23302         
23303         return;
23304     },
23305     
23306     validate : function()
23307     {
23308         if(this.getVisibilityEl().hasClass('hidden')){
23309             return true;
23310         }
23311         
23312         if(
23313                 this.disabled || 
23314                 (this.inputType == 'radio' && this.validateRadio()) ||
23315                 (this.inputType == 'checkbox' && this.validateCheckbox())
23316         ){
23317             this.markValid();
23318             return true;
23319         }
23320         
23321         this.markInvalid();
23322         return false;
23323     },
23324     
23325     validateRadio : function()
23326     {
23327         if(this.getVisibilityEl().hasClass('hidden')){
23328             return true;
23329         }
23330         
23331         if(this.allowBlank){
23332             return true;
23333         }
23334         
23335         var valid = false;
23336         
23337         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23338             if(!e.dom.checked){
23339                 return;
23340             }
23341             
23342             valid = true;
23343             
23344             return false;
23345         });
23346         
23347         return valid;
23348     },
23349     
23350     validateCheckbox : function()
23351     {
23352         if(!this.groupId){
23353             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23354             //return (this.getValue() == this.inputValue) ? true : false;
23355         }
23356         
23357         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23358         
23359         if(!group){
23360             return false;
23361         }
23362         
23363         var r = false;
23364         
23365         for(var i in group){
23366             if(group[i].el.isVisible(true)){
23367                 r = false;
23368                 break;
23369             }
23370             
23371             r = true;
23372         }
23373         
23374         for(var i in group){
23375             if(r){
23376                 break;
23377             }
23378             
23379             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23380         }
23381         
23382         return r;
23383     },
23384     
23385     /**
23386      * Mark this field as valid
23387      */
23388     markValid : function()
23389     {
23390         var _this = this;
23391         
23392         this.fireEvent('valid', this);
23393         
23394         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23395         
23396         if(this.groupId){
23397             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23398         }
23399         
23400         if(label){
23401             label.markValid();
23402         }
23403
23404         if(this.inputType == 'radio'){
23405             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23406                 var fg = e.findParent('.form-group', false, true);
23407                 if (Roo.bootstrap.version == 3) {
23408                     fg.removeClass([_this.invalidClass, _this.validClass]);
23409                     fg.addClass(_this.validClass);
23410                 } else {
23411                     fg.removeClass(['is-valid', 'is-invalid']);
23412                     fg.addClass('is-valid');
23413                 }
23414             });
23415             
23416             return;
23417         }
23418
23419         if(!this.groupId){
23420             var fg = this.el.findParent('.form-group', false, true);
23421             if (Roo.bootstrap.version == 3) {
23422                 fg.removeClass([this.invalidClass, this.validClass]);
23423                 fg.addClass(this.validClass);
23424             } else {
23425                 fg.removeClass(['is-valid', 'is-invalid']);
23426                 fg.addClass('is-valid');
23427             }
23428             return;
23429         }
23430         
23431         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23432         
23433         if(!group){
23434             return;
23435         }
23436         
23437         for(var i in group){
23438             var fg = group[i].el.findParent('.form-group', false, true);
23439             if (Roo.bootstrap.version == 3) {
23440                 fg.removeClass([this.invalidClass, this.validClass]);
23441                 fg.addClass(this.validClass);
23442             } else {
23443                 fg.removeClass(['is-valid', 'is-invalid']);
23444                 fg.addClass('is-valid');
23445             }
23446         }
23447     },
23448     
23449      /**
23450      * Mark this field as invalid
23451      * @param {String} msg The validation message
23452      */
23453     markInvalid : function(msg)
23454     {
23455         if(this.allowBlank){
23456             return;
23457         }
23458         
23459         var _this = this;
23460         
23461         this.fireEvent('invalid', this, msg);
23462         
23463         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23464         
23465         if(this.groupId){
23466             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23467         }
23468         
23469         if(label){
23470             label.markInvalid();
23471         }
23472             
23473         if(this.inputType == 'radio'){
23474             
23475             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23476                 var fg = e.findParent('.form-group', false, true);
23477                 if (Roo.bootstrap.version == 3) {
23478                     fg.removeClass([_this.invalidClass, _this.validClass]);
23479                     fg.addClass(_this.invalidClass);
23480                 } else {
23481                     fg.removeClass(['is-invalid', 'is-valid']);
23482                     fg.addClass('is-invalid');
23483                 }
23484             });
23485             
23486             return;
23487         }
23488         
23489         if(!this.groupId){
23490             var fg = this.el.findParent('.form-group', false, true);
23491             if (Roo.bootstrap.version == 3) {
23492                 fg.removeClass([_this.invalidClass, _this.validClass]);
23493                 fg.addClass(_this.invalidClass);
23494             } else {
23495                 fg.removeClass(['is-invalid', 'is-valid']);
23496                 fg.addClass('is-invalid');
23497             }
23498             return;
23499         }
23500         
23501         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23502         
23503         if(!group){
23504             return;
23505         }
23506         
23507         for(var i in group){
23508             var fg = group[i].el.findParent('.form-group', false, true);
23509             if (Roo.bootstrap.version == 3) {
23510                 fg.removeClass([_this.invalidClass, _this.validClass]);
23511                 fg.addClass(_this.invalidClass);
23512             } else {
23513                 fg.removeClass(['is-invalid', 'is-valid']);
23514                 fg.addClass('is-invalid');
23515             }
23516         }
23517         
23518     },
23519     
23520     clearInvalid : function()
23521     {
23522         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23523         
23524         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23525         
23526         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23527         
23528         if (label && label.iconEl) {
23529             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23530             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23531         }
23532     },
23533     
23534     disable : function()
23535     {
23536         if(this.inputType != 'radio'){
23537             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23538             return;
23539         }
23540         
23541         var _this = this;
23542         
23543         if(this.rendered){
23544             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23545                 _this.getActionEl().addClass(this.disabledClass);
23546                 e.dom.disabled = true;
23547             });
23548         }
23549         
23550         this.disabled = true;
23551         this.fireEvent("disable", this);
23552         return this;
23553     },
23554
23555     enable : function()
23556     {
23557         if(this.inputType != 'radio'){
23558             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23559             return;
23560         }
23561         
23562         var _this = this;
23563         
23564         if(this.rendered){
23565             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23566                 _this.getActionEl().removeClass(this.disabledClass);
23567                 e.dom.disabled = false;
23568             });
23569         }
23570         
23571         this.disabled = false;
23572         this.fireEvent("enable", this);
23573         return this;
23574     },
23575     
23576     setBoxLabel : function(v)
23577     {
23578         this.boxLabel = v;
23579         
23580         if(this.rendered){
23581             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23582         }
23583     }
23584
23585 });
23586
23587 Roo.apply(Roo.bootstrap.CheckBox, {
23588     
23589     groups: {},
23590     
23591      /**
23592     * register a CheckBox Group
23593     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23594     */
23595     register : function(checkbox)
23596     {
23597         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23598             this.groups[checkbox.groupId] = {};
23599         }
23600         
23601         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23602             return;
23603         }
23604         
23605         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23606         
23607     },
23608     /**
23609     * fetch a CheckBox Group based on the group ID
23610     * @param {string} the group ID
23611     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23612     */
23613     get: function(groupId) {
23614         if (typeof(this.groups[groupId]) == 'undefined') {
23615             return false;
23616         }
23617         
23618         return this.groups[groupId] ;
23619     }
23620     
23621     
23622 });
23623 /*
23624  * - LGPL
23625  *
23626  * RadioItem
23627  * 
23628  */
23629
23630 /**
23631  * @class Roo.bootstrap.Radio
23632  * @extends Roo.bootstrap.Component
23633  * Bootstrap Radio class
23634  * @cfg {String} boxLabel - the label associated
23635  * @cfg {String} value - the value of radio
23636  * 
23637  * @constructor
23638  * Create a new Radio
23639  * @param {Object} config The config object
23640  */
23641 Roo.bootstrap.Radio = function(config){
23642     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23643     
23644 };
23645
23646 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23647     
23648     boxLabel : '',
23649     
23650     value : '',
23651     
23652     getAutoCreate : function()
23653     {
23654         var cfg = {
23655             tag : 'div',
23656             cls : 'form-group radio',
23657             cn : [
23658                 {
23659                     tag : 'label',
23660                     cls : 'box-label',
23661                     html : this.boxLabel
23662                 }
23663             ]
23664         };
23665         
23666         return cfg;
23667     },
23668     
23669     initEvents : function() 
23670     {
23671         this.parent().register(this);
23672         
23673         this.el.on('click', this.onClick, this);
23674         
23675     },
23676     
23677     onClick : function(e)
23678     {
23679         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23680             this.setChecked(true);
23681         }
23682     },
23683     
23684     setChecked : function(state, suppressEvent)
23685     {
23686         this.parent().setValue(this.value, suppressEvent);
23687         
23688     },
23689     
23690     setBoxLabel : function(v)
23691     {
23692         this.boxLabel = v;
23693         
23694         if(this.rendered){
23695             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23696         }
23697     }
23698     
23699 });
23700  
23701
23702  /*
23703  * - LGPL
23704  *
23705  * Input
23706  * 
23707  */
23708
23709 /**
23710  * @class Roo.bootstrap.SecurePass
23711  * @extends Roo.bootstrap.Input
23712  * Bootstrap SecurePass class
23713  *
23714  * 
23715  * @constructor
23716  * Create a new SecurePass
23717  * @param {Object} config The config object
23718  */
23719  
23720 Roo.bootstrap.SecurePass = function (config) {
23721     // these go here, so the translation tool can replace them..
23722     this.errors = {
23723         PwdEmpty: "Please type a password, and then retype it to confirm.",
23724         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23725         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23726         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23727         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23728         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23729         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23730         TooWeak: "Your password is Too Weak."
23731     },
23732     this.meterLabel = "Password strength:";
23733     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23734     this.meterClass = [
23735         "roo-password-meter-tooweak", 
23736         "roo-password-meter-weak", 
23737         "roo-password-meter-medium", 
23738         "roo-password-meter-strong", 
23739         "roo-password-meter-grey"
23740     ];
23741     
23742     this.errors = {};
23743     
23744     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23745 }
23746
23747 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23748     /**
23749      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23750      * {
23751      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23752      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23753      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23754      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23755      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23756      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23757      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23758      * })
23759      */
23760     // private
23761     
23762     meterWidth: 300,
23763     errorMsg :'',    
23764     errors: false,
23765     imageRoot: '/',
23766     /**
23767      * @cfg {String/Object} Label for the strength meter (defaults to
23768      * 'Password strength:')
23769      */
23770     // private
23771     meterLabel: '',
23772     /**
23773      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23774      * ['Weak', 'Medium', 'Strong'])
23775      */
23776     // private    
23777     pwdStrengths: false,    
23778     // private
23779     strength: 0,
23780     // private
23781     _lastPwd: null,
23782     // private
23783     kCapitalLetter: 0,
23784     kSmallLetter: 1,
23785     kDigit: 2,
23786     kPunctuation: 3,
23787     
23788     insecure: false,
23789     // private
23790     initEvents: function ()
23791     {
23792         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23793
23794         if (this.el.is('input[type=password]') && Roo.isSafari) {
23795             this.el.on('keydown', this.SafariOnKeyDown, this);
23796         }
23797
23798         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23799     },
23800     // private
23801     onRender: function (ct, position)
23802     {
23803         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23804         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23805         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23806
23807         this.trigger.createChild({
23808                    cn: [
23809                     {
23810                     //id: 'PwdMeter',
23811                     tag: 'div',
23812                     cls: 'roo-password-meter-grey col-xs-12',
23813                     style: {
23814                         //width: 0,
23815                         //width: this.meterWidth + 'px'                                                
23816                         }
23817                     },
23818                     {                            
23819                          cls: 'roo-password-meter-text'                          
23820                     }
23821                 ]            
23822         });
23823
23824          
23825         if (this.hideTrigger) {
23826             this.trigger.setDisplayed(false);
23827         }
23828         this.setSize(this.width || '', this.height || '');
23829     },
23830     // private
23831     onDestroy: function ()
23832     {
23833         if (this.trigger) {
23834             this.trigger.removeAllListeners();
23835             this.trigger.remove();
23836         }
23837         if (this.wrap) {
23838             this.wrap.remove();
23839         }
23840         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23841     },
23842     // private
23843     checkStrength: function ()
23844     {
23845         var pwd = this.inputEl().getValue();
23846         if (pwd == this._lastPwd) {
23847             return;
23848         }
23849
23850         var strength;
23851         if (this.ClientSideStrongPassword(pwd)) {
23852             strength = 3;
23853         } else if (this.ClientSideMediumPassword(pwd)) {
23854             strength = 2;
23855         } else if (this.ClientSideWeakPassword(pwd)) {
23856             strength = 1;
23857         } else {
23858             strength = 0;
23859         }
23860         
23861         Roo.log('strength1: ' + strength);
23862         
23863         //var pm = this.trigger.child('div/div/div').dom;
23864         var pm = this.trigger.child('div/div');
23865         pm.removeClass(this.meterClass);
23866         pm.addClass(this.meterClass[strength]);
23867                 
23868         
23869         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23870                 
23871         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23872         
23873         this._lastPwd = pwd;
23874     },
23875     reset: function ()
23876     {
23877         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23878         
23879         this._lastPwd = '';
23880         
23881         var pm = this.trigger.child('div/div');
23882         pm.removeClass(this.meterClass);
23883         pm.addClass('roo-password-meter-grey');        
23884         
23885         
23886         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23887         
23888         pt.innerHTML = '';
23889         this.inputEl().dom.type='password';
23890     },
23891     // private
23892     validateValue: function (value)
23893     {
23894         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23895             return false;
23896         }
23897         if (value.length == 0) {
23898             if (this.allowBlank) {
23899                 this.clearInvalid();
23900                 return true;
23901             }
23902
23903             this.markInvalid(this.errors.PwdEmpty);
23904             this.errorMsg = this.errors.PwdEmpty;
23905             return false;
23906         }
23907         
23908         if(this.insecure){
23909             return true;
23910         }
23911         
23912         if (!value.match(/[\x21-\x7e]+/)) {
23913             this.markInvalid(this.errors.PwdBadChar);
23914             this.errorMsg = this.errors.PwdBadChar;
23915             return false;
23916         }
23917         if (value.length < 6) {
23918             this.markInvalid(this.errors.PwdShort);
23919             this.errorMsg = this.errors.PwdShort;
23920             return false;
23921         }
23922         if (value.length > 16) {
23923             this.markInvalid(this.errors.PwdLong);
23924             this.errorMsg = this.errors.PwdLong;
23925             return false;
23926         }
23927         var strength;
23928         if (this.ClientSideStrongPassword(value)) {
23929             strength = 3;
23930         } else if (this.ClientSideMediumPassword(value)) {
23931             strength = 2;
23932         } else if (this.ClientSideWeakPassword(value)) {
23933             strength = 1;
23934         } else {
23935             strength = 0;
23936         }
23937
23938         
23939         if (strength < 2) {
23940             //this.markInvalid(this.errors.TooWeak);
23941             this.errorMsg = this.errors.TooWeak;
23942             //return false;
23943         }
23944         
23945         
23946         console.log('strength2: ' + strength);
23947         
23948         //var pm = this.trigger.child('div/div/div').dom;
23949         
23950         var pm = this.trigger.child('div/div');
23951         pm.removeClass(this.meterClass);
23952         pm.addClass(this.meterClass[strength]);
23953                 
23954         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23955                 
23956         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23957         
23958         this.errorMsg = ''; 
23959         return true;
23960     },
23961     // private
23962     CharacterSetChecks: function (type)
23963     {
23964         this.type = type;
23965         this.fResult = false;
23966     },
23967     // private
23968     isctype: function (character, type)
23969     {
23970         switch (type) {  
23971             case this.kCapitalLetter:
23972                 if (character >= 'A' && character <= 'Z') {
23973                     return true;
23974                 }
23975                 break;
23976             
23977             case this.kSmallLetter:
23978                 if (character >= 'a' && character <= 'z') {
23979                     return true;
23980                 }
23981                 break;
23982             
23983             case this.kDigit:
23984                 if (character >= '0' && character <= '9') {
23985                     return true;
23986                 }
23987                 break;
23988             
23989             case this.kPunctuation:
23990                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23991                     return true;
23992                 }
23993                 break;
23994             
23995             default:
23996                 return false;
23997         }
23998
23999     },
24000     // private
24001     IsLongEnough: function (pwd, size)
24002     {
24003         return !(pwd == null || isNaN(size) || pwd.length < size);
24004     },
24005     // private
24006     SpansEnoughCharacterSets: function (word, nb)
24007     {
24008         if (!this.IsLongEnough(word, nb))
24009         {
24010             return false;
24011         }
24012
24013         var characterSetChecks = new Array(
24014             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24015             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24016         );
24017         
24018         for (var index = 0; index < word.length; ++index) {
24019             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24020                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24021                     characterSetChecks[nCharSet].fResult = true;
24022                     break;
24023                 }
24024             }
24025         }
24026
24027         var nCharSets = 0;
24028         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24029             if (characterSetChecks[nCharSet].fResult) {
24030                 ++nCharSets;
24031             }
24032         }
24033
24034         if (nCharSets < nb) {
24035             return false;
24036         }
24037         return true;
24038     },
24039     // private
24040     ClientSideStrongPassword: function (pwd)
24041     {
24042         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24043     },
24044     // private
24045     ClientSideMediumPassword: function (pwd)
24046     {
24047         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24048     },
24049     // private
24050     ClientSideWeakPassword: function (pwd)
24051     {
24052         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24053     }
24054           
24055 })//<script type="text/javascript">
24056
24057 /*
24058  * Based  Ext JS Library 1.1.1
24059  * Copyright(c) 2006-2007, Ext JS, LLC.
24060  * LGPL
24061  *
24062  */
24063  
24064 /**
24065  * @class Roo.HtmlEditorCore
24066  * @extends Roo.Component
24067  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24068  *
24069  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24070  */
24071
24072 Roo.HtmlEditorCore = function(config){
24073     
24074     
24075     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24076     
24077     
24078     this.addEvents({
24079         /**
24080          * @event initialize
24081          * Fires when the editor is fully initialized (including the iframe)
24082          * @param {Roo.HtmlEditorCore} this
24083          */
24084         initialize: true,
24085         /**
24086          * @event activate
24087          * Fires when the editor is first receives the focus. Any insertion must wait
24088          * until after this event.
24089          * @param {Roo.HtmlEditorCore} this
24090          */
24091         activate: true,
24092          /**
24093          * @event beforesync
24094          * Fires before the textarea is updated with content from the editor iframe. Return false
24095          * to cancel the sync.
24096          * @param {Roo.HtmlEditorCore} this
24097          * @param {String} html
24098          */
24099         beforesync: true,
24100          /**
24101          * @event beforepush
24102          * Fires before the iframe editor is updated with content from the textarea. Return false
24103          * to cancel the push.
24104          * @param {Roo.HtmlEditorCore} this
24105          * @param {String} html
24106          */
24107         beforepush: true,
24108          /**
24109          * @event sync
24110          * Fires when the textarea is updated with content from the editor iframe.
24111          * @param {Roo.HtmlEditorCore} this
24112          * @param {String} html
24113          */
24114         sync: true,
24115          /**
24116          * @event push
24117          * Fires when the iframe editor is updated with content from the textarea.
24118          * @param {Roo.HtmlEditorCore} this
24119          * @param {String} html
24120          */
24121         push: true,
24122         
24123         /**
24124          * @event editorevent
24125          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24126          * @param {Roo.HtmlEditorCore} this
24127          */
24128         editorevent: true
24129         
24130     });
24131     
24132     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24133     
24134     // defaults : white / black...
24135     this.applyBlacklists();
24136     
24137     
24138     
24139 };
24140
24141
24142 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24143
24144
24145      /**
24146      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24147      */
24148     
24149     owner : false,
24150     
24151      /**
24152      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24153      *                        Roo.resizable.
24154      */
24155     resizable : false,
24156      /**
24157      * @cfg {Number} height (in pixels)
24158      */   
24159     height: 300,
24160    /**
24161      * @cfg {Number} width (in pixels)
24162      */   
24163     width: 500,
24164     
24165     /**
24166      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24167      * 
24168      */
24169     stylesheets: false,
24170     
24171     // id of frame..
24172     frameId: false,
24173     
24174     // private properties
24175     validationEvent : false,
24176     deferHeight: true,
24177     initialized : false,
24178     activated : false,
24179     sourceEditMode : false,
24180     onFocus : Roo.emptyFn,
24181     iframePad:3,
24182     hideMode:'offsets',
24183     
24184     clearUp: true,
24185     
24186     // blacklist + whitelisted elements..
24187     black: false,
24188     white: false,
24189      
24190     bodyCls : '',
24191
24192     /**
24193      * Protected method that will not generally be called directly. It
24194      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24195      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24196      */
24197     getDocMarkup : function(){
24198         // body styles..
24199         var st = '';
24200         
24201         // inherit styels from page...?? 
24202         if (this.stylesheets === false) {
24203             
24204             Roo.get(document.head).select('style').each(function(node) {
24205                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24206             });
24207             
24208             Roo.get(document.head).select('link').each(function(node) { 
24209                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24210             });
24211             
24212         } else if (!this.stylesheets.length) {
24213                 // simple..
24214                 st = '<style type="text/css">' +
24215                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24216                    '</style>';
24217         } else {
24218             for (var i in this.stylesheets) { 
24219                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24220             }
24221             
24222         }
24223         
24224         st +=  '<style type="text/css">' +
24225             'IMG { cursor: pointer } ' +
24226         '</style>';
24227
24228         var cls = 'roo-htmleditor-body';
24229         
24230         if(this.bodyCls.length){
24231             cls += ' ' + this.bodyCls;
24232         }
24233         
24234         return '<html><head>' + st  +
24235             //<style type="text/css">' +
24236             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24237             //'</style>' +
24238             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24239     },
24240
24241     // private
24242     onRender : function(ct, position)
24243     {
24244         var _t = this;
24245         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24246         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24247         
24248         
24249         this.el.dom.style.border = '0 none';
24250         this.el.dom.setAttribute('tabIndex', -1);
24251         this.el.addClass('x-hidden hide');
24252         
24253         
24254         
24255         if(Roo.isIE){ // fix IE 1px bogus margin
24256             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24257         }
24258        
24259         
24260         this.frameId = Roo.id();
24261         
24262          
24263         
24264         var iframe = this.owner.wrap.createChild({
24265             tag: 'iframe',
24266             cls: 'form-control', // bootstrap..
24267             id: this.frameId,
24268             name: this.frameId,
24269             frameBorder : 'no',
24270             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24271         }, this.el
24272         );
24273         
24274         
24275         this.iframe = iframe.dom;
24276
24277          this.assignDocWin();
24278         
24279         this.doc.designMode = 'on';
24280        
24281         this.doc.open();
24282         this.doc.write(this.getDocMarkup());
24283         this.doc.close();
24284
24285         
24286         var task = { // must defer to wait for browser to be ready
24287             run : function(){
24288                 //console.log("run task?" + this.doc.readyState);
24289                 this.assignDocWin();
24290                 if(this.doc.body || this.doc.readyState == 'complete'){
24291                     try {
24292                         this.doc.designMode="on";
24293                     } catch (e) {
24294                         return;
24295                     }
24296                     Roo.TaskMgr.stop(task);
24297                     this.initEditor.defer(10, this);
24298                 }
24299             },
24300             interval : 10,
24301             duration: 10000,
24302             scope: this
24303         };
24304         Roo.TaskMgr.start(task);
24305
24306     },
24307
24308     // private
24309     onResize : function(w, h)
24310     {
24311          Roo.log('resize: ' +w + ',' + h );
24312         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24313         if(!this.iframe){
24314             return;
24315         }
24316         if(typeof w == 'number'){
24317             
24318             this.iframe.style.width = w + 'px';
24319         }
24320         if(typeof h == 'number'){
24321             
24322             this.iframe.style.height = h + 'px';
24323             if(this.doc){
24324                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24325             }
24326         }
24327         
24328     },
24329
24330     /**
24331      * Toggles the editor between standard and source edit mode.
24332      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24333      */
24334     toggleSourceEdit : function(sourceEditMode){
24335         
24336         this.sourceEditMode = sourceEditMode === true;
24337         
24338         if(this.sourceEditMode){
24339  
24340             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24341             
24342         }else{
24343             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24344             //this.iframe.className = '';
24345             this.deferFocus();
24346         }
24347         //this.setSize(this.owner.wrap.getSize());
24348         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24349     },
24350
24351     
24352   
24353
24354     /**
24355      * Protected method that will not generally be called directly. If you need/want
24356      * custom HTML cleanup, this is the method you should override.
24357      * @param {String} html The HTML to be cleaned
24358      * return {String} The cleaned HTML
24359      */
24360     cleanHtml : function(html){
24361         html = String(html);
24362         if(html.length > 5){
24363             if(Roo.isSafari){ // strip safari nonsense
24364                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24365             }
24366         }
24367         if(html == '&nbsp;'){
24368             html = '';
24369         }
24370         return html;
24371     },
24372
24373     /**
24374      * HTML Editor -> Textarea
24375      * Protected method that will not generally be called directly. Syncs the contents
24376      * of the editor iframe with the textarea.
24377      */
24378     syncValue : function(){
24379         if(this.initialized){
24380             var bd = (this.doc.body || this.doc.documentElement);
24381             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24382             var html = bd.innerHTML;
24383             if(Roo.isSafari){
24384                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24385                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24386                 if(m && m[1]){
24387                     html = '<div style="'+m[0]+'">' + html + '</div>';
24388                 }
24389             }
24390             html = this.cleanHtml(html);
24391             // fix up the special chars.. normaly like back quotes in word...
24392             // however we do not want to do this with chinese..
24393             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24394                 
24395                 var cc = match.charCodeAt();
24396
24397                 // Get the character value, handling surrogate pairs
24398                 if (match.length == 2) {
24399                     // It's a surrogate pair, calculate the Unicode code point
24400                     var high = match.charCodeAt(0) - 0xD800;
24401                     var low  = match.charCodeAt(1) - 0xDC00;
24402                     cc = (high * 0x400) + low + 0x10000;
24403                 }  else if (
24404                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24405                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24406                     (cc >= 0xf900 && cc < 0xfb00 )
24407                 ) {
24408                         return match;
24409                 }  
24410          
24411                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24412                 return "&#" + cc + ";";
24413                 
24414                 
24415             });
24416             
24417             
24418              
24419             if(this.owner.fireEvent('beforesync', this, html) !== false){
24420                 this.el.dom.value = html;
24421                 this.owner.fireEvent('sync', this, html);
24422             }
24423         }
24424     },
24425
24426     /**
24427      * Protected method that will not generally be called directly. Pushes the value of the textarea
24428      * into the iframe editor.
24429      */
24430     pushValue : function(){
24431         if(this.initialized){
24432             var v = this.el.dom.value.trim();
24433             
24434 //            if(v.length < 1){
24435 //                v = '&#160;';
24436 //            }
24437             
24438             if(this.owner.fireEvent('beforepush', this, v) !== false){
24439                 var d = (this.doc.body || this.doc.documentElement);
24440                 d.innerHTML = v;
24441                 this.cleanUpPaste();
24442                 this.el.dom.value = d.innerHTML;
24443                 this.owner.fireEvent('push', this, v);
24444             }
24445         }
24446     },
24447
24448     // private
24449     deferFocus : function(){
24450         this.focus.defer(10, this);
24451     },
24452
24453     // doc'ed in Field
24454     focus : function(){
24455         if(this.win && !this.sourceEditMode){
24456             this.win.focus();
24457         }else{
24458             this.el.focus();
24459         }
24460     },
24461     
24462     assignDocWin: function()
24463     {
24464         var iframe = this.iframe;
24465         
24466          if(Roo.isIE){
24467             this.doc = iframe.contentWindow.document;
24468             this.win = iframe.contentWindow;
24469         } else {
24470 //            if (!Roo.get(this.frameId)) {
24471 //                return;
24472 //            }
24473 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24474 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24475             
24476             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24477                 return;
24478             }
24479             
24480             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24481             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24482         }
24483     },
24484     
24485     // private
24486     initEditor : function(){
24487         //console.log("INIT EDITOR");
24488         this.assignDocWin();
24489         
24490         
24491         
24492         this.doc.designMode="on";
24493         this.doc.open();
24494         this.doc.write(this.getDocMarkup());
24495         this.doc.close();
24496         
24497         var dbody = (this.doc.body || this.doc.documentElement);
24498         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24499         // this copies styles from the containing element into thsi one..
24500         // not sure why we need all of this..
24501         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24502         
24503         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24504         //ss['background-attachment'] = 'fixed'; // w3c
24505         dbody.bgProperties = 'fixed'; // ie
24506         //Roo.DomHelper.applyStyles(dbody, ss);
24507         Roo.EventManager.on(this.doc, {
24508             //'mousedown': this.onEditorEvent,
24509             'mouseup': this.onEditorEvent,
24510             'dblclick': this.onEditorEvent,
24511             'click': this.onEditorEvent,
24512             'keyup': this.onEditorEvent,
24513             buffer:100,
24514             scope: this
24515         });
24516         if(Roo.isGecko){
24517             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24518         }
24519         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24520             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24521         }
24522         this.initialized = true;
24523
24524         this.owner.fireEvent('initialize', this);
24525         this.pushValue();
24526     },
24527
24528     // private
24529     onDestroy : function(){
24530         
24531         
24532         
24533         if(this.rendered){
24534             
24535             //for (var i =0; i < this.toolbars.length;i++) {
24536             //    // fixme - ask toolbars for heights?
24537             //    this.toolbars[i].onDestroy();
24538            // }
24539             
24540             //this.wrap.dom.innerHTML = '';
24541             //this.wrap.remove();
24542         }
24543     },
24544
24545     // private
24546     onFirstFocus : function(){
24547         
24548         this.assignDocWin();
24549         
24550         
24551         this.activated = true;
24552          
24553     
24554         if(Roo.isGecko){ // prevent silly gecko errors
24555             this.win.focus();
24556             var s = this.win.getSelection();
24557             if(!s.focusNode || s.focusNode.nodeType != 3){
24558                 var r = s.getRangeAt(0);
24559                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24560                 r.collapse(true);
24561                 this.deferFocus();
24562             }
24563             try{
24564                 this.execCmd('useCSS', true);
24565                 this.execCmd('styleWithCSS', false);
24566             }catch(e){}
24567         }
24568         this.owner.fireEvent('activate', this);
24569     },
24570
24571     // private
24572     adjustFont: function(btn){
24573         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24574         //if(Roo.isSafari){ // safari
24575         //    adjust *= 2;
24576        // }
24577         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24578         if(Roo.isSafari){ // safari
24579             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24580             v =  (v < 10) ? 10 : v;
24581             v =  (v > 48) ? 48 : v;
24582             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24583             
24584         }
24585         
24586         
24587         v = Math.max(1, v+adjust);
24588         
24589         this.execCmd('FontSize', v  );
24590     },
24591
24592     onEditorEvent : function(e)
24593     {
24594         this.owner.fireEvent('editorevent', this, e);
24595       //  this.updateToolbar();
24596         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24597     },
24598
24599     insertTag : function(tg)
24600     {
24601         // could be a bit smarter... -> wrap the current selected tRoo..
24602         if (tg.toLowerCase() == 'span' ||
24603             tg.toLowerCase() == 'code' ||
24604             tg.toLowerCase() == 'sup' ||
24605             tg.toLowerCase() == 'sub' 
24606             ) {
24607             
24608             range = this.createRange(this.getSelection());
24609             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24610             wrappingNode.appendChild(range.extractContents());
24611             range.insertNode(wrappingNode);
24612
24613             return;
24614             
24615             
24616             
24617         }
24618         this.execCmd("formatblock",   tg);
24619         
24620     },
24621     
24622     insertText : function(txt)
24623     {
24624         
24625         
24626         var range = this.createRange();
24627         range.deleteContents();
24628                //alert(Sender.getAttribute('label'));
24629                
24630         range.insertNode(this.doc.createTextNode(txt));
24631     } ,
24632     
24633      
24634
24635     /**
24636      * Executes a Midas editor command on the editor document and performs necessary focus and
24637      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24638      * @param {String} cmd The Midas command
24639      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24640      */
24641     relayCmd : function(cmd, value){
24642         this.win.focus();
24643         this.execCmd(cmd, value);
24644         this.owner.fireEvent('editorevent', this);
24645         //this.updateToolbar();
24646         this.owner.deferFocus();
24647     },
24648
24649     /**
24650      * Executes a Midas editor command directly on the editor document.
24651      * For visual commands, you should use {@link #relayCmd} instead.
24652      * <b>This should only be called after the editor is initialized.</b>
24653      * @param {String} cmd The Midas command
24654      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24655      */
24656     execCmd : function(cmd, value){
24657         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24658         this.syncValue();
24659     },
24660  
24661  
24662    
24663     /**
24664      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24665      * to insert tRoo.
24666      * @param {String} text | dom node.. 
24667      */
24668     insertAtCursor : function(text)
24669     {
24670         
24671         if(!this.activated){
24672             return;
24673         }
24674         /*
24675         if(Roo.isIE){
24676             this.win.focus();
24677             var r = this.doc.selection.createRange();
24678             if(r){
24679                 r.collapse(true);
24680                 r.pasteHTML(text);
24681                 this.syncValue();
24682                 this.deferFocus();
24683             
24684             }
24685             return;
24686         }
24687         */
24688         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24689             this.win.focus();
24690             
24691             
24692             // from jquery ui (MIT licenced)
24693             var range, node;
24694             var win = this.win;
24695             
24696             if (win.getSelection && win.getSelection().getRangeAt) {
24697                 range = win.getSelection().getRangeAt(0);
24698                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24699                 range.insertNode(node);
24700             } else if (win.document.selection && win.document.selection.createRange) {
24701                 // no firefox support
24702                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24703                 win.document.selection.createRange().pasteHTML(txt);
24704             } else {
24705                 // no firefox support
24706                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24707                 this.execCmd('InsertHTML', txt);
24708             } 
24709             
24710             this.syncValue();
24711             
24712             this.deferFocus();
24713         }
24714     },
24715  // private
24716     mozKeyPress : function(e){
24717         if(e.ctrlKey){
24718             var c = e.getCharCode(), cmd;
24719           
24720             if(c > 0){
24721                 c = String.fromCharCode(c).toLowerCase();
24722                 switch(c){
24723                     case 'b':
24724                         cmd = 'bold';
24725                         break;
24726                     case 'i':
24727                         cmd = 'italic';
24728                         break;
24729                     
24730                     case 'u':
24731                         cmd = 'underline';
24732                         break;
24733                     
24734                     case 'v':
24735                         this.cleanUpPaste.defer(100, this);
24736                         return;
24737                         
24738                 }
24739                 if(cmd){
24740                     this.win.focus();
24741                     this.execCmd(cmd);
24742                     this.deferFocus();
24743                     e.preventDefault();
24744                 }
24745                 
24746             }
24747         }
24748     },
24749
24750     // private
24751     fixKeys : function(){ // load time branching for fastest keydown performance
24752         if(Roo.isIE){
24753             return function(e){
24754                 var k = e.getKey(), r;
24755                 if(k == e.TAB){
24756                     e.stopEvent();
24757                     r = this.doc.selection.createRange();
24758                     if(r){
24759                         r.collapse(true);
24760                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24761                         this.deferFocus();
24762                     }
24763                     return;
24764                 }
24765                 
24766                 if(k == e.ENTER){
24767                     r = this.doc.selection.createRange();
24768                     if(r){
24769                         var target = r.parentElement();
24770                         if(!target || target.tagName.toLowerCase() != 'li'){
24771                             e.stopEvent();
24772                             r.pasteHTML('<br />');
24773                             r.collapse(false);
24774                             r.select();
24775                         }
24776                     }
24777                 }
24778                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24779                     this.cleanUpPaste.defer(100, this);
24780                     return;
24781                 }
24782                 
24783                 
24784             };
24785         }else if(Roo.isOpera){
24786             return function(e){
24787                 var k = e.getKey();
24788                 if(k == e.TAB){
24789                     e.stopEvent();
24790                     this.win.focus();
24791                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24792                     this.deferFocus();
24793                 }
24794                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24795                     this.cleanUpPaste.defer(100, this);
24796                     return;
24797                 }
24798                 
24799             };
24800         }else if(Roo.isSafari){
24801             return function(e){
24802                 var k = e.getKey();
24803                 
24804                 if(k == e.TAB){
24805                     e.stopEvent();
24806                     this.execCmd('InsertText','\t');
24807                     this.deferFocus();
24808                     return;
24809                 }
24810                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24811                     this.cleanUpPaste.defer(100, this);
24812                     return;
24813                 }
24814                 
24815              };
24816         }
24817     }(),
24818     
24819     getAllAncestors: function()
24820     {
24821         var p = this.getSelectedNode();
24822         var a = [];
24823         if (!p) {
24824             a.push(p); // push blank onto stack..
24825             p = this.getParentElement();
24826         }
24827         
24828         
24829         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24830             a.push(p);
24831             p = p.parentNode;
24832         }
24833         a.push(this.doc.body);
24834         return a;
24835     },
24836     lastSel : false,
24837     lastSelNode : false,
24838     
24839     
24840     getSelection : function() 
24841     {
24842         this.assignDocWin();
24843         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24844     },
24845     
24846     getSelectedNode: function() 
24847     {
24848         // this may only work on Gecko!!!
24849         
24850         // should we cache this!!!!
24851         
24852         
24853         
24854          
24855         var range = this.createRange(this.getSelection()).cloneRange();
24856         
24857         if (Roo.isIE) {
24858             var parent = range.parentElement();
24859             while (true) {
24860                 var testRange = range.duplicate();
24861                 testRange.moveToElementText(parent);
24862                 if (testRange.inRange(range)) {
24863                     break;
24864                 }
24865                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24866                     break;
24867                 }
24868                 parent = parent.parentElement;
24869             }
24870             return parent;
24871         }
24872         
24873         // is ancestor a text element.
24874         var ac =  range.commonAncestorContainer;
24875         if (ac.nodeType == 3) {
24876             ac = ac.parentNode;
24877         }
24878         
24879         var ar = ac.childNodes;
24880          
24881         var nodes = [];
24882         var other_nodes = [];
24883         var has_other_nodes = false;
24884         for (var i=0;i<ar.length;i++) {
24885             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24886                 continue;
24887             }
24888             // fullly contained node.
24889             
24890             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24891                 nodes.push(ar[i]);
24892                 continue;
24893             }
24894             
24895             // probably selected..
24896             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24897                 other_nodes.push(ar[i]);
24898                 continue;
24899             }
24900             // outer..
24901             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24902                 continue;
24903             }
24904             
24905             
24906             has_other_nodes = true;
24907         }
24908         if (!nodes.length && other_nodes.length) {
24909             nodes= other_nodes;
24910         }
24911         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24912             return false;
24913         }
24914         
24915         return nodes[0];
24916     },
24917     createRange: function(sel)
24918     {
24919         // this has strange effects when using with 
24920         // top toolbar - not sure if it's a great idea.
24921         //this.editor.contentWindow.focus();
24922         if (typeof sel != "undefined") {
24923             try {
24924                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24925             } catch(e) {
24926                 return this.doc.createRange();
24927             }
24928         } else {
24929             return this.doc.createRange();
24930         }
24931     },
24932     getParentElement: function()
24933     {
24934         
24935         this.assignDocWin();
24936         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24937         
24938         var range = this.createRange(sel);
24939          
24940         try {
24941             var p = range.commonAncestorContainer;
24942             while (p.nodeType == 3) { // text node
24943                 p = p.parentNode;
24944             }
24945             return p;
24946         } catch (e) {
24947             return null;
24948         }
24949     
24950     },
24951     /***
24952      *
24953      * Range intersection.. the hard stuff...
24954      *  '-1' = before
24955      *  '0' = hits..
24956      *  '1' = after.
24957      *         [ -- selected range --- ]
24958      *   [fail]                        [fail]
24959      *
24960      *    basically..
24961      *      if end is before start or  hits it. fail.
24962      *      if start is after end or hits it fail.
24963      *
24964      *   if either hits (but other is outside. - then it's not 
24965      *   
24966      *    
24967      **/
24968     
24969     
24970     // @see http://www.thismuchiknow.co.uk/?p=64.
24971     rangeIntersectsNode : function(range, node)
24972     {
24973         var nodeRange = node.ownerDocument.createRange();
24974         try {
24975             nodeRange.selectNode(node);
24976         } catch (e) {
24977             nodeRange.selectNodeContents(node);
24978         }
24979     
24980         var rangeStartRange = range.cloneRange();
24981         rangeStartRange.collapse(true);
24982     
24983         var rangeEndRange = range.cloneRange();
24984         rangeEndRange.collapse(false);
24985     
24986         var nodeStartRange = nodeRange.cloneRange();
24987         nodeStartRange.collapse(true);
24988     
24989         var nodeEndRange = nodeRange.cloneRange();
24990         nodeEndRange.collapse(false);
24991     
24992         return rangeStartRange.compareBoundaryPoints(
24993                  Range.START_TO_START, nodeEndRange) == -1 &&
24994                rangeEndRange.compareBoundaryPoints(
24995                  Range.START_TO_START, nodeStartRange) == 1;
24996         
24997          
24998     },
24999     rangeCompareNode : function(range, node)
25000     {
25001         var nodeRange = node.ownerDocument.createRange();
25002         try {
25003             nodeRange.selectNode(node);
25004         } catch (e) {
25005             nodeRange.selectNodeContents(node);
25006         }
25007         
25008         
25009         range.collapse(true);
25010     
25011         nodeRange.collapse(true);
25012      
25013         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25014         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25015          
25016         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25017         
25018         var nodeIsBefore   =  ss == 1;
25019         var nodeIsAfter    = ee == -1;
25020         
25021         if (nodeIsBefore && nodeIsAfter) {
25022             return 0; // outer
25023         }
25024         if (!nodeIsBefore && nodeIsAfter) {
25025             return 1; //right trailed.
25026         }
25027         
25028         if (nodeIsBefore && !nodeIsAfter) {
25029             return 2;  // left trailed.
25030         }
25031         // fully contined.
25032         return 3;
25033     },
25034
25035     // private? - in a new class?
25036     cleanUpPaste :  function()
25037     {
25038         // cleans up the whole document..
25039         Roo.log('cleanuppaste');
25040         
25041         this.cleanUpChildren(this.doc.body);
25042         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25043         if (clean != this.doc.body.innerHTML) {
25044             this.doc.body.innerHTML = clean;
25045         }
25046         
25047     },
25048     
25049     cleanWordChars : function(input) {// change the chars to hex code
25050         var he = Roo.HtmlEditorCore;
25051         
25052         var output = input;
25053         Roo.each(he.swapCodes, function(sw) { 
25054             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25055             
25056             output = output.replace(swapper, sw[1]);
25057         });
25058         
25059         return output;
25060     },
25061     
25062     
25063     cleanUpChildren : function (n)
25064     {
25065         if (!n.childNodes.length) {
25066             return;
25067         }
25068         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25069            this.cleanUpChild(n.childNodes[i]);
25070         }
25071     },
25072     
25073     
25074         
25075     
25076     cleanUpChild : function (node)
25077     {
25078         var ed = this;
25079         //console.log(node);
25080         if (node.nodeName == "#text") {
25081             // clean up silly Windows -- stuff?
25082             return; 
25083         }
25084         if (node.nodeName == "#comment") {
25085             node.parentNode.removeChild(node);
25086             // clean up silly Windows -- stuff?
25087             return; 
25088         }
25089         var lcname = node.tagName.toLowerCase();
25090         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25091         // whitelist of tags..
25092         
25093         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25094             // remove node.
25095             node.parentNode.removeChild(node);
25096             return;
25097             
25098         }
25099         
25100         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25101         
25102         // spans with no attributes - just remove them..
25103         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25104             remove_keep_children = true;
25105         }
25106         
25107         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25108         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25109         
25110         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25111         //    remove_keep_children = true;
25112         //}
25113         
25114         if (remove_keep_children) {
25115             this.cleanUpChildren(node);
25116             // inserts everything just before this node...
25117             while (node.childNodes.length) {
25118                 var cn = node.childNodes[0];
25119                 node.removeChild(cn);
25120                 node.parentNode.insertBefore(cn, node);
25121             }
25122             node.parentNode.removeChild(node);
25123             return;
25124         }
25125         
25126         if (!node.attributes || !node.attributes.length) {
25127             
25128           
25129             
25130             
25131             this.cleanUpChildren(node);
25132             return;
25133         }
25134         
25135         function cleanAttr(n,v)
25136         {
25137             
25138             if (v.match(/^\./) || v.match(/^\//)) {
25139                 return;
25140             }
25141             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25142                 return;
25143             }
25144             if (v.match(/^#/)) {
25145                 return;
25146             }
25147             if (v.match(/^\{/)) { // allow template editing.
25148                 return;
25149             }
25150 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25151             node.removeAttribute(n);
25152             
25153         }
25154         
25155         var cwhite = this.cwhite;
25156         var cblack = this.cblack;
25157             
25158         function cleanStyle(n,v)
25159         {
25160             if (v.match(/expression/)) { //XSS?? should we even bother..
25161                 node.removeAttribute(n);
25162                 return;
25163             }
25164             
25165             var parts = v.split(/;/);
25166             var clean = [];
25167             
25168             Roo.each(parts, function(p) {
25169                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25170                 if (!p.length) {
25171                     return true;
25172                 }
25173                 var l = p.split(':').shift().replace(/\s+/g,'');
25174                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25175                 
25176                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25177 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25178                     //node.removeAttribute(n);
25179                     return true;
25180                 }
25181                 //Roo.log()
25182                 // only allow 'c whitelisted system attributes'
25183                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25184 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25185                     //node.removeAttribute(n);
25186                     return true;
25187                 }
25188                 
25189                 
25190                  
25191                 
25192                 clean.push(p);
25193                 return true;
25194             });
25195             if (clean.length) { 
25196                 node.setAttribute(n, clean.join(';'));
25197             } else {
25198                 node.removeAttribute(n);
25199             }
25200             
25201         }
25202         
25203         
25204         for (var i = node.attributes.length-1; i > -1 ; i--) {
25205             var a = node.attributes[i];
25206             //console.log(a);
25207             
25208             if (a.name.toLowerCase().substr(0,2)=='on')  {
25209                 node.removeAttribute(a.name);
25210                 continue;
25211             }
25212             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25213                 node.removeAttribute(a.name);
25214                 continue;
25215             }
25216             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25217                 cleanAttr(a.name,a.value); // fixme..
25218                 continue;
25219             }
25220             if (a.name == 'style') {
25221                 cleanStyle(a.name,a.value);
25222                 continue;
25223             }
25224             /// clean up MS crap..
25225             // tecnically this should be a list of valid class'es..
25226             
25227             
25228             if (a.name == 'class') {
25229                 if (a.value.match(/^Mso/)) {
25230                     node.removeAttribute('class');
25231                 }
25232                 
25233                 if (a.value.match(/^body$/)) {
25234                     node.removeAttribute('class');
25235                 }
25236                 continue;
25237             }
25238             
25239             // style cleanup!?
25240             // class cleanup?
25241             
25242         }
25243         
25244         
25245         this.cleanUpChildren(node);
25246         
25247         
25248     },
25249     
25250     /**
25251      * Clean up MS wordisms...
25252      */
25253     cleanWord : function(node)
25254     {
25255         if (!node) {
25256             this.cleanWord(this.doc.body);
25257             return;
25258         }
25259         
25260         if(
25261                 node.nodeName == 'SPAN' &&
25262                 !node.hasAttributes() &&
25263                 node.childNodes.length == 1 &&
25264                 node.firstChild.nodeName == "#text"  
25265         ) {
25266             var textNode = node.firstChild;
25267             node.removeChild(textNode);
25268             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25269                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25270             }
25271             node.parentNode.insertBefore(textNode, node);
25272             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25273                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25274             }
25275             node.parentNode.removeChild(node);
25276         }
25277         
25278         if (node.nodeName == "#text") {
25279             // clean up silly Windows -- stuff?
25280             return; 
25281         }
25282         if (node.nodeName == "#comment") {
25283             node.parentNode.removeChild(node);
25284             // clean up silly Windows -- stuff?
25285             return; 
25286         }
25287         
25288         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25289             node.parentNode.removeChild(node);
25290             return;
25291         }
25292         //Roo.log(node.tagName);
25293         // remove - but keep children..
25294         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25295             //Roo.log('-- removed');
25296             while (node.childNodes.length) {
25297                 var cn = node.childNodes[0];
25298                 node.removeChild(cn);
25299                 node.parentNode.insertBefore(cn, node);
25300                 // move node to parent - and clean it..
25301                 this.cleanWord(cn);
25302             }
25303             node.parentNode.removeChild(node);
25304             /// no need to iterate chidlren = it's got none..
25305             //this.iterateChildren(node, this.cleanWord);
25306             return;
25307         }
25308         // clean styles
25309         if (node.className.length) {
25310             
25311             var cn = node.className.split(/\W+/);
25312             var cna = [];
25313             Roo.each(cn, function(cls) {
25314                 if (cls.match(/Mso[a-zA-Z]+/)) {
25315                     return;
25316                 }
25317                 cna.push(cls);
25318             });
25319             node.className = cna.length ? cna.join(' ') : '';
25320             if (!cna.length) {
25321                 node.removeAttribute("class");
25322             }
25323         }
25324         
25325         if (node.hasAttribute("lang")) {
25326             node.removeAttribute("lang");
25327         }
25328         
25329         if (node.hasAttribute("style")) {
25330             
25331             var styles = node.getAttribute("style").split(";");
25332             var nstyle = [];
25333             Roo.each(styles, function(s) {
25334                 if (!s.match(/:/)) {
25335                     return;
25336                 }
25337                 var kv = s.split(":");
25338                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25339                     return;
25340                 }
25341                 // what ever is left... we allow.
25342                 nstyle.push(s);
25343             });
25344             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25345             if (!nstyle.length) {
25346                 node.removeAttribute('style');
25347             }
25348         }
25349         this.iterateChildren(node, this.cleanWord);
25350         
25351         
25352         
25353     },
25354     /**
25355      * iterateChildren of a Node, calling fn each time, using this as the scole..
25356      * @param {DomNode} node node to iterate children of.
25357      * @param {Function} fn method of this class to call on each item.
25358      */
25359     iterateChildren : function(node, fn)
25360     {
25361         if (!node.childNodes.length) {
25362                 return;
25363         }
25364         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25365            fn.call(this, node.childNodes[i])
25366         }
25367     },
25368     
25369     
25370     /**
25371      * cleanTableWidths.
25372      *
25373      * Quite often pasting from word etc.. results in tables with column and widths.
25374      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25375      *
25376      */
25377     cleanTableWidths : function(node)
25378     {
25379          
25380          
25381         if (!node) {
25382             this.cleanTableWidths(this.doc.body);
25383             return;
25384         }
25385         
25386         // ignore list...
25387         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25388             return; 
25389         }
25390         Roo.log(node.tagName);
25391         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25392             this.iterateChildren(node, this.cleanTableWidths);
25393             return;
25394         }
25395         if (node.hasAttribute('width')) {
25396             node.removeAttribute('width');
25397         }
25398         
25399          
25400         if (node.hasAttribute("style")) {
25401             // pretty basic...
25402             
25403             var styles = node.getAttribute("style").split(";");
25404             var nstyle = [];
25405             Roo.each(styles, function(s) {
25406                 if (!s.match(/:/)) {
25407                     return;
25408                 }
25409                 var kv = s.split(":");
25410                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25411                     return;
25412                 }
25413                 // what ever is left... we allow.
25414                 nstyle.push(s);
25415             });
25416             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25417             if (!nstyle.length) {
25418                 node.removeAttribute('style');
25419             }
25420         }
25421         
25422         this.iterateChildren(node, this.cleanTableWidths);
25423         
25424         
25425     },
25426     
25427     
25428     
25429     
25430     domToHTML : function(currentElement, depth, nopadtext) {
25431         
25432         depth = depth || 0;
25433         nopadtext = nopadtext || false;
25434     
25435         if (!currentElement) {
25436             return this.domToHTML(this.doc.body);
25437         }
25438         
25439         //Roo.log(currentElement);
25440         var j;
25441         var allText = false;
25442         var nodeName = currentElement.nodeName;
25443         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25444         
25445         if  (nodeName == '#text') {
25446             
25447             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25448         }
25449         
25450         
25451         var ret = '';
25452         if (nodeName != 'BODY') {
25453              
25454             var i = 0;
25455             // Prints the node tagName, such as <A>, <IMG>, etc
25456             if (tagName) {
25457                 var attr = [];
25458                 for(i = 0; i < currentElement.attributes.length;i++) {
25459                     // quoting?
25460                     var aname = currentElement.attributes.item(i).name;
25461                     if (!currentElement.attributes.item(i).value.length) {
25462                         continue;
25463                     }
25464                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25465                 }
25466                 
25467                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25468             } 
25469             else {
25470                 
25471                 // eack
25472             }
25473         } else {
25474             tagName = false;
25475         }
25476         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25477             return ret;
25478         }
25479         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25480             nopadtext = true;
25481         }
25482         
25483         
25484         // Traverse the tree
25485         i = 0;
25486         var currentElementChild = currentElement.childNodes.item(i);
25487         var allText = true;
25488         var innerHTML  = '';
25489         lastnode = '';
25490         while (currentElementChild) {
25491             // Formatting code (indent the tree so it looks nice on the screen)
25492             var nopad = nopadtext;
25493             if (lastnode == 'SPAN') {
25494                 nopad  = true;
25495             }
25496             // text
25497             if  (currentElementChild.nodeName == '#text') {
25498                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25499                 toadd = nopadtext ? toadd : toadd.trim();
25500                 if (!nopad && toadd.length > 80) {
25501                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25502                 }
25503                 innerHTML  += toadd;
25504                 
25505                 i++;
25506                 currentElementChild = currentElement.childNodes.item(i);
25507                 lastNode = '';
25508                 continue;
25509             }
25510             allText = false;
25511             
25512             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25513                 
25514             // Recursively traverse the tree structure of the child node
25515             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25516             lastnode = currentElementChild.nodeName;
25517             i++;
25518             currentElementChild=currentElement.childNodes.item(i);
25519         }
25520         
25521         ret += innerHTML;
25522         
25523         if (!allText) {
25524                 // The remaining code is mostly for formatting the tree
25525             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25526         }
25527         
25528         
25529         if (tagName) {
25530             ret+= "</"+tagName+">";
25531         }
25532         return ret;
25533         
25534     },
25535         
25536     applyBlacklists : function()
25537     {
25538         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25539         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25540         
25541         this.white = [];
25542         this.black = [];
25543         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25544             if (b.indexOf(tag) > -1) {
25545                 return;
25546             }
25547             this.white.push(tag);
25548             
25549         }, this);
25550         
25551         Roo.each(w, function(tag) {
25552             if (b.indexOf(tag) > -1) {
25553                 return;
25554             }
25555             if (this.white.indexOf(tag) > -1) {
25556                 return;
25557             }
25558             this.white.push(tag);
25559             
25560         }, this);
25561         
25562         
25563         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25564             if (w.indexOf(tag) > -1) {
25565                 return;
25566             }
25567             this.black.push(tag);
25568             
25569         }, this);
25570         
25571         Roo.each(b, function(tag) {
25572             if (w.indexOf(tag) > -1) {
25573                 return;
25574             }
25575             if (this.black.indexOf(tag) > -1) {
25576                 return;
25577             }
25578             this.black.push(tag);
25579             
25580         }, this);
25581         
25582         
25583         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25584         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25585         
25586         this.cwhite = [];
25587         this.cblack = [];
25588         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25589             if (b.indexOf(tag) > -1) {
25590                 return;
25591             }
25592             this.cwhite.push(tag);
25593             
25594         }, this);
25595         
25596         Roo.each(w, function(tag) {
25597             if (b.indexOf(tag) > -1) {
25598                 return;
25599             }
25600             if (this.cwhite.indexOf(tag) > -1) {
25601                 return;
25602             }
25603             this.cwhite.push(tag);
25604             
25605         }, this);
25606         
25607         
25608         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25609             if (w.indexOf(tag) > -1) {
25610                 return;
25611             }
25612             this.cblack.push(tag);
25613             
25614         }, this);
25615         
25616         Roo.each(b, function(tag) {
25617             if (w.indexOf(tag) > -1) {
25618                 return;
25619             }
25620             if (this.cblack.indexOf(tag) > -1) {
25621                 return;
25622             }
25623             this.cblack.push(tag);
25624             
25625         }, this);
25626     },
25627     
25628     setStylesheets : function(stylesheets)
25629     {
25630         if(typeof(stylesheets) == 'string'){
25631             Roo.get(this.iframe.contentDocument.head).createChild({
25632                 tag : 'link',
25633                 rel : 'stylesheet',
25634                 type : 'text/css',
25635                 href : stylesheets
25636             });
25637             
25638             return;
25639         }
25640         var _this = this;
25641      
25642         Roo.each(stylesheets, function(s) {
25643             if(!s.length){
25644                 return;
25645             }
25646             
25647             Roo.get(_this.iframe.contentDocument.head).createChild({
25648                 tag : 'link',
25649                 rel : 'stylesheet',
25650                 type : 'text/css',
25651                 href : s
25652             });
25653         });
25654
25655         
25656     },
25657     
25658     removeStylesheets : function()
25659     {
25660         var _this = this;
25661         
25662         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25663             s.remove();
25664         });
25665     },
25666     
25667     setStyle : function(style)
25668     {
25669         Roo.get(this.iframe.contentDocument.head).createChild({
25670             tag : 'style',
25671             type : 'text/css',
25672             html : style
25673         });
25674
25675         return;
25676     }
25677     
25678     // hide stuff that is not compatible
25679     /**
25680      * @event blur
25681      * @hide
25682      */
25683     /**
25684      * @event change
25685      * @hide
25686      */
25687     /**
25688      * @event focus
25689      * @hide
25690      */
25691     /**
25692      * @event specialkey
25693      * @hide
25694      */
25695     /**
25696      * @cfg {String} fieldClass @hide
25697      */
25698     /**
25699      * @cfg {String} focusClass @hide
25700      */
25701     /**
25702      * @cfg {String} autoCreate @hide
25703      */
25704     /**
25705      * @cfg {String} inputType @hide
25706      */
25707     /**
25708      * @cfg {String} invalidClass @hide
25709      */
25710     /**
25711      * @cfg {String} invalidText @hide
25712      */
25713     /**
25714      * @cfg {String} msgFx @hide
25715      */
25716     /**
25717      * @cfg {String} validateOnBlur @hide
25718      */
25719 });
25720
25721 Roo.HtmlEditorCore.white = [
25722         'area', 'br', 'img', 'input', 'hr', 'wbr',
25723         
25724        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25725        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25726        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25727        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25728        'table',   'ul',         'xmp', 
25729        
25730        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25731       'thead',   'tr', 
25732      
25733       'dir', 'menu', 'ol', 'ul', 'dl',
25734        
25735       'embed',  'object'
25736 ];
25737
25738
25739 Roo.HtmlEditorCore.black = [
25740     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25741         'applet', // 
25742         'base',   'basefont', 'bgsound', 'blink',  'body', 
25743         'frame',  'frameset', 'head',    'html',   'ilayer', 
25744         'iframe', 'layer',  'link',     'meta',    'object',   
25745         'script', 'style' ,'title',  'xml' // clean later..
25746 ];
25747 Roo.HtmlEditorCore.clean = [
25748     'script', 'style', 'title', 'xml'
25749 ];
25750 Roo.HtmlEditorCore.remove = [
25751     'font'
25752 ];
25753 // attributes..
25754
25755 Roo.HtmlEditorCore.ablack = [
25756     'on'
25757 ];
25758     
25759 Roo.HtmlEditorCore.aclean = [ 
25760     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25761 ];
25762
25763 // protocols..
25764 Roo.HtmlEditorCore.pwhite= [
25765         'http',  'https',  'mailto'
25766 ];
25767
25768 // white listed style attributes.
25769 Roo.HtmlEditorCore.cwhite= [
25770       //  'text-align', /// default is to allow most things..
25771       
25772          
25773 //        'font-size'//??
25774 ];
25775
25776 // black listed style attributes.
25777 Roo.HtmlEditorCore.cblack= [
25778       //  'font-size' -- this can be set by the project 
25779 ];
25780
25781
25782 Roo.HtmlEditorCore.swapCodes   =[ 
25783     [    8211, "--" ], 
25784     [    8212, "--" ], 
25785     [    8216,  "'" ],  
25786     [    8217, "'" ],  
25787     [    8220, '"' ],  
25788     [    8221, '"' ],  
25789     [    8226, "*" ],  
25790     [    8230, "..." ]
25791 ]; 
25792
25793     /*
25794  * - LGPL
25795  *
25796  * HtmlEditor
25797  * 
25798  */
25799
25800 /**
25801  * @class Roo.bootstrap.HtmlEditor
25802  * @extends Roo.bootstrap.TextArea
25803  * Bootstrap HtmlEditor class
25804
25805  * @constructor
25806  * Create a new HtmlEditor
25807  * @param {Object} config The config object
25808  */
25809
25810 Roo.bootstrap.HtmlEditor = function(config){
25811     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25812     if (!this.toolbars) {
25813         this.toolbars = [];
25814     }
25815     
25816     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25817     this.addEvents({
25818             /**
25819              * @event initialize
25820              * Fires when the editor is fully initialized (including the iframe)
25821              * @param {HtmlEditor} this
25822              */
25823             initialize: true,
25824             /**
25825              * @event activate
25826              * Fires when the editor is first receives the focus. Any insertion must wait
25827              * until after this event.
25828              * @param {HtmlEditor} this
25829              */
25830             activate: true,
25831              /**
25832              * @event beforesync
25833              * Fires before the textarea is updated with content from the editor iframe. Return false
25834              * to cancel the sync.
25835              * @param {HtmlEditor} this
25836              * @param {String} html
25837              */
25838             beforesync: true,
25839              /**
25840              * @event beforepush
25841              * Fires before the iframe editor is updated with content from the textarea. Return false
25842              * to cancel the push.
25843              * @param {HtmlEditor} this
25844              * @param {String} html
25845              */
25846             beforepush: true,
25847              /**
25848              * @event sync
25849              * Fires when the textarea is updated with content from the editor iframe.
25850              * @param {HtmlEditor} this
25851              * @param {String} html
25852              */
25853             sync: true,
25854              /**
25855              * @event push
25856              * Fires when the iframe editor is updated with content from the textarea.
25857              * @param {HtmlEditor} this
25858              * @param {String} html
25859              */
25860             push: true,
25861              /**
25862              * @event editmodechange
25863              * Fires when the editor switches edit modes
25864              * @param {HtmlEditor} this
25865              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25866              */
25867             editmodechange: true,
25868             /**
25869              * @event editorevent
25870              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25871              * @param {HtmlEditor} this
25872              */
25873             editorevent: true,
25874             /**
25875              * @event firstfocus
25876              * Fires when on first focus - needed by toolbars..
25877              * @param {HtmlEditor} this
25878              */
25879             firstfocus: true,
25880             /**
25881              * @event autosave
25882              * Auto save the htmlEditor value as a file into Events
25883              * @param {HtmlEditor} this
25884              */
25885             autosave: true,
25886             /**
25887              * @event savedpreview
25888              * preview the saved version of htmlEditor
25889              * @param {HtmlEditor} this
25890              */
25891             savedpreview: true
25892         });
25893 };
25894
25895
25896 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25897     
25898     
25899       /**
25900      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25901      */
25902     toolbars : false,
25903     
25904      /**
25905     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25906     */
25907     btns : [],
25908    
25909      /**
25910      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25911      *                        Roo.resizable.
25912      */
25913     resizable : false,
25914      /**
25915      * @cfg {Number} height (in pixels)
25916      */   
25917     height: 300,
25918    /**
25919      * @cfg {Number} width (in pixels)
25920      */   
25921     width: false,
25922     
25923     /**
25924      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25925      * 
25926      */
25927     stylesheets: false,
25928     
25929     // id of frame..
25930     frameId: false,
25931     
25932     // private properties
25933     validationEvent : false,
25934     deferHeight: true,
25935     initialized : false,
25936     activated : false,
25937     
25938     onFocus : Roo.emptyFn,
25939     iframePad:3,
25940     hideMode:'offsets',
25941     
25942     tbContainer : false,
25943     
25944     bodyCls : '',
25945     
25946     toolbarContainer :function() {
25947         return this.wrap.select('.x-html-editor-tb',true).first();
25948     },
25949
25950     /**
25951      * Protected method that will not generally be called directly. It
25952      * is called when the editor creates its toolbar. Override this method if you need to
25953      * add custom toolbar buttons.
25954      * @param {HtmlEditor} editor
25955      */
25956     createToolbar : function(){
25957         Roo.log('renewing');
25958         Roo.log("create toolbars");
25959         
25960         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25961         this.toolbars[0].render(this.toolbarContainer());
25962         
25963         return;
25964         
25965 //        if (!editor.toolbars || !editor.toolbars.length) {
25966 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25967 //        }
25968 //        
25969 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25970 //            editor.toolbars[i] = Roo.factory(
25971 //                    typeof(editor.toolbars[i]) == 'string' ?
25972 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25973 //                Roo.bootstrap.HtmlEditor);
25974 //            editor.toolbars[i].init(editor);
25975 //        }
25976     },
25977
25978      
25979     // private
25980     onRender : function(ct, position)
25981     {
25982        // Roo.log("Call onRender: " + this.xtype);
25983         var _t = this;
25984         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25985       
25986         this.wrap = this.inputEl().wrap({
25987             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25988         });
25989         
25990         this.editorcore.onRender(ct, position);
25991          
25992         if (this.resizable) {
25993             this.resizeEl = new Roo.Resizable(this.wrap, {
25994                 pinned : true,
25995                 wrap: true,
25996                 dynamic : true,
25997                 minHeight : this.height,
25998                 height: this.height,
25999                 handles : this.resizable,
26000                 width: this.width,
26001                 listeners : {
26002                     resize : function(r, w, h) {
26003                         _t.onResize(w,h); // -something
26004                     }
26005                 }
26006             });
26007             
26008         }
26009         this.createToolbar(this);
26010        
26011         
26012         if(!this.width && this.resizable){
26013             this.setSize(this.wrap.getSize());
26014         }
26015         if (this.resizeEl) {
26016             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26017             // should trigger onReize..
26018         }
26019         
26020     },
26021
26022     // private
26023     onResize : function(w, h)
26024     {
26025         Roo.log('resize: ' +w + ',' + h );
26026         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26027         var ew = false;
26028         var eh = false;
26029         
26030         if(this.inputEl() ){
26031             if(typeof w == 'number'){
26032                 var aw = w - this.wrap.getFrameWidth('lr');
26033                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26034                 ew = aw;
26035             }
26036             if(typeof h == 'number'){
26037                  var tbh = -11;  // fixme it needs to tool bar size!
26038                 for (var i =0; i < this.toolbars.length;i++) {
26039                     // fixme - ask toolbars for heights?
26040                     tbh += this.toolbars[i].el.getHeight();
26041                     //if (this.toolbars[i].footer) {
26042                     //    tbh += this.toolbars[i].footer.el.getHeight();
26043                     //}
26044                 }
26045               
26046                 
26047                 
26048                 
26049                 
26050                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26051                 ah -= 5; // knock a few pixes off for look..
26052                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26053                 var eh = ah;
26054             }
26055         }
26056         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26057         this.editorcore.onResize(ew,eh);
26058         
26059     },
26060
26061     /**
26062      * Toggles the editor between standard and source edit mode.
26063      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26064      */
26065     toggleSourceEdit : function(sourceEditMode)
26066     {
26067         this.editorcore.toggleSourceEdit(sourceEditMode);
26068         
26069         if(this.editorcore.sourceEditMode){
26070             Roo.log('editor - showing textarea');
26071             
26072 //            Roo.log('in');
26073 //            Roo.log(this.syncValue());
26074             this.syncValue();
26075             this.inputEl().removeClass(['hide', 'x-hidden']);
26076             this.inputEl().dom.removeAttribute('tabIndex');
26077             this.inputEl().focus();
26078         }else{
26079             Roo.log('editor - hiding textarea');
26080 //            Roo.log('out')
26081 //            Roo.log(this.pushValue()); 
26082             this.pushValue();
26083             
26084             this.inputEl().addClass(['hide', 'x-hidden']);
26085             this.inputEl().dom.setAttribute('tabIndex', -1);
26086             //this.deferFocus();
26087         }
26088          
26089         if(this.resizable){
26090             this.setSize(this.wrap.getSize());
26091         }
26092         
26093         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26094     },
26095  
26096     // private (for BoxComponent)
26097     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26098
26099     // private (for BoxComponent)
26100     getResizeEl : function(){
26101         return this.wrap;
26102     },
26103
26104     // private (for BoxComponent)
26105     getPositionEl : function(){
26106         return this.wrap;
26107     },
26108
26109     // private
26110     initEvents : function(){
26111         this.originalValue = this.getValue();
26112     },
26113
26114 //    /**
26115 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26116 //     * @method
26117 //     */
26118 //    markInvalid : Roo.emptyFn,
26119 //    /**
26120 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26121 //     * @method
26122 //     */
26123 //    clearInvalid : Roo.emptyFn,
26124
26125     setValue : function(v){
26126         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26127         this.editorcore.pushValue();
26128     },
26129
26130      
26131     // private
26132     deferFocus : function(){
26133         this.focus.defer(10, this);
26134     },
26135
26136     // doc'ed in Field
26137     focus : function(){
26138         this.editorcore.focus();
26139         
26140     },
26141       
26142
26143     // private
26144     onDestroy : function(){
26145         
26146         
26147         
26148         if(this.rendered){
26149             
26150             for (var i =0; i < this.toolbars.length;i++) {
26151                 // fixme - ask toolbars for heights?
26152                 this.toolbars[i].onDestroy();
26153             }
26154             
26155             this.wrap.dom.innerHTML = '';
26156             this.wrap.remove();
26157         }
26158     },
26159
26160     // private
26161     onFirstFocus : function(){
26162         //Roo.log("onFirstFocus");
26163         this.editorcore.onFirstFocus();
26164          for (var i =0; i < this.toolbars.length;i++) {
26165             this.toolbars[i].onFirstFocus();
26166         }
26167         
26168     },
26169     
26170     // private
26171     syncValue : function()
26172     {   
26173         this.editorcore.syncValue();
26174     },
26175     
26176     pushValue : function()
26177     {   
26178         this.editorcore.pushValue();
26179     }
26180      
26181     
26182     // hide stuff that is not compatible
26183     /**
26184      * @event blur
26185      * @hide
26186      */
26187     /**
26188      * @event change
26189      * @hide
26190      */
26191     /**
26192      * @event focus
26193      * @hide
26194      */
26195     /**
26196      * @event specialkey
26197      * @hide
26198      */
26199     /**
26200      * @cfg {String} fieldClass @hide
26201      */
26202     /**
26203      * @cfg {String} focusClass @hide
26204      */
26205     /**
26206      * @cfg {String} autoCreate @hide
26207      */
26208     /**
26209      * @cfg {String} inputType @hide
26210      */
26211      
26212     /**
26213      * @cfg {String} invalidText @hide
26214      */
26215     /**
26216      * @cfg {String} msgFx @hide
26217      */
26218     /**
26219      * @cfg {String} validateOnBlur @hide
26220      */
26221 });
26222  
26223     
26224    
26225    
26226    
26227       
26228 Roo.namespace('Roo.bootstrap.htmleditor');
26229 /**
26230  * @class Roo.bootstrap.HtmlEditorToolbar1
26231  * Basic Toolbar
26232  * 
26233  * @example
26234  * Usage:
26235  *
26236  new Roo.bootstrap.HtmlEditor({
26237     ....
26238     toolbars : [
26239         new Roo.bootstrap.HtmlEditorToolbar1({
26240             disable : { fonts: 1 , format: 1, ..., ... , ...],
26241             btns : [ .... ]
26242         })
26243     }
26244      
26245  * 
26246  * @cfg {Object} disable List of elements to disable..
26247  * @cfg {Array} btns List of additional buttons.
26248  * 
26249  * 
26250  * NEEDS Extra CSS? 
26251  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26252  */
26253  
26254 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26255 {
26256     
26257     Roo.apply(this, config);
26258     
26259     // default disabled, based on 'good practice'..
26260     this.disable = this.disable || {};
26261     Roo.applyIf(this.disable, {
26262         fontSize : true,
26263         colors : true,
26264         specialElements : true
26265     });
26266     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26267     
26268     this.editor = config.editor;
26269     this.editorcore = config.editor.editorcore;
26270     
26271     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26272     
26273     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26274     // dont call parent... till later.
26275 }
26276 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26277      
26278     bar : true,
26279     
26280     editor : false,
26281     editorcore : false,
26282     
26283     
26284     formats : [
26285         "p" ,  
26286         "h1","h2","h3","h4","h5","h6", 
26287         "pre", "code", 
26288         "abbr", "acronym", "address", "cite", "samp", "var",
26289         'div','span'
26290     ],
26291     
26292     onRender : function(ct, position)
26293     {
26294        // Roo.log("Call onRender: " + this.xtype);
26295         
26296        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26297        Roo.log(this.el);
26298        this.el.dom.style.marginBottom = '0';
26299        var _this = this;
26300        var editorcore = this.editorcore;
26301        var editor= this.editor;
26302        
26303        var children = [];
26304        var btn = function(id,cmd , toggle, handler, html){
26305        
26306             var  event = toggle ? 'toggle' : 'click';
26307        
26308             var a = {
26309                 size : 'sm',
26310                 xtype: 'Button',
26311                 xns: Roo.bootstrap,
26312                 //glyphicon : id,
26313                 fa: id,
26314                 cmd : id || cmd,
26315                 enableToggle:toggle !== false,
26316                 html : html || '',
26317                 pressed : toggle ? false : null,
26318                 listeners : {}
26319             };
26320             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26321                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26322             };
26323             children.push(a);
26324             return a;
26325        }
26326        
26327     //    var cb_box = function...
26328         
26329         var style = {
26330                 xtype: 'Button',
26331                 size : 'sm',
26332                 xns: Roo.bootstrap,
26333                 fa : 'font',
26334                 //html : 'submit'
26335                 menu : {
26336                     xtype: 'Menu',
26337                     xns: Roo.bootstrap,
26338                     items:  []
26339                 }
26340         };
26341         Roo.each(this.formats, function(f) {
26342             style.menu.items.push({
26343                 xtype :'MenuItem',
26344                 xns: Roo.bootstrap,
26345                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26346                 tagname : f,
26347                 listeners : {
26348                     click : function()
26349                     {
26350                         editorcore.insertTag(this.tagname);
26351                         editor.focus();
26352                     }
26353                 }
26354                 
26355             });
26356         });
26357         children.push(style);   
26358         
26359         btn('bold',false,true);
26360         btn('italic',false,true);
26361         btn('align-left', 'justifyleft',true);
26362         btn('align-center', 'justifycenter',true);
26363         btn('align-right' , 'justifyright',true);
26364         btn('link', false, false, function(btn) {
26365             //Roo.log("create link?");
26366             var url = prompt(this.createLinkText, this.defaultLinkValue);
26367             if(url && url != 'http:/'+'/'){
26368                 this.editorcore.relayCmd('createlink', url);
26369             }
26370         }),
26371         btn('list','insertunorderedlist',true);
26372         btn('pencil', false,true, function(btn){
26373                 Roo.log(this);
26374                 this.toggleSourceEdit(btn.pressed);
26375         });
26376         
26377         if (this.editor.btns.length > 0) {
26378             for (var i = 0; i<this.editor.btns.length; i++) {
26379                 children.push(this.editor.btns[i]);
26380             }
26381         }
26382         
26383         /*
26384         var cog = {
26385                 xtype: 'Button',
26386                 size : 'sm',
26387                 xns: Roo.bootstrap,
26388                 glyphicon : 'cog',
26389                 //html : 'submit'
26390                 menu : {
26391                     xtype: 'Menu',
26392                     xns: Roo.bootstrap,
26393                     items:  []
26394                 }
26395         };
26396         
26397         cog.menu.items.push({
26398             xtype :'MenuItem',
26399             xns: Roo.bootstrap,
26400             html : Clean styles,
26401             tagname : f,
26402             listeners : {
26403                 click : function()
26404                 {
26405                     editorcore.insertTag(this.tagname);
26406                     editor.focus();
26407                 }
26408             }
26409             
26410         });
26411        */
26412         
26413          
26414        this.xtype = 'NavSimplebar';
26415         
26416         for(var i=0;i< children.length;i++) {
26417             
26418             this.buttons.add(this.addxtypeChild(children[i]));
26419             
26420         }
26421         
26422         editor.on('editorevent', this.updateToolbar, this);
26423     },
26424     onBtnClick : function(id)
26425     {
26426        this.editorcore.relayCmd(id);
26427        this.editorcore.focus();
26428     },
26429     
26430     /**
26431      * Protected method that will not generally be called directly. It triggers
26432      * a toolbar update by reading the markup state of the current selection in the editor.
26433      */
26434     updateToolbar: function(){
26435
26436         if(!this.editorcore.activated){
26437             this.editor.onFirstFocus(); // is this neeed?
26438             return;
26439         }
26440
26441         var btns = this.buttons; 
26442         var doc = this.editorcore.doc;
26443         btns.get('bold').setActive(doc.queryCommandState('bold'));
26444         btns.get('italic').setActive(doc.queryCommandState('italic'));
26445         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26446         
26447         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26448         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26449         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26450         
26451         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26452         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26453          /*
26454         
26455         var ans = this.editorcore.getAllAncestors();
26456         if (this.formatCombo) {
26457             
26458             
26459             var store = this.formatCombo.store;
26460             this.formatCombo.setValue("");
26461             for (var i =0; i < ans.length;i++) {
26462                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26463                     // select it..
26464                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26465                     break;
26466                 }
26467             }
26468         }
26469         
26470         
26471         
26472         // hides menus... - so this cant be on a menu...
26473         Roo.bootstrap.MenuMgr.hideAll();
26474         */
26475         Roo.bootstrap.MenuMgr.hideAll();
26476         //this.editorsyncValue();
26477     },
26478     onFirstFocus: function() {
26479         this.buttons.each(function(item){
26480            item.enable();
26481         });
26482     },
26483     toggleSourceEdit : function(sourceEditMode){
26484         
26485           
26486         if(sourceEditMode){
26487             Roo.log("disabling buttons");
26488            this.buttons.each( function(item){
26489                 if(item.cmd != 'pencil'){
26490                     item.disable();
26491                 }
26492             });
26493           
26494         }else{
26495             Roo.log("enabling buttons");
26496             if(this.editorcore.initialized){
26497                 this.buttons.each( function(item){
26498                     item.enable();
26499                 });
26500             }
26501             
26502         }
26503         Roo.log("calling toggole on editor");
26504         // tell the editor that it's been pressed..
26505         this.editor.toggleSourceEdit(sourceEditMode);
26506        
26507     }
26508 });
26509
26510
26511
26512
26513  
26514 /*
26515  * - LGPL
26516  */
26517
26518 /**
26519  * @class Roo.bootstrap.Markdown
26520  * @extends Roo.bootstrap.TextArea
26521  * Bootstrap Showdown editable area
26522  * @cfg {string} content
26523  * 
26524  * @constructor
26525  * Create a new Showdown
26526  */
26527
26528 Roo.bootstrap.Markdown = function(config){
26529     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26530    
26531 };
26532
26533 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26534     
26535     editing :false,
26536     
26537     initEvents : function()
26538     {
26539         
26540         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26541         this.markdownEl = this.el.createChild({
26542             cls : 'roo-markdown-area'
26543         });
26544         this.inputEl().addClass('d-none');
26545         if (this.getValue() == '') {
26546             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26547             
26548         } else {
26549             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26550         }
26551         this.markdownEl.on('click', this.toggleTextEdit, this);
26552         this.on('blur', this.toggleTextEdit, this);
26553         this.on('specialkey', this.resizeTextArea, this);
26554     },
26555     
26556     toggleTextEdit : function()
26557     {
26558         var sh = this.markdownEl.getHeight();
26559         this.inputEl().addClass('d-none');
26560         this.markdownEl.addClass('d-none');
26561         if (!this.editing) {
26562             // show editor?
26563             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26564             this.inputEl().removeClass('d-none');
26565             this.inputEl().focus();
26566             this.editing = true;
26567             return;
26568         }
26569         // show showdown...
26570         this.updateMarkdown();
26571         this.markdownEl.removeClass('d-none');
26572         this.editing = false;
26573         return;
26574     },
26575     updateMarkdown : function()
26576     {
26577         if (this.getValue() == '') {
26578             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26579             return;
26580         }
26581  
26582         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26583     },
26584     
26585     resizeTextArea: function () {
26586         
26587         var sh = 100;
26588         Roo.log([sh, this.getValue().split("\n").length * 30]);
26589         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26590     },
26591     setValue : function(val)
26592     {
26593         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26594         if (!this.editing) {
26595             this.updateMarkdown();
26596         }
26597         
26598     },
26599     focus : function()
26600     {
26601         if (!this.editing) {
26602             this.toggleTextEdit();
26603         }
26604         
26605     }
26606
26607
26608 });
26609 /**
26610  * @class Roo.bootstrap.Table.AbstractSelectionModel
26611  * @extends Roo.util.Observable
26612  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26613  * implemented by descendant classes.  This class should not be directly instantiated.
26614  * @constructor
26615  */
26616 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26617     this.locked = false;
26618     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26619 };
26620
26621
26622 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26623     /** @ignore Called by the grid automatically. Do not call directly. */
26624     init : function(grid){
26625         this.grid = grid;
26626         this.initEvents();
26627     },
26628
26629     /**
26630      * Locks the selections.
26631      */
26632     lock : function(){
26633         this.locked = true;
26634     },
26635
26636     /**
26637      * Unlocks the selections.
26638      */
26639     unlock : function(){
26640         this.locked = false;
26641     },
26642
26643     /**
26644      * Returns true if the selections are locked.
26645      * @return {Boolean}
26646      */
26647     isLocked : function(){
26648         return this.locked;
26649     },
26650     
26651     
26652     initEvents : function ()
26653     {
26654         
26655     }
26656 });
26657 /**
26658  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26659  * @class Roo.bootstrap.Table.RowSelectionModel
26660  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26661  * It supports multiple selections and keyboard selection/navigation. 
26662  * @constructor
26663  * @param {Object} config
26664  */
26665
26666 Roo.bootstrap.Table.RowSelectionModel = function(config){
26667     Roo.apply(this, config);
26668     this.selections = new Roo.util.MixedCollection(false, function(o){
26669         return o.id;
26670     });
26671
26672     this.last = false;
26673     this.lastActive = false;
26674
26675     this.addEvents({
26676         /**
26677              * @event selectionchange
26678              * Fires when the selection changes
26679              * @param {SelectionModel} this
26680              */
26681             "selectionchange" : true,
26682         /**
26683              * @event afterselectionchange
26684              * Fires after the selection changes (eg. by key press or clicking)
26685              * @param {SelectionModel} this
26686              */
26687             "afterselectionchange" : true,
26688         /**
26689              * @event beforerowselect
26690              * Fires when a row is selected being selected, return false to cancel.
26691              * @param {SelectionModel} this
26692              * @param {Number} rowIndex The selected index
26693              * @param {Boolean} keepExisting False if other selections will be cleared
26694              */
26695             "beforerowselect" : true,
26696         /**
26697              * @event rowselect
26698              * Fires when a row is selected.
26699              * @param {SelectionModel} this
26700              * @param {Number} rowIndex The selected index
26701              * @param {Roo.data.Record} r The record
26702              */
26703             "rowselect" : true,
26704         /**
26705              * @event rowdeselect
26706              * Fires when a row is deselected.
26707              * @param {SelectionModel} this
26708              * @param {Number} rowIndex The selected index
26709              */
26710         "rowdeselect" : true
26711     });
26712     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26713     this.locked = false;
26714  };
26715
26716 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26717     /**
26718      * @cfg {Boolean} singleSelect
26719      * True to allow selection of only one row at a time (defaults to false)
26720      */
26721     singleSelect : false,
26722
26723     // private
26724     initEvents : function()
26725     {
26726
26727         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26728         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26729         //}else{ // allow click to work like normal
26730          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26731         //}
26732         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26733         this.grid.on("rowclick", this.handleMouseDown, this);
26734         
26735         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26736             "up" : function(e){
26737                 if(!e.shiftKey){
26738                     this.selectPrevious(e.shiftKey);
26739                 }else if(this.last !== false && this.lastActive !== false){
26740                     var last = this.last;
26741                     this.selectRange(this.last,  this.lastActive-1);
26742                     this.grid.getView().focusRow(this.lastActive);
26743                     if(last !== false){
26744                         this.last = last;
26745                     }
26746                 }else{
26747                     this.selectFirstRow();
26748                 }
26749                 this.fireEvent("afterselectionchange", this);
26750             },
26751             "down" : function(e){
26752                 if(!e.shiftKey){
26753                     this.selectNext(e.shiftKey);
26754                 }else if(this.last !== false && this.lastActive !== false){
26755                     var last = this.last;
26756                     this.selectRange(this.last,  this.lastActive+1);
26757                     this.grid.getView().focusRow(this.lastActive);
26758                     if(last !== false){
26759                         this.last = last;
26760                     }
26761                 }else{
26762                     this.selectFirstRow();
26763                 }
26764                 this.fireEvent("afterselectionchange", this);
26765             },
26766             scope: this
26767         });
26768         this.grid.store.on('load', function(){
26769             this.selections.clear();
26770         },this);
26771         /*
26772         var view = this.grid.view;
26773         view.on("refresh", this.onRefresh, this);
26774         view.on("rowupdated", this.onRowUpdated, this);
26775         view.on("rowremoved", this.onRemove, this);
26776         */
26777     },
26778
26779     // private
26780     onRefresh : function()
26781     {
26782         var ds = this.grid.store, i, v = this.grid.view;
26783         var s = this.selections;
26784         s.each(function(r){
26785             if((i = ds.indexOfId(r.id)) != -1){
26786                 v.onRowSelect(i);
26787             }else{
26788                 s.remove(r);
26789             }
26790         });
26791     },
26792
26793     // private
26794     onRemove : function(v, index, r){
26795         this.selections.remove(r);
26796     },
26797
26798     // private
26799     onRowUpdated : function(v, index, r){
26800         if(this.isSelected(r)){
26801             v.onRowSelect(index);
26802         }
26803     },
26804
26805     /**
26806      * Select records.
26807      * @param {Array} records The records to select
26808      * @param {Boolean} keepExisting (optional) True to keep existing selections
26809      */
26810     selectRecords : function(records, keepExisting)
26811     {
26812         if(!keepExisting){
26813             this.clearSelections();
26814         }
26815             var ds = this.grid.store;
26816         for(var i = 0, len = records.length; i < len; i++){
26817             this.selectRow(ds.indexOf(records[i]), true);
26818         }
26819     },
26820
26821     /**
26822      * Gets the number of selected rows.
26823      * @return {Number}
26824      */
26825     getCount : function(){
26826         return this.selections.length;
26827     },
26828
26829     /**
26830      * Selects the first row in the grid.
26831      */
26832     selectFirstRow : function(){
26833         this.selectRow(0);
26834     },
26835
26836     /**
26837      * Select the last row.
26838      * @param {Boolean} keepExisting (optional) True to keep existing selections
26839      */
26840     selectLastRow : function(keepExisting){
26841         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26842         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26843     },
26844
26845     /**
26846      * Selects the row immediately following the last selected row.
26847      * @param {Boolean} keepExisting (optional) True to keep existing selections
26848      */
26849     selectNext : function(keepExisting)
26850     {
26851             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26852             this.selectRow(this.last+1, keepExisting);
26853             this.grid.getView().focusRow(this.last);
26854         }
26855     },
26856
26857     /**
26858      * Selects the row that precedes the last selected row.
26859      * @param {Boolean} keepExisting (optional) True to keep existing selections
26860      */
26861     selectPrevious : function(keepExisting){
26862         if(this.last){
26863             this.selectRow(this.last-1, keepExisting);
26864             this.grid.getView().focusRow(this.last);
26865         }
26866     },
26867
26868     /**
26869      * Returns the selected records
26870      * @return {Array} Array of selected records
26871      */
26872     getSelections : function(){
26873         return [].concat(this.selections.items);
26874     },
26875
26876     /**
26877      * Returns the first selected record.
26878      * @return {Record}
26879      */
26880     getSelected : function(){
26881         return this.selections.itemAt(0);
26882     },
26883
26884
26885     /**
26886      * Clears all selections.
26887      */
26888     clearSelections : function(fast)
26889     {
26890         if(this.locked) {
26891             return;
26892         }
26893         if(fast !== true){
26894                 var ds = this.grid.store;
26895             var s = this.selections;
26896             s.each(function(r){
26897                 this.deselectRow(ds.indexOfId(r.id));
26898             }, this);
26899             s.clear();
26900         }else{
26901             this.selections.clear();
26902         }
26903         this.last = false;
26904     },
26905
26906
26907     /**
26908      * Selects all rows.
26909      */
26910     selectAll : function(){
26911         if(this.locked) {
26912             return;
26913         }
26914         this.selections.clear();
26915         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26916             this.selectRow(i, true);
26917         }
26918     },
26919
26920     /**
26921      * Returns True if there is a selection.
26922      * @return {Boolean}
26923      */
26924     hasSelection : function(){
26925         return this.selections.length > 0;
26926     },
26927
26928     /**
26929      * Returns True if the specified row is selected.
26930      * @param {Number/Record} record The record or index of the record to check
26931      * @return {Boolean}
26932      */
26933     isSelected : function(index){
26934             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26935         return (r && this.selections.key(r.id) ? true : false);
26936     },
26937
26938     /**
26939      * Returns True if the specified record id is selected.
26940      * @param {String} id The id of record to check
26941      * @return {Boolean}
26942      */
26943     isIdSelected : function(id){
26944         return (this.selections.key(id) ? true : false);
26945     },
26946
26947
26948     // private
26949     handleMouseDBClick : function(e, t){
26950         
26951     },
26952     // private
26953     handleMouseDown : function(e, t)
26954     {
26955             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26956         if(this.isLocked() || rowIndex < 0 ){
26957             return;
26958         };
26959         if(e.shiftKey && this.last !== false){
26960             var last = this.last;
26961             this.selectRange(last, rowIndex, e.ctrlKey);
26962             this.last = last; // reset the last
26963             t.focus();
26964     
26965         }else{
26966             var isSelected = this.isSelected(rowIndex);
26967             //Roo.log("select row:" + rowIndex);
26968             if(isSelected){
26969                 this.deselectRow(rowIndex);
26970             } else {
26971                         this.selectRow(rowIndex, true);
26972             }
26973     
26974             /*
26975                 if(e.button !== 0 && isSelected){
26976                 alert('rowIndex 2: ' + rowIndex);
26977                     view.focusRow(rowIndex);
26978                 }else if(e.ctrlKey && isSelected){
26979                     this.deselectRow(rowIndex);
26980                 }else if(!isSelected){
26981                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26982                     view.focusRow(rowIndex);
26983                 }
26984             */
26985         }
26986         this.fireEvent("afterselectionchange", this);
26987     },
26988     // private
26989     handleDragableRowClick :  function(grid, rowIndex, e) 
26990     {
26991         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26992             this.selectRow(rowIndex, false);
26993             grid.view.focusRow(rowIndex);
26994              this.fireEvent("afterselectionchange", this);
26995         }
26996     },
26997     
26998     /**
26999      * Selects multiple rows.
27000      * @param {Array} rows Array of the indexes of the row to select
27001      * @param {Boolean} keepExisting (optional) True to keep existing selections
27002      */
27003     selectRows : function(rows, keepExisting){
27004         if(!keepExisting){
27005             this.clearSelections();
27006         }
27007         for(var i = 0, len = rows.length; i < len; i++){
27008             this.selectRow(rows[i], true);
27009         }
27010     },
27011
27012     /**
27013      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27014      * @param {Number} startRow The index of the first row in the range
27015      * @param {Number} endRow The index of the last row in the range
27016      * @param {Boolean} keepExisting (optional) True to retain existing selections
27017      */
27018     selectRange : function(startRow, endRow, keepExisting){
27019         if(this.locked) {
27020             return;
27021         }
27022         if(!keepExisting){
27023             this.clearSelections();
27024         }
27025         if(startRow <= endRow){
27026             for(var i = startRow; i <= endRow; i++){
27027                 this.selectRow(i, true);
27028             }
27029         }else{
27030             for(var i = startRow; i >= endRow; i--){
27031                 this.selectRow(i, true);
27032             }
27033         }
27034     },
27035
27036     /**
27037      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27038      * @param {Number} startRow The index of the first row in the range
27039      * @param {Number} endRow The index of the last row in the range
27040      */
27041     deselectRange : function(startRow, endRow, preventViewNotify){
27042         if(this.locked) {
27043             return;
27044         }
27045         for(var i = startRow; i <= endRow; i++){
27046             this.deselectRow(i, preventViewNotify);
27047         }
27048     },
27049
27050     /**
27051      * Selects a row.
27052      * @param {Number} row The index of the row to select
27053      * @param {Boolean} keepExisting (optional) True to keep existing selections
27054      */
27055     selectRow : function(index, keepExisting, preventViewNotify)
27056     {
27057             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27058             return;
27059         }
27060         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27061             if(!keepExisting || this.singleSelect){
27062                 this.clearSelections();
27063             }
27064             
27065             var r = this.grid.store.getAt(index);
27066             //console.log('selectRow - record id :' + r.id);
27067             
27068             this.selections.add(r);
27069             this.last = this.lastActive = index;
27070             if(!preventViewNotify){
27071                 var proxy = new Roo.Element(
27072                                 this.grid.getRowDom(index)
27073                 );
27074                 proxy.addClass('bg-info info');
27075             }
27076             this.fireEvent("rowselect", this, index, r);
27077             this.fireEvent("selectionchange", this);
27078         }
27079     },
27080
27081     /**
27082      * Deselects a row.
27083      * @param {Number} row The index of the row to deselect
27084      */
27085     deselectRow : function(index, preventViewNotify)
27086     {
27087         if(this.locked) {
27088             return;
27089         }
27090         if(this.last == index){
27091             this.last = false;
27092         }
27093         if(this.lastActive == index){
27094             this.lastActive = false;
27095         }
27096         
27097         var r = this.grid.store.getAt(index);
27098         if (!r) {
27099             return;
27100         }
27101         
27102         this.selections.remove(r);
27103         //.console.log('deselectRow - record id :' + r.id);
27104         if(!preventViewNotify){
27105         
27106             var proxy = new Roo.Element(
27107                 this.grid.getRowDom(index)
27108             );
27109             proxy.removeClass('bg-info info');
27110         }
27111         this.fireEvent("rowdeselect", this, index);
27112         this.fireEvent("selectionchange", this);
27113     },
27114
27115     // private
27116     restoreLast : function(){
27117         if(this._last){
27118             this.last = this._last;
27119         }
27120     },
27121
27122     // private
27123     acceptsNav : function(row, col, cm){
27124         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27125     },
27126
27127     // private
27128     onEditorKey : function(field, e){
27129         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27130         if(k == e.TAB){
27131             e.stopEvent();
27132             ed.completeEdit();
27133             if(e.shiftKey){
27134                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27135             }else{
27136                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27137             }
27138         }else if(k == e.ENTER && !e.ctrlKey){
27139             e.stopEvent();
27140             ed.completeEdit();
27141             if(e.shiftKey){
27142                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27143             }else{
27144                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27145             }
27146         }else if(k == e.ESC){
27147             ed.cancelEdit();
27148         }
27149         if(newCell){
27150             g.startEditing(newCell[0], newCell[1]);
27151         }
27152     }
27153 });
27154 /*
27155  * Based on:
27156  * Ext JS Library 1.1.1
27157  * Copyright(c) 2006-2007, Ext JS, LLC.
27158  *
27159  * Originally Released Under LGPL - original licence link has changed is not relivant.
27160  *
27161  * Fork - LGPL
27162  * <script type="text/javascript">
27163  */
27164  
27165 /**
27166  * @class Roo.bootstrap.PagingToolbar
27167  * @extends Roo.bootstrap.NavSimplebar
27168  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27169  * @constructor
27170  * Create a new PagingToolbar
27171  * @param {Object} config The config object
27172  * @param {Roo.data.Store} store
27173  */
27174 Roo.bootstrap.PagingToolbar = function(config)
27175 {
27176     // old args format still supported... - xtype is prefered..
27177         // created from xtype...
27178     
27179     this.ds = config.dataSource;
27180     
27181     if (config.store && !this.ds) {
27182         this.store= Roo.factory(config.store, Roo.data);
27183         this.ds = this.store;
27184         this.ds.xmodule = this.xmodule || false;
27185     }
27186     
27187     this.toolbarItems = [];
27188     if (config.items) {
27189         this.toolbarItems = config.items;
27190     }
27191     
27192     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27193     
27194     this.cursor = 0;
27195     
27196     if (this.ds) { 
27197         this.bind(this.ds);
27198     }
27199     
27200     if (Roo.bootstrap.version == 4) {
27201         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27202     } else {
27203         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27204     }
27205     
27206 };
27207
27208 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27209     /**
27210      * @cfg {Roo.data.Store} dataSource
27211      * The underlying data store providing the paged data
27212      */
27213     /**
27214      * @cfg {String/HTMLElement/Element} container
27215      * container The id or element that will contain the toolbar
27216      */
27217     /**
27218      * @cfg {Boolean} displayInfo
27219      * True to display the displayMsg (defaults to false)
27220      */
27221     /**
27222      * @cfg {Number} pageSize
27223      * The number of records to display per page (defaults to 20)
27224      */
27225     pageSize: 20,
27226     /**
27227      * @cfg {String} displayMsg
27228      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27229      */
27230     displayMsg : 'Displaying {0} - {1} of {2}',
27231     /**
27232      * @cfg {String} emptyMsg
27233      * The message to display when no records are found (defaults to "No data to display")
27234      */
27235     emptyMsg : 'No data to display',
27236     /**
27237      * Customizable piece of the default paging text (defaults to "Page")
27238      * @type String
27239      */
27240     beforePageText : "Page",
27241     /**
27242      * Customizable piece of the default paging text (defaults to "of %0")
27243      * @type String
27244      */
27245     afterPageText : "of {0}",
27246     /**
27247      * Customizable piece of the default paging text (defaults to "First Page")
27248      * @type String
27249      */
27250     firstText : "First Page",
27251     /**
27252      * Customizable piece of the default paging text (defaults to "Previous Page")
27253      * @type String
27254      */
27255     prevText : "Previous Page",
27256     /**
27257      * Customizable piece of the default paging text (defaults to "Next Page")
27258      * @type String
27259      */
27260     nextText : "Next Page",
27261     /**
27262      * Customizable piece of the default paging text (defaults to "Last Page")
27263      * @type String
27264      */
27265     lastText : "Last Page",
27266     /**
27267      * Customizable piece of the default paging text (defaults to "Refresh")
27268      * @type String
27269      */
27270     refreshText : "Refresh",
27271
27272     buttons : false,
27273     // private
27274     onRender : function(ct, position) 
27275     {
27276         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27277         this.navgroup.parentId = this.id;
27278         this.navgroup.onRender(this.el, null);
27279         // add the buttons to the navgroup
27280         
27281         if(this.displayInfo){
27282             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27283             this.displayEl = this.el.select('.x-paging-info', true).first();
27284 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27285 //            this.displayEl = navel.el.select('span',true).first();
27286         }
27287         
27288         var _this = this;
27289         
27290         if(this.buttons){
27291             Roo.each(_this.buttons, function(e){ // this might need to use render????
27292                Roo.factory(e).render(_this.el);
27293             });
27294         }
27295             
27296         Roo.each(_this.toolbarItems, function(e) {
27297             _this.navgroup.addItem(e);
27298         });
27299         
27300         
27301         this.first = this.navgroup.addItem({
27302             tooltip: this.firstText,
27303             cls: "prev btn-outline-secondary",
27304             html : ' <i class="fa fa-step-backward"></i>',
27305             disabled: true,
27306             preventDefault: true,
27307             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27308         });
27309         
27310         this.prev =  this.navgroup.addItem({
27311             tooltip: this.prevText,
27312             cls: "prev btn-outline-secondary",
27313             html : ' <i class="fa fa-backward"></i>',
27314             disabled: true,
27315             preventDefault: true,
27316             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27317         });
27318     //this.addSeparator();
27319         
27320         
27321         var field = this.navgroup.addItem( {
27322             tagtype : 'span',
27323             cls : 'x-paging-position  btn-outline-secondary',
27324              disabled: true,
27325             html : this.beforePageText  +
27326                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27327                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27328          } ); //?? escaped?
27329         
27330         this.field = field.el.select('input', true).first();
27331         this.field.on("keydown", this.onPagingKeydown, this);
27332         this.field.on("focus", function(){this.dom.select();});
27333     
27334     
27335         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27336         //this.field.setHeight(18);
27337         //this.addSeparator();
27338         this.next = this.navgroup.addItem({
27339             tooltip: this.nextText,
27340             cls: "next btn-outline-secondary",
27341             html : ' <i class="fa fa-forward"></i>',
27342             disabled: true,
27343             preventDefault: true,
27344             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27345         });
27346         this.last = this.navgroup.addItem({
27347             tooltip: this.lastText,
27348             html : ' <i class="fa fa-step-forward"></i>',
27349             cls: "next btn-outline-secondary",
27350             disabled: true,
27351             preventDefault: true,
27352             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27353         });
27354     //this.addSeparator();
27355         this.loading = this.navgroup.addItem({
27356             tooltip: this.refreshText,
27357             cls: "btn-outline-secondary",
27358             html : ' <i class="fa fa-refresh"></i>',
27359             preventDefault: true,
27360             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27361         });
27362         
27363     },
27364
27365     // private
27366     updateInfo : function(){
27367         if(this.displayEl){
27368             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27369             var msg = count == 0 ?
27370                 this.emptyMsg :
27371                 String.format(
27372                     this.displayMsg,
27373                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27374                 );
27375             this.displayEl.update(msg);
27376         }
27377     },
27378
27379     // private
27380     onLoad : function(ds, r, o)
27381     {
27382         this.cursor = o.params && o.params.start ? o.params.start : 0;
27383         
27384         var d = this.getPageData(),
27385             ap = d.activePage,
27386             ps = d.pages;
27387         
27388         
27389         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27390         this.field.dom.value = ap;
27391         this.first.setDisabled(ap == 1);
27392         this.prev.setDisabled(ap == 1);
27393         this.next.setDisabled(ap == ps);
27394         this.last.setDisabled(ap == ps);
27395         this.loading.enable();
27396         this.updateInfo();
27397     },
27398
27399     // private
27400     getPageData : function(){
27401         var total = this.ds.getTotalCount();
27402         return {
27403             total : total,
27404             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27405             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27406         };
27407     },
27408
27409     // private
27410     onLoadError : function(){
27411         this.loading.enable();
27412     },
27413
27414     // private
27415     onPagingKeydown : function(e){
27416         var k = e.getKey();
27417         var d = this.getPageData();
27418         if(k == e.RETURN){
27419             var v = this.field.dom.value, pageNum;
27420             if(!v || isNaN(pageNum = parseInt(v, 10))){
27421                 this.field.dom.value = d.activePage;
27422                 return;
27423             }
27424             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27425             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27426             e.stopEvent();
27427         }
27428         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))
27429         {
27430           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27431           this.field.dom.value = pageNum;
27432           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27433           e.stopEvent();
27434         }
27435         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27436         {
27437           var v = this.field.dom.value, pageNum; 
27438           var increment = (e.shiftKey) ? 10 : 1;
27439           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27440                 increment *= -1;
27441           }
27442           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27443             this.field.dom.value = d.activePage;
27444             return;
27445           }
27446           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27447           {
27448             this.field.dom.value = parseInt(v, 10) + increment;
27449             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27450             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27451           }
27452           e.stopEvent();
27453         }
27454     },
27455
27456     // private
27457     beforeLoad : function(){
27458         if(this.loading){
27459             this.loading.disable();
27460         }
27461     },
27462
27463     // private
27464     onClick : function(which){
27465         
27466         var ds = this.ds;
27467         if (!ds) {
27468             return;
27469         }
27470         
27471         switch(which){
27472             case "first":
27473                 ds.load({params:{start: 0, limit: this.pageSize}});
27474             break;
27475             case "prev":
27476                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27477             break;
27478             case "next":
27479                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27480             break;
27481             case "last":
27482                 var total = ds.getTotalCount();
27483                 var extra = total % this.pageSize;
27484                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27485                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27486             break;
27487             case "refresh":
27488                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27489             break;
27490         }
27491     },
27492
27493     /**
27494      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27495      * @param {Roo.data.Store} store The data store to unbind
27496      */
27497     unbind : function(ds){
27498         ds.un("beforeload", this.beforeLoad, this);
27499         ds.un("load", this.onLoad, this);
27500         ds.un("loadexception", this.onLoadError, this);
27501         ds.un("remove", this.updateInfo, this);
27502         ds.un("add", this.updateInfo, this);
27503         this.ds = undefined;
27504     },
27505
27506     /**
27507      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27508      * @param {Roo.data.Store} store The data store to bind
27509      */
27510     bind : function(ds){
27511         ds.on("beforeload", this.beforeLoad, this);
27512         ds.on("load", this.onLoad, this);
27513         ds.on("loadexception", this.onLoadError, this);
27514         ds.on("remove", this.updateInfo, this);
27515         ds.on("add", this.updateInfo, this);
27516         this.ds = ds;
27517     }
27518 });/*
27519  * - LGPL
27520  *
27521  * element
27522  * 
27523  */
27524
27525 /**
27526  * @class Roo.bootstrap.MessageBar
27527  * @extends Roo.bootstrap.Component
27528  * Bootstrap MessageBar class
27529  * @cfg {String} html contents of the MessageBar
27530  * @cfg {String} weight (info | success | warning | danger) default info
27531  * @cfg {String} beforeClass insert the bar before the given class
27532  * @cfg {Boolean} closable (true | false) default false
27533  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27534  * 
27535  * @constructor
27536  * Create a new Element
27537  * @param {Object} config The config object
27538  */
27539
27540 Roo.bootstrap.MessageBar = function(config){
27541     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27542 };
27543
27544 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27545     
27546     html: '',
27547     weight: 'info',
27548     closable: false,
27549     fixed: false,
27550     beforeClass: 'bootstrap-sticky-wrap',
27551     
27552     getAutoCreate : function(){
27553         
27554         var cfg = {
27555             tag: 'div',
27556             cls: 'alert alert-dismissable alert-' + this.weight,
27557             cn: [
27558                 {
27559                     tag: 'span',
27560                     cls: 'message',
27561                     html: this.html || ''
27562                 }
27563             ]
27564         };
27565         
27566         if(this.fixed){
27567             cfg.cls += ' alert-messages-fixed';
27568         }
27569         
27570         if(this.closable){
27571             cfg.cn.push({
27572                 tag: 'button',
27573                 cls: 'close',
27574                 html: 'x'
27575             });
27576         }
27577         
27578         return cfg;
27579     },
27580     
27581     onRender : function(ct, position)
27582     {
27583         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27584         
27585         if(!this.el){
27586             var cfg = Roo.apply({},  this.getAutoCreate());
27587             cfg.id = Roo.id();
27588             
27589             if (this.cls) {
27590                 cfg.cls += ' ' + this.cls;
27591             }
27592             if (this.style) {
27593                 cfg.style = this.style;
27594             }
27595             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27596             
27597             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27598         }
27599         
27600         this.el.select('>button.close').on('click', this.hide, this);
27601         
27602     },
27603     
27604     show : function()
27605     {
27606         if (!this.rendered) {
27607             this.render();
27608         }
27609         
27610         this.el.show();
27611         
27612         this.fireEvent('show', this);
27613         
27614     },
27615     
27616     hide : function()
27617     {
27618         if (!this.rendered) {
27619             this.render();
27620         }
27621         
27622         this.el.hide();
27623         
27624         this.fireEvent('hide', this);
27625     },
27626     
27627     update : function()
27628     {
27629 //        var e = this.el.dom.firstChild;
27630 //        
27631 //        if(this.closable){
27632 //            e = e.nextSibling;
27633 //        }
27634 //        
27635 //        e.data = this.html || '';
27636
27637         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27638     }
27639    
27640 });
27641
27642  
27643
27644      /*
27645  * - LGPL
27646  *
27647  * Graph
27648  * 
27649  */
27650
27651
27652 /**
27653  * @class Roo.bootstrap.Graph
27654  * @extends Roo.bootstrap.Component
27655  * Bootstrap Graph class
27656 > Prameters
27657  -sm {number} sm 4
27658  -md {number} md 5
27659  @cfg {String} graphtype  bar | vbar | pie
27660  @cfg {number} g_x coodinator | centre x (pie)
27661  @cfg {number} g_y coodinator | centre y (pie)
27662  @cfg {number} g_r radius (pie)
27663  @cfg {number} g_height height of the chart (respected by all elements in the set)
27664  @cfg {number} g_width width of the chart (respected by all elements in the set)
27665  @cfg {Object} title The title of the chart
27666     
27667  -{Array}  values
27668  -opts (object) options for the chart 
27669      o {
27670      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27671      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27672      o vgutter (number)
27673      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.
27674      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27675      o to
27676      o stretch (boolean)
27677      o }
27678  -opts (object) options for the pie
27679      o{
27680      o cut
27681      o startAngle (number)
27682      o endAngle (number)
27683      } 
27684  *
27685  * @constructor
27686  * Create a new Input
27687  * @param {Object} config The config object
27688  */
27689
27690 Roo.bootstrap.Graph = function(config){
27691     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27692     
27693     this.addEvents({
27694         // img events
27695         /**
27696          * @event click
27697          * The img click event for the img.
27698          * @param {Roo.EventObject} e
27699          */
27700         "click" : true
27701     });
27702 };
27703
27704 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27705     
27706     sm: 4,
27707     md: 5,
27708     graphtype: 'bar',
27709     g_height: 250,
27710     g_width: 400,
27711     g_x: 50,
27712     g_y: 50,
27713     g_r: 30,
27714     opts:{
27715         //g_colors: this.colors,
27716         g_type: 'soft',
27717         g_gutter: '20%'
27718
27719     },
27720     title : false,
27721
27722     getAutoCreate : function(){
27723         
27724         var cfg = {
27725             tag: 'div',
27726             html : null
27727         };
27728         
27729         
27730         return  cfg;
27731     },
27732
27733     onRender : function(ct,position){
27734         
27735         
27736         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27737         
27738         if (typeof(Raphael) == 'undefined') {
27739             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27740             return;
27741         }
27742         
27743         this.raphael = Raphael(this.el.dom);
27744         
27745                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27746                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27747                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27748                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27749                 /*
27750                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27751                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27752                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27753                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27754                 
27755                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27756                 r.barchart(330, 10, 300, 220, data1);
27757                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27758                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27759                 */
27760                 
27761                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27762                 // r.barchart(30, 30, 560, 250,  xdata, {
27763                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27764                 //     axis : "0 0 1 1",
27765                 //     axisxlabels :  xdata
27766                 //     //yvalues : cols,
27767                    
27768                 // });
27769 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27770 //        
27771 //        this.load(null,xdata,{
27772 //                axis : "0 0 1 1",
27773 //                axisxlabels :  xdata
27774 //                });
27775
27776     },
27777
27778     load : function(graphtype,xdata,opts)
27779     {
27780         this.raphael.clear();
27781         if(!graphtype) {
27782             graphtype = this.graphtype;
27783         }
27784         if(!opts){
27785             opts = this.opts;
27786         }
27787         var r = this.raphael,
27788             fin = function () {
27789                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27790             },
27791             fout = function () {
27792                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27793             },
27794             pfin = function() {
27795                 this.sector.stop();
27796                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27797
27798                 if (this.label) {
27799                     this.label[0].stop();
27800                     this.label[0].attr({ r: 7.5 });
27801                     this.label[1].attr({ "font-weight": 800 });
27802                 }
27803             },
27804             pfout = function() {
27805                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27806
27807                 if (this.label) {
27808                     this.label[0].animate({ r: 5 }, 500, "bounce");
27809                     this.label[1].attr({ "font-weight": 400 });
27810                 }
27811             };
27812
27813         switch(graphtype){
27814             case 'bar':
27815                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27816                 break;
27817             case 'hbar':
27818                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27819                 break;
27820             case 'pie':
27821 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27822 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27823 //            
27824                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27825                 
27826                 break;
27827
27828         }
27829         
27830         if(this.title){
27831             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27832         }
27833         
27834     },
27835     
27836     setTitle: function(o)
27837     {
27838         this.title = o;
27839     },
27840     
27841     initEvents: function() {
27842         
27843         if(!this.href){
27844             this.el.on('click', this.onClick, this);
27845         }
27846     },
27847     
27848     onClick : function(e)
27849     {
27850         Roo.log('img onclick');
27851         this.fireEvent('click', this, e);
27852     }
27853    
27854 });
27855
27856  
27857 /*
27858  * - LGPL
27859  *
27860  * numberBox
27861  * 
27862  */
27863 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27864
27865 /**
27866  * @class Roo.bootstrap.dash.NumberBox
27867  * @extends Roo.bootstrap.Component
27868  * Bootstrap NumberBox class
27869  * @cfg {String} headline Box headline
27870  * @cfg {String} content Box content
27871  * @cfg {String} icon Box icon
27872  * @cfg {String} footer Footer text
27873  * @cfg {String} fhref Footer href
27874  * 
27875  * @constructor
27876  * Create a new NumberBox
27877  * @param {Object} config The config object
27878  */
27879
27880
27881 Roo.bootstrap.dash.NumberBox = function(config){
27882     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27883     
27884 };
27885
27886 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27887     
27888     headline : '',
27889     content : '',
27890     icon : '',
27891     footer : '',
27892     fhref : '',
27893     ficon : '',
27894     
27895     getAutoCreate : function(){
27896         
27897         var cfg = {
27898             tag : 'div',
27899             cls : 'small-box ',
27900             cn : [
27901                 {
27902                     tag : 'div',
27903                     cls : 'inner',
27904                     cn :[
27905                         {
27906                             tag : 'h3',
27907                             cls : 'roo-headline',
27908                             html : this.headline
27909                         },
27910                         {
27911                             tag : 'p',
27912                             cls : 'roo-content',
27913                             html : this.content
27914                         }
27915                     ]
27916                 }
27917             ]
27918         };
27919         
27920         if(this.icon){
27921             cfg.cn.push({
27922                 tag : 'div',
27923                 cls : 'icon',
27924                 cn :[
27925                     {
27926                         tag : 'i',
27927                         cls : 'ion ' + this.icon
27928                     }
27929                 ]
27930             });
27931         }
27932         
27933         if(this.footer){
27934             var footer = {
27935                 tag : 'a',
27936                 cls : 'small-box-footer',
27937                 href : this.fhref || '#',
27938                 html : this.footer
27939             };
27940             
27941             cfg.cn.push(footer);
27942             
27943         }
27944         
27945         return  cfg;
27946     },
27947
27948     onRender : function(ct,position){
27949         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27950
27951
27952        
27953                 
27954     },
27955
27956     setHeadline: function (value)
27957     {
27958         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27959     },
27960     
27961     setFooter: function (value, href)
27962     {
27963         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27964         
27965         if(href){
27966             this.el.select('a.small-box-footer',true).first().attr('href', href);
27967         }
27968         
27969     },
27970
27971     setContent: function (value)
27972     {
27973         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27974     },
27975
27976     initEvents: function() 
27977     {   
27978         
27979     }
27980     
27981 });
27982
27983  
27984 /*
27985  * - LGPL
27986  *
27987  * TabBox
27988  * 
27989  */
27990 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27991
27992 /**
27993  * @class Roo.bootstrap.dash.TabBox
27994  * @extends Roo.bootstrap.Component
27995  * Bootstrap TabBox class
27996  * @cfg {String} title Title of the TabBox
27997  * @cfg {String} icon Icon of the TabBox
27998  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27999  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28000  * 
28001  * @constructor
28002  * Create a new TabBox
28003  * @param {Object} config The config object
28004  */
28005
28006
28007 Roo.bootstrap.dash.TabBox = function(config){
28008     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28009     this.addEvents({
28010         // raw events
28011         /**
28012          * @event addpane
28013          * When a pane is added
28014          * @param {Roo.bootstrap.dash.TabPane} pane
28015          */
28016         "addpane" : true,
28017         /**
28018          * @event activatepane
28019          * When a pane is activated
28020          * @param {Roo.bootstrap.dash.TabPane} pane
28021          */
28022         "activatepane" : true
28023         
28024          
28025     });
28026     
28027     this.panes = [];
28028 };
28029
28030 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28031
28032     title : '',
28033     icon : false,
28034     showtabs : true,
28035     tabScrollable : false,
28036     
28037     getChildContainer : function()
28038     {
28039         return this.el.select('.tab-content', true).first();
28040     },
28041     
28042     getAutoCreate : function(){
28043         
28044         var header = {
28045             tag: 'li',
28046             cls: 'pull-left header',
28047             html: this.title,
28048             cn : []
28049         };
28050         
28051         if(this.icon){
28052             header.cn.push({
28053                 tag: 'i',
28054                 cls: 'fa ' + this.icon
28055             });
28056         }
28057         
28058         var h = {
28059             tag: 'ul',
28060             cls: 'nav nav-tabs pull-right',
28061             cn: [
28062                 header
28063             ]
28064         };
28065         
28066         if(this.tabScrollable){
28067             h = {
28068                 tag: 'div',
28069                 cls: 'tab-header',
28070                 cn: [
28071                     {
28072                         tag: 'ul',
28073                         cls: 'nav nav-tabs pull-right',
28074                         cn: [
28075                             header
28076                         ]
28077                     }
28078                 ]
28079             };
28080         }
28081         
28082         var cfg = {
28083             tag: 'div',
28084             cls: 'nav-tabs-custom',
28085             cn: [
28086                 h,
28087                 {
28088                     tag: 'div',
28089                     cls: 'tab-content no-padding',
28090                     cn: []
28091                 }
28092             ]
28093         };
28094
28095         return  cfg;
28096     },
28097     initEvents : function()
28098     {
28099         //Roo.log('add add pane handler');
28100         this.on('addpane', this.onAddPane, this);
28101     },
28102      /**
28103      * Updates the box title
28104      * @param {String} html to set the title to.
28105      */
28106     setTitle : function(value)
28107     {
28108         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28109     },
28110     onAddPane : function(pane)
28111     {
28112         this.panes.push(pane);
28113         //Roo.log('addpane');
28114         //Roo.log(pane);
28115         // tabs are rendere left to right..
28116         if(!this.showtabs){
28117             return;
28118         }
28119         
28120         var ctr = this.el.select('.nav-tabs', true).first();
28121          
28122          
28123         var existing = ctr.select('.nav-tab',true);
28124         var qty = existing.getCount();;
28125         
28126         
28127         var tab = ctr.createChild({
28128             tag : 'li',
28129             cls : 'nav-tab' + (qty ? '' : ' active'),
28130             cn : [
28131                 {
28132                     tag : 'a',
28133                     href:'#',
28134                     html : pane.title
28135                 }
28136             ]
28137         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28138         pane.tab = tab;
28139         
28140         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28141         if (!qty) {
28142             pane.el.addClass('active');
28143         }
28144         
28145                 
28146     },
28147     onTabClick : function(ev,un,ob,pane)
28148     {
28149         //Roo.log('tab - prev default');
28150         ev.preventDefault();
28151         
28152         
28153         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28154         pane.tab.addClass('active');
28155         //Roo.log(pane.title);
28156         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28157         // technically we should have a deactivate event.. but maybe add later.
28158         // and it should not de-activate the selected tab...
28159         this.fireEvent('activatepane', pane);
28160         pane.el.addClass('active');
28161         pane.fireEvent('activate');
28162         
28163         
28164     },
28165     
28166     getActivePane : function()
28167     {
28168         var r = false;
28169         Roo.each(this.panes, function(p) {
28170             if(p.el.hasClass('active')){
28171                 r = p;
28172                 return false;
28173             }
28174             
28175             return;
28176         });
28177         
28178         return r;
28179     }
28180     
28181     
28182 });
28183
28184  
28185 /*
28186  * - LGPL
28187  *
28188  * Tab pane
28189  * 
28190  */
28191 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28192 /**
28193  * @class Roo.bootstrap.TabPane
28194  * @extends Roo.bootstrap.Component
28195  * Bootstrap TabPane class
28196  * @cfg {Boolean} active (false | true) Default false
28197  * @cfg {String} title title of panel
28198
28199  * 
28200  * @constructor
28201  * Create a new TabPane
28202  * @param {Object} config The config object
28203  */
28204
28205 Roo.bootstrap.dash.TabPane = function(config){
28206     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28207     
28208     this.addEvents({
28209         // raw events
28210         /**
28211          * @event activate
28212          * When a pane is activated
28213          * @param {Roo.bootstrap.dash.TabPane} pane
28214          */
28215         "activate" : true
28216          
28217     });
28218 };
28219
28220 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28221     
28222     active : false,
28223     title : '',
28224     
28225     // the tabBox that this is attached to.
28226     tab : false,
28227      
28228     getAutoCreate : function() 
28229     {
28230         var cfg = {
28231             tag: 'div',
28232             cls: 'tab-pane'
28233         };
28234         
28235         if(this.active){
28236             cfg.cls += ' active';
28237         }
28238         
28239         return cfg;
28240     },
28241     initEvents  : function()
28242     {
28243         //Roo.log('trigger add pane handler');
28244         this.parent().fireEvent('addpane', this)
28245     },
28246     
28247      /**
28248      * Updates the tab title 
28249      * @param {String} html to set the title to.
28250      */
28251     setTitle: function(str)
28252     {
28253         if (!this.tab) {
28254             return;
28255         }
28256         this.title = str;
28257         this.tab.select('a', true).first().dom.innerHTML = str;
28258         
28259     }
28260     
28261     
28262     
28263 });
28264
28265  
28266
28267
28268  /*
28269  * - LGPL
28270  *
28271  * menu
28272  * 
28273  */
28274 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28275
28276 /**
28277  * @class Roo.bootstrap.menu.Menu
28278  * @extends Roo.bootstrap.Component
28279  * Bootstrap Menu class - container for Menu
28280  * @cfg {String} html Text of the menu
28281  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28282  * @cfg {String} icon Font awesome icon
28283  * @cfg {String} pos Menu align to (top | bottom) default bottom
28284  * 
28285  * 
28286  * @constructor
28287  * Create a new Menu
28288  * @param {Object} config The config object
28289  */
28290
28291
28292 Roo.bootstrap.menu.Menu = function(config){
28293     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28294     
28295     this.addEvents({
28296         /**
28297          * @event beforeshow
28298          * Fires before this menu is displayed
28299          * @param {Roo.bootstrap.menu.Menu} this
28300          */
28301         beforeshow : true,
28302         /**
28303          * @event beforehide
28304          * Fires before this menu is hidden
28305          * @param {Roo.bootstrap.menu.Menu} this
28306          */
28307         beforehide : true,
28308         /**
28309          * @event show
28310          * Fires after this menu is displayed
28311          * @param {Roo.bootstrap.menu.Menu} this
28312          */
28313         show : true,
28314         /**
28315          * @event hide
28316          * Fires after this menu is hidden
28317          * @param {Roo.bootstrap.menu.Menu} this
28318          */
28319         hide : true,
28320         /**
28321          * @event click
28322          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28323          * @param {Roo.bootstrap.menu.Menu} this
28324          * @param {Roo.EventObject} e
28325          */
28326         click : true
28327     });
28328     
28329 };
28330
28331 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28332     
28333     submenu : false,
28334     html : '',
28335     weight : 'default',
28336     icon : false,
28337     pos : 'bottom',
28338     
28339     
28340     getChildContainer : function() {
28341         if(this.isSubMenu){
28342             return this.el;
28343         }
28344         
28345         return this.el.select('ul.dropdown-menu', true).first();  
28346     },
28347     
28348     getAutoCreate : function()
28349     {
28350         var text = [
28351             {
28352                 tag : 'span',
28353                 cls : 'roo-menu-text',
28354                 html : this.html
28355             }
28356         ];
28357         
28358         if(this.icon){
28359             text.unshift({
28360                 tag : 'i',
28361                 cls : 'fa ' + this.icon
28362             })
28363         }
28364         
28365         
28366         var cfg = {
28367             tag : 'div',
28368             cls : 'btn-group',
28369             cn : [
28370                 {
28371                     tag : 'button',
28372                     cls : 'dropdown-button btn btn-' + this.weight,
28373                     cn : text
28374                 },
28375                 {
28376                     tag : 'button',
28377                     cls : 'dropdown-toggle btn btn-' + this.weight,
28378                     cn : [
28379                         {
28380                             tag : 'span',
28381                             cls : 'caret'
28382                         }
28383                     ]
28384                 },
28385                 {
28386                     tag : 'ul',
28387                     cls : 'dropdown-menu'
28388                 }
28389             ]
28390             
28391         };
28392         
28393         if(this.pos == 'top'){
28394             cfg.cls += ' dropup';
28395         }
28396         
28397         if(this.isSubMenu){
28398             cfg = {
28399                 tag : 'ul',
28400                 cls : 'dropdown-menu'
28401             }
28402         }
28403         
28404         return cfg;
28405     },
28406     
28407     onRender : function(ct, position)
28408     {
28409         this.isSubMenu = ct.hasClass('dropdown-submenu');
28410         
28411         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28412     },
28413     
28414     initEvents : function() 
28415     {
28416         if(this.isSubMenu){
28417             return;
28418         }
28419         
28420         this.hidden = true;
28421         
28422         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28423         this.triggerEl.on('click', this.onTriggerPress, this);
28424         
28425         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28426         this.buttonEl.on('click', this.onClick, this);
28427         
28428     },
28429     
28430     list : function()
28431     {
28432         if(this.isSubMenu){
28433             return this.el;
28434         }
28435         
28436         return this.el.select('ul.dropdown-menu', true).first();
28437     },
28438     
28439     onClick : function(e)
28440     {
28441         this.fireEvent("click", this, e);
28442     },
28443     
28444     onTriggerPress  : function(e)
28445     {   
28446         if (this.isVisible()) {
28447             this.hide();
28448         } else {
28449             this.show();
28450         }
28451     },
28452     
28453     isVisible : function(){
28454         return !this.hidden;
28455     },
28456     
28457     show : function()
28458     {
28459         this.fireEvent("beforeshow", this);
28460         
28461         this.hidden = false;
28462         this.el.addClass('open');
28463         
28464         Roo.get(document).on("mouseup", this.onMouseUp, this);
28465         
28466         this.fireEvent("show", this);
28467         
28468         
28469     },
28470     
28471     hide : function()
28472     {
28473         this.fireEvent("beforehide", this);
28474         
28475         this.hidden = true;
28476         this.el.removeClass('open');
28477         
28478         Roo.get(document).un("mouseup", this.onMouseUp);
28479         
28480         this.fireEvent("hide", this);
28481     },
28482     
28483     onMouseUp : function()
28484     {
28485         this.hide();
28486     }
28487     
28488 });
28489
28490  
28491  /*
28492  * - LGPL
28493  *
28494  * menu item
28495  * 
28496  */
28497 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28498
28499 /**
28500  * @class Roo.bootstrap.menu.Item
28501  * @extends Roo.bootstrap.Component
28502  * Bootstrap MenuItem class
28503  * @cfg {Boolean} submenu (true | false) default false
28504  * @cfg {String} html text of the item
28505  * @cfg {String} href the link
28506  * @cfg {Boolean} disable (true | false) default false
28507  * @cfg {Boolean} preventDefault (true | false) default true
28508  * @cfg {String} icon Font awesome icon
28509  * @cfg {String} pos Submenu align to (left | right) default right 
28510  * 
28511  * 
28512  * @constructor
28513  * Create a new Item
28514  * @param {Object} config The config object
28515  */
28516
28517
28518 Roo.bootstrap.menu.Item = function(config){
28519     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28520     this.addEvents({
28521         /**
28522          * @event mouseover
28523          * Fires when the mouse is hovering over this menu
28524          * @param {Roo.bootstrap.menu.Item} this
28525          * @param {Roo.EventObject} e
28526          */
28527         mouseover : true,
28528         /**
28529          * @event mouseout
28530          * Fires when the mouse exits this menu
28531          * @param {Roo.bootstrap.menu.Item} this
28532          * @param {Roo.EventObject} e
28533          */
28534         mouseout : true,
28535         // raw events
28536         /**
28537          * @event click
28538          * The raw click event for the entire grid.
28539          * @param {Roo.EventObject} e
28540          */
28541         click : true
28542     });
28543 };
28544
28545 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28546     
28547     submenu : false,
28548     href : '',
28549     html : '',
28550     preventDefault: true,
28551     disable : false,
28552     icon : false,
28553     pos : 'right',
28554     
28555     getAutoCreate : function()
28556     {
28557         var text = [
28558             {
28559                 tag : 'span',
28560                 cls : 'roo-menu-item-text',
28561                 html : this.html
28562             }
28563         ];
28564         
28565         if(this.icon){
28566             text.unshift({
28567                 tag : 'i',
28568                 cls : 'fa ' + this.icon
28569             })
28570         }
28571         
28572         var cfg = {
28573             tag : 'li',
28574             cn : [
28575                 {
28576                     tag : 'a',
28577                     href : this.href || '#',
28578                     cn : text
28579                 }
28580             ]
28581         };
28582         
28583         if(this.disable){
28584             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28585         }
28586         
28587         if(this.submenu){
28588             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28589             
28590             if(this.pos == 'left'){
28591                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28592             }
28593         }
28594         
28595         return cfg;
28596     },
28597     
28598     initEvents : function() 
28599     {
28600         this.el.on('mouseover', this.onMouseOver, this);
28601         this.el.on('mouseout', this.onMouseOut, this);
28602         
28603         this.el.select('a', true).first().on('click', this.onClick, this);
28604         
28605     },
28606     
28607     onClick : function(e)
28608     {
28609         if(this.preventDefault){
28610             e.preventDefault();
28611         }
28612         
28613         this.fireEvent("click", this, e);
28614     },
28615     
28616     onMouseOver : function(e)
28617     {
28618         if(this.submenu && this.pos == 'left'){
28619             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28620         }
28621         
28622         this.fireEvent("mouseover", this, e);
28623     },
28624     
28625     onMouseOut : function(e)
28626     {
28627         this.fireEvent("mouseout", this, e);
28628     }
28629 });
28630
28631  
28632
28633  /*
28634  * - LGPL
28635  *
28636  * menu separator
28637  * 
28638  */
28639 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28640
28641 /**
28642  * @class Roo.bootstrap.menu.Separator
28643  * @extends Roo.bootstrap.Component
28644  * Bootstrap Separator class
28645  * 
28646  * @constructor
28647  * Create a new Separator
28648  * @param {Object} config The config object
28649  */
28650
28651
28652 Roo.bootstrap.menu.Separator = function(config){
28653     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28654 };
28655
28656 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28657     
28658     getAutoCreate : function(){
28659         var cfg = {
28660             tag : 'li',
28661             cls: 'divider'
28662         };
28663         
28664         return cfg;
28665     }
28666    
28667 });
28668
28669  
28670
28671  /*
28672  * - LGPL
28673  *
28674  * Tooltip
28675  * 
28676  */
28677
28678 /**
28679  * @class Roo.bootstrap.Tooltip
28680  * Bootstrap Tooltip class
28681  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28682  * to determine which dom element triggers the tooltip.
28683  * 
28684  * It needs to add support for additional attributes like tooltip-position
28685  * 
28686  * @constructor
28687  * Create a new Toolti
28688  * @param {Object} config The config object
28689  */
28690
28691 Roo.bootstrap.Tooltip = function(config){
28692     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28693     
28694     this.alignment = Roo.bootstrap.Tooltip.alignment;
28695     
28696     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28697         this.alignment = config.alignment;
28698     }
28699     
28700 };
28701
28702 Roo.apply(Roo.bootstrap.Tooltip, {
28703     /**
28704      * @function init initialize tooltip monitoring.
28705      * @static
28706      */
28707     currentEl : false,
28708     currentTip : false,
28709     currentRegion : false,
28710     
28711     //  init : delay?
28712     
28713     init : function()
28714     {
28715         Roo.get(document).on('mouseover', this.enter ,this);
28716         Roo.get(document).on('mouseout', this.leave, this);
28717          
28718         
28719         this.currentTip = new Roo.bootstrap.Tooltip();
28720     },
28721     
28722     enter : function(ev)
28723     {
28724         var dom = ev.getTarget();
28725         
28726         //Roo.log(['enter',dom]);
28727         var el = Roo.fly(dom);
28728         if (this.currentEl) {
28729             //Roo.log(dom);
28730             //Roo.log(this.currentEl);
28731             //Roo.log(this.currentEl.contains(dom));
28732             if (this.currentEl == el) {
28733                 return;
28734             }
28735             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28736                 return;
28737             }
28738
28739         }
28740         
28741         if (this.currentTip.el) {
28742             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28743         }    
28744         //Roo.log(ev);
28745         
28746         if(!el || el.dom == document){
28747             return;
28748         }
28749         
28750         var bindEl = el;
28751         
28752         // you can not look for children, as if el is the body.. then everythign is the child..
28753         if (!el.attr('tooltip')) { //
28754             if (!el.select("[tooltip]").elements.length) {
28755                 return;
28756             }
28757             // is the mouse over this child...?
28758             bindEl = el.select("[tooltip]").first();
28759             var xy = ev.getXY();
28760             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28761                 //Roo.log("not in region.");
28762                 return;
28763             }
28764             //Roo.log("child element over..");
28765             
28766         }
28767         this.currentEl = bindEl;
28768         this.currentTip.bind(bindEl);
28769         this.currentRegion = Roo.lib.Region.getRegion(dom);
28770         this.currentTip.enter();
28771         
28772     },
28773     leave : function(ev)
28774     {
28775         var dom = ev.getTarget();
28776         //Roo.log(['leave',dom]);
28777         if (!this.currentEl) {
28778             return;
28779         }
28780         
28781         
28782         if (dom != this.currentEl.dom) {
28783             return;
28784         }
28785         var xy = ev.getXY();
28786         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28787             return;
28788         }
28789         // only activate leave if mouse cursor is outside... bounding box..
28790         
28791         
28792         
28793         
28794         if (this.currentTip) {
28795             this.currentTip.leave();
28796         }
28797         //Roo.log('clear currentEl');
28798         this.currentEl = false;
28799         
28800         
28801     },
28802     alignment : {
28803         'left' : ['r-l', [-2,0], 'right'],
28804         'right' : ['l-r', [2,0], 'left'],
28805         'bottom' : ['t-b', [0,2], 'top'],
28806         'top' : [ 'b-t', [0,-2], 'bottom']
28807     }
28808     
28809 });
28810
28811
28812 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28813     
28814     
28815     bindEl : false,
28816     
28817     delay : null, // can be { show : 300 , hide: 500}
28818     
28819     timeout : null,
28820     
28821     hoverState : null, //???
28822     
28823     placement : 'bottom', 
28824     
28825     alignment : false,
28826     
28827     getAutoCreate : function(){
28828     
28829         var cfg = {
28830            cls : 'tooltip',   
28831            role : 'tooltip',
28832            cn : [
28833                 {
28834                     cls : 'tooltip-arrow arrow'
28835                 },
28836                 {
28837                     cls : 'tooltip-inner'
28838                 }
28839            ]
28840         };
28841         
28842         return cfg;
28843     },
28844     bind : function(el)
28845     {
28846         this.bindEl = el;
28847     },
28848     
28849     initEvents : function()
28850     {
28851         this.arrowEl = this.el.select('.arrow', true).first();
28852         this.innerEl = this.el.select('.tooltip-inner', true).first();
28853     },
28854     
28855     enter : function () {
28856        
28857         if (this.timeout != null) {
28858             clearTimeout(this.timeout);
28859         }
28860         
28861         this.hoverState = 'in';
28862          //Roo.log("enter - show");
28863         if (!this.delay || !this.delay.show) {
28864             this.show();
28865             return;
28866         }
28867         var _t = this;
28868         this.timeout = setTimeout(function () {
28869             if (_t.hoverState == 'in') {
28870                 _t.show();
28871             }
28872         }, this.delay.show);
28873     },
28874     leave : function()
28875     {
28876         clearTimeout(this.timeout);
28877     
28878         this.hoverState = 'out';
28879          if (!this.delay || !this.delay.hide) {
28880             this.hide();
28881             return;
28882         }
28883        
28884         var _t = this;
28885         this.timeout = setTimeout(function () {
28886             //Roo.log("leave - timeout");
28887             
28888             if (_t.hoverState == 'out') {
28889                 _t.hide();
28890                 Roo.bootstrap.Tooltip.currentEl = false;
28891             }
28892         }, delay);
28893     },
28894     
28895     show : function (msg)
28896     {
28897         if (!this.el) {
28898             this.render(document.body);
28899         }
28900         // set content.
28901         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28902         
28903         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28904         
28905         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28906         
28907         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28908                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28909         
28910         var placement = typeof this.placement == 'function' ?
28911             this.placement.call(this, this.el, on_el) :
28912             this.placement;
28913             
28914         var autoToken = /\s?auto?\s?/i;
28915         var autoPlace = autoToken.test(placement);
28916         if (autoPlace) {
28917             placement = placement.replace(autoToken, '') || 'top';
28918         }
28919         
28920         //this.el.detach()
28921         //this.el.setXY([0,0]);
28922         this.el.show();
28923         //this.el.dom.style.display='block';
28924         
28925         //this.el.appendTo(on_el);
28926         
28927         var p = this.getPosition();
28928         var box = this.el.getBox();
28929         
28930         if (autoPlace) {
28931             // fixme..
28932         }
28933         
28934         var align = this.alignment[placement];
28935         
28936         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28937         
28938         if(placement == 'top' || placement == 'bottom'){
28939             if(xy[0] < 0){
28940                 placement = 'right';
28941             }
28942             
28943             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28944                 placement = 'left';
28945             }
28946             
28947             var scroll = Roo.select('body', true).first().getScroll();
28948             
28949             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28950                 placement = 'top';
28951             }
28952             
28953             align = this.alignment[placement];
28954             
28955             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28956             
28957         }
28958         
28959         this.el.alignTo(this.bindEl, align[0],align[1]);
28960         //var arrow = this.el.select('.arrow',true).first();
28961         //arrow.set(align[2], 
28962         
28963         this.el.addClass(placement);
28964         this.el.addClass("bs-tooltip-"+ placement);
28965         
28966         this.el.addClass('in fade show');
28967         
28968         this.hoverState = null;
28969         
28970         if (this.el.hasClass('fade')) {
28971             // fade it?
28972         }
28973         
28974         
28975         
28976         
28977         
28978     },
28979     hide : function()
28980     {
28981          
28982         if (!this.el) {
28983             return;
28984         }
28985         //this.el.setXY([0,0]);
28986         this.el.removeClass(['show', 'in']);
28987         //this.el.hide();
28988         
28989     }
28990     
28991 });
28992  
28993
28994  /*
28995  * - LGPL
28996  *
28997  * Location Picker
28998  * 
28999  */
29000
29001 /**
29002  * @class Roo.bootstrap.LocationPicker
29003  * @extends Roo.bootstrap.Component
29004  * Bootstrap LocationPicker class
29005  * @cfg {Number} latitude Position when init default 0
29006  * @cfg {Number} longitude Position when init default 0
29007  * @cfg {Number} zoom default 15
29008  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29009  * @cfg {Boolean} mapTypeControl default false
29010  * @cfg {Boolean} disableDoubleClickZoom default false
29011  * @cfg {Boolean} scrollwheel default true
29012  * @cfg {Boolean} streetViewControl default false
29013  * @cfg {Number} radius default 0
29014  * @cfg {String} locationName
29015  * @cfg {Boolean} draggable default true
29016  * @cfg {Boolean} enableAutocomplete default false
29017  * @cfg {Boolean} enableReverseGeocode default true
29018  * @cfg {String} markerTitle
29019  * 
29020  * @constructor
29021  * Create a new LocationPicker
29022  * @param {Object} config The config object
29023  */
29024
29025
29026 Roo.bootstrap.LocationPicker = function(config){
29027     
29028     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29029     
29030     this.addEvents({
29031         /**
29032          * @event initial
29033          * Fires when the picker initialized.
29034          * @param {Roo.bootstrap.LocationPicker} this
29035          * @param {Google Location} location
29036          */
29037         initial : true,
29038         /**
29039          * @event positionchanged
29040          * Fires when the picker position changed.
29041          * @param {Roo.bootstrap.LocationPicker} this
29042          * @param {Google Location} location
29043          */
29044         positionchanged : true,
29045         /**
29046          * @event resize
29047          * Fires when the map resize.
29048          * @param {Roo.bootstrap.LocationPicker} this
29049          */
29050         resize : true,
29051         /**
29052          * @event show
29053          * Fires when the map show.
29054          * @param {Roo.bootstrap.LocationPicker} this
29055          */
29056         show : true,
29057         /**
29058          * @event hide
29059          * Fires when the map hide.
29060          * @param {Roo.bootstrap.LocationPicker} this
29061          */
29062         hide : true,
29063         /**
29064          * @event mapClick
29065          * Fires when click the map.
29066          * @param {Roo.bootstrap.LocationPicker} this
29067          * @param {Map event} e
29068          */
29069         mapClick : true,
29070         /**
29071          * @event mapRightClick
29072          * Fires when right click the map.
29073          * @param {Roo.bootstrap.LocationPicker} this
29074          * @param {Map event} e
29075          */
29076         mapRightClick : true,
29077         /**
29078          * @event markerClick
29079          * Fires when click the marker.
29080          * @param {Roo.bootstrap.LocationPicker} this
29081          * @param {Map event} e
29082          */
29083         markerClick : true,
29084         /**
29085          * @event markerRightClick
29086          * Fires when right click the marker.
29087          * @param {Roo.bootstrap.LocationPicker} this
29088          * @param {Map event} e
29089          */
29090         markerRightClick : true,
29091         /**
29092          * @event OverlayViewDraw
29093          * Fires when OverlayView Draw
29094          * @param {Roo.bootstrap.LocationPicker} this
29095          */
29096         OverlayViewDraw : true,
29097         /**
29098          * @event OverlayViewOnAdd
29099          * Fires when OverlayView Draw
29100          * @param {Roo.bootstrap.LocationPicker} this
29101          */
29102         OverlayViewOnAdd : true,
29103         /**
29104          * @event OverlayViewOnRemove
29105          * Fires when OverlayView Draw
29106          * @param {Roo.bootstrap.LocationPicker} this
29107          */
29108         OverlayViewOnRemove : true,
29109         /**
29110          * @event OverlayViewShow
29111          * Fires when OverlayView Draw
29112          * @param {Roo.bootstrap.LocationPicker} this
29113          * @param {Pixel} cpx
29114          */
29115         OverlayViewShow : true,
29116         /**
29117          * @event OverlayViewHide
29118          * Fires when OverlayView Draw
29119          * @param {Roo.bootstrap.LocationPicker} this
29120          */
29121         OverlayViewHide : true,
29122         /**
29123          * @event loadexception
29124          * Fires when load google lib failed.
29125          * @param {Roo.bootstrap.LocationPicker} this
29126          */
29127         loadexception : true
29128     });
29129         
29130 };
29131
29132 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29133     
29134     gMapContext: false,
29135     
29136     latitude: 0,
29137     longitude: 0,
29138     zoom: 15,
29139     mapTypeId: false,
29140     mapTypeControl: false,
29141     disableDoubleClickZoom: false,
29142     scrollwheel: true,
29143     streetViewControl: false,
29144     radius: 0,
29145     locationName: '',
29146     draggable: true,
29147     enableAutocomplete: false,
29148     enableReverseGeocode: true,
29149     markerTitle: '',
29150     
29151     getAutoCreate: function()
29152     {
29153
29154         var cfg = {
29155             tag: 'div',
29156             cls: 'roo-location-picker'
29157         };
29158         
29159         return cfg
29160     },
29161     
29162     initEvents: function(ct, position)
29163     {       
29164         if(!this.el.getWidth() || this.isApplied()){
29165             return;
29166         }
29167         
29168         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29169         
29170         this.initial();
29171     },
29172     
29173     initial: function()
29174     {
29175         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29176             this.fireEvent('loadexception', this);
29177             return;
29178         }
29179         
29180         if(!this.mapTypeId){
29181             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29182         }
29183         
29184         this.gMapContext = this.GMapContext();
29185         
29186         this.initOverlayView();
29187         
29188         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29189         
29190         var _this = this;
29191                 
29192         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29193             _this.setPosition(_this.gMapContext.marker.position);
29194         });
29195         
29196         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29197             _this.fireEvent('mapClick', this, event);
29198             
29199         });
29200
29201         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29202             _this.fireEvent('mapRightClick', this, event);
29203             
29204         });
29205         
29206         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29207             _this.fireEvent('markerClick', this, event);
29208             
29209         });
29210
29211         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29212             _this.fireEvent('markerRightClick', this, event);
29213             
29214         });
29215         
29216         this.setPosition(this.gMapContext.location);
29217         
29218         this.fireEvent('initial', this, this.gMapContext.location);
29219     },
29220     
29221     initOverlayView: function()
29222     {
29223         var _this = this;
29224         
29225         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29226             
29227             draw: function()
29228             {
29229                 _this.fireEvent('OverlayViewDraw', _this);
29230             },
29231             
29232             onAdd: function()
29233             {
29234                 _this.fireEvent('OverlayViewOnAdd', _this);
29235             },
29236             
29237             onRemove: function()
29238             {
29239                 _this.fireEvent('OverlayViewOnRemove', _this);
29240             },
29241             
29242             show: function(cpx)
29243             {
29244                 _this.fireEvent('OverlayViewShow', _this, cpx);
29245             },
29246             
29247             hide: function()
29248             {
29249                 _this.fireEvent('OverlayViewHide', _this);
29250             }
29251             
29252         });
29253     },
29254     
29255     fromLatLngToContainerPixel: function(event)
29256     {
29257         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29258     },
29259     
29260     isApplied: function() 
29261     {
29262         return this.getGmapContext() == false ? false : true;
29263     },
29264     
29265     getGmapContext: function() 
29266     {
29267         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29268     },
29269     
29270     GMapContext: function() 
29271     {
29272         var position = new google.maps.LatLng(this.latitude, this.longitude);
29273         
29274         var _map = new google.maps.Map(this.el.dom, {
29275             center: position,
29276             zoom: this.zoom,
29277             mapTypeId: this.mapTypeId,
29278             mapTypeControl: this.mapTypeControl,
29279             disableDoubleClickZoom: this.disableDoubleClickZoom,
29280             scrollwheel: this.scrollwheel,
29281             streetViewControl: this.streetViewControl,
29282             locationName: this.locationName,
29283             draggable: this.draggable,
29284             enableAutocomplete: this.enableAutocomplete,
29285             enableReverseGeocode: this.enableReverseGeocode
29286         });
29287         
29288         var _marker = new google.maps.Marker({
29289             position: position,
29290             map: _map,
29291             title: this.markerTitle,
29292             draggable: this.draggable
29293         });
29294         
29295         return {
29296             map: _map,
29297             marker: _marker,
29298             circle: null,
29299             location: position,
29300             radius: this.radius,
29301             locationName: this.locationName,
29302             addressComponents: {
29303                 formatted_address: null,
29304                 addressLine1: null,
29305                 addressLine2: null,
29306                 streetName: null,
29307                 streetNumber: null,
29308                 city: null,
29309                 district: null,
29310                 state: null,
29311                 stateOrProvince: null
29312             },
29313             settings: this,
29314             domContainer: this.el.dom,
29315             geodecoder: new google.maps.Geocoder()
29316         };
29317     },
29318     
29319     drawCircle: function(center, radius, options) 
29320     {
29321         if (this.gMapContext.circle != null) {
29322             this.gMapContext.circle.setMap(null);
29323         }
29324         if (radius > 0) {
29325             radius *= 1;
29326             options = Roo.apply({}, options, {
29327                 strokeColor: "#0000FF",
29328                 strokeOpacity: .35,
29329                 strokeWeight: 2,
29330                 fillColor: "#0000FF",
29331                 fillOpacity: .2
29332             });
29333             
29334             options.map = this.gMapContext.map;
29335             options.radius = radius;
29336             options.center = center;
29337             this.gMapContext.circle = new google.maps.Circle(options);
29338             return this.gMapContext.circle;
29339         }
29340         
29341         return null;
29342     },
29343     
29344     setPosition: function(location) 
29345     {
29346         this.gMapContext.location = location;
29347         this.gMapContext.marker.setPosition(location);
29348         this.gMapContext.map.panTo(location);
29349         this.drawCircle(location, this.gMapContext.radius, {});
29350         
29351         var _this = this;
29352         
29353         if (this.gMapContext.settings.enableReverseGeocode) {
29354             this.gMapContext.geodecoder.geocode({
29355                 latLng: this.gMapContext.location
29356             }, function(results, status) {
29357                 
29358                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29359                     _this.gMapContext.locationName = results[0].formatted_address;
29360                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29361                     
29362                     _this.fireEvent('positionchanged', this, location);
29363                 }
29364             });
29365             
29366             return;
29367         }
29368         
29369         this.fireEvent('positionchanged', this, location);
29370     },
29371     
29372     resize: function()
29373     {
29374         google.maps.event.trigger(this.gMapContext.map, "resize");
29375         
29376         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29377         
29378         this.fireEvent('resize', this);
29379     },
29380     
29381     setPositionByLatLng: function(latitude, longitude)
29382     {
29383         this.setPosition(new google.maps.LatLng(latitude, longitude));
29384     },
29385     
29386     getCurrentPosition: function() 
29387     {
29388         return {
29389             latitude: this.gMapContext.location.lat(),
29390             longitude: this.gMapContext.location.lng()
29391         };
29392     },
29393     
29394     getAddressName: function() 
29395     {
29396         return this.gMapContext.locationName;
29397     },
29398     
29399     getAddressComponents: function() 
29400     {
29401         return this.gMapContext.addressComponents;
29402     },
29403     
29404     address_component_from_google_geocode: function(address_components) 
29405     {
29406         var result = {};
29407         
29408         for (var i = 0; i < address_components.length; i++) {
29409             var component = address_components[i];
29410             if (component.types.indexOf("postal_code") >= 0) {
29411                 result.postalCode = component.short_name;
29412             } else if (component.types.indexOf("street_number") >= 0) {
29413                 result.streetNumber = component.short_name;
29414             } else if (component.types.indexOf("route") >= 0) {
29415                 result.streetName = component.short_name;
29416             } else if (component.types.indexOf("neighborhood") >= 0) {
29417                 result.city = component.short_name;
29418             } else if (component.types.indexOf("locality") >= 0) {
29419                 result.city = component.short_name;
29420             } else if (component.types.indexOf("sublocality") >= 0) {
29421                 result.district = component.short_name;
29422             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29423                 result.stateOrProvince = component.short_name;
29424             } else if (component.types.indexOf("country") >= 0) {
29425                 result.country = component.short_name;
29426             }
29427         }
29428         
29429         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29430         result.addressLine2 = "";
29431         return result;
29432     },
29433     
29434     setZoomLevel: function(zoom)
29435     {
29436         this.gMapContext.map.setZoom(zoom);
29437     },
29438     
29439     show: function()
29440     {
29441         if(!this.el){
29442             return;
29443         }
29444         
29445         this.el.show();
29446         
29447         this.resize();
29448         
29449         this.fireEvent('show', this);
29450     },
29451     
29452     hide: function()
29453     {
29454         if(!this.el){
29455             return;
29456         }
29457         
29458         this.el.hide();
29459         
29460         this.fireEvent('hide', this);
29461     }
29462     
29463 });
29464
29465 Roo.apply(Roo.bootstrap.LocationPicker, {
29466     
29467     OverlayView : function(map, options)
29468     {
29469         options = options || {};
29470         
29471         this.setMap(map);
29472     }
29473     
29474     
29475 });/**
29476  * @class Roo.bootstrap.Alert
29477  * @extends Roo.bootstrap.Component
29478  * Bootstrap Alert class - shows an alert area box
29479  * eg
29480  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29481   Enter a valid email address
29482 </div>
29483  * @licence LGPL
29484  * @cfg {String} title The title of alert
29485  * @cfg {String} html The content of alert
29486  * @cfg {String} weight (  success | info | warning | danger )
29487  * @cfg {String} faicon font-awesomeicon
29488  * 
29489  * @constructor
29490  * Create a new alert
29491  * @param {Object} config The config object
29492  */
29493
29494
29495 Roo.bootstrap.Alert = function(config){
29496     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29497     
29498 };
29499
29500 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29501     
29502     title: '',
29503     html: '',
29504     weight: false,
29505     faicon: false,
29506     
29507     getAutoCreate : function()
29508     {
29509         
29510         var cfg = {
29511             tag : 'div',
29512             cls : 'alert',
29513             cn : [
29514                 {
29515                     tag : 'i',
29516                     cls : 'roo-alert-icon'
29517                     
29518                 },
29519                 {
29520                     tag : 'b',
29521                     cls : 'roo-alert-title',
29522                     html : this.title
29523                 },
29524                 {
29525                     tag : 'span',
29526                     cls : 'roo-alert-text',
29527                     html : this.html
29528                 }
29529             ]
29530         };
29531         
29532         if(this.faicon){
29533             cfg.cn[0].cls += ' fa ' + this.faicon;
29534         }
29535         
29536         if(this.weight){
29537             cfg.cls += ' alert-' + this.weight;
29538         }
29539         
29540         return cfg;
29541     },
29542     
29543     initEvents: function() 
29544     {
29545         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29546     },
29547     
29548     setTitle : function(str)
29549     {
29550         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29551     },
29552     
29553     setText : function(str)
29554     {
29555         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29556     },
29557     
29558     setWeight : function(weight)
29559     {
29560         if(this.weight){
29561             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29562         }
29563         
29564         this.weight = weight;
29565         
29566         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29567     },
29568     
29569     setIcon : function(icon)
29570     {
29571         if(this.faicon){
29572             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29573         }
29574         
29575         this.faicon = icon;
29576         
29577         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29578     },
29579     
29580     hide: function() 
29581     {
29582         this.el.hide();   
29583     },
29584     
29585     show: function() 
29586     {  
29587         this.el.show();   
29588     }
29589     
29590 });
29591
29592  
29593 /*
29594 * Licence: LGPL
29595 */
29596
29597 /**
29598  * @class Roo.bootstrap.UploadCropbox
29599  * @extends Roo.bootstrap.Component
29600  * Bootstrap UploadCropbox class
29601  * @cfg {String} emptyText show when image has been loaded
29602  * @cfg {String} rotateNotify show when image too small to rotate
29603  * @cfg {Number} errorTimeout default 3000
29604  * @cfg {Number} minWidth default 300
29605  * @cfg {Number} minHeight default 300
29606  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29607  * @cfg {Boolean} isDocument (true|false) default false
29608  * @cfg {String} url action url
29609  * @cfg {String} paramName default 'imageUpload'
29610  * @cfg {String} method default POST
29611  * @cfg {Boolean} loadMask (true|false) default true
29612  * @cfg {Boolean} loadingText default 'Loading...'
29613  * 
29614  * @constructor
29615  * Create a new UploadCropbox
29616  * @param {Object} config The config object
29617  */
29618
29619 Roo.bootstrap.UploadCropbox = function(config){
29620     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29621     
29622     this.addEvents({
29623         /**
29624          * @event beforeselectfile
29625          * Fire before select file
29626          * @param {Roo.bootstrap.UploadCropbox} this
29627          */
29628         "beforeselectfile" : true,
29629         /**
29630          * @event initial
29631          * Fire after initEvent
29632          * @param {Roo.bootstrap.UploadCropbox} this
29633          */
29634         "initial" : true,
29635         /**
29636          * @event crop
29637          * Fire after initEvent
29638          * @param {Roo.bootstrap.UploadCropbox} this
29639          * @param {String} data
29640          */
29641         "crop" : true,
29642         /**
29643          * @event prepare
29644          * Fire when preparing the file data
29645          * @param {Roo.bootstrap.UploadCropbox} this
29646          * @param {Object} file
29647          */
29648         "prepare" : true,
29649         /**
29650          * @event exception
29651          * Fire when get exception
29652          * @param {Roo.bootstrap.UploadCropbox} this
29653          * @param {XMLHttpRequest} xhr
29654          */
29655         "exception" : true,
29656         /**
29657          * @event beforeloadcanvas
29658          * Fire before load the canvas
29659          * @param {Roo.bootstrap.UploadCropbox} this
29660          * @param {String} src
29661          */
29662         "beforeloadcanvas" : true,
29663         /**
29664          * @event trash
29665          * Fire when trash image
29666          * @param {Roo.bootstrap.UploadCropbox} this
29667          */
29668         "trash" : true,
29669         /**
29670          * @event download
29671          * Fire when download the image
29672          * @param {Roo.bootstrap.UploadCropbox} this
29673          */
29674         "download" : true,
29675         /**
29676          * @event footerbuttonclick
29677          * Fire when footerbuttonclick
29678          * @param {Roo.bootstrap.UploadCropbox} this
29679          * @param {String} type
29680          */
29681         "footerbuttonclick" : true,
29682         /**
29683          * @event resize
29684          * Fire when resize
29685          * @param {Roo.bootstrap.UploadCropbox} this
29686          */
29687         "resize" : true,
29688         /**
29689          * @event rotate
29690          * Fire when rotate the image
29691          * @param {Roo.bootstrap.UploadCropbox} this
29692          * @param {String} pos
29693          */
29694         "rotate" : true,
29695         /**
29696          * @event inspect
29697          * Fire when inspect the file
29698          * @param {Roo.bootstrap.UploadCropbox} this
29699          * @param {Object} file
29700          */
29701         "inspect" : true,
29702         /**
29703          * @event upload
29704          * Fire when xhr upload the file
29705          * @param {Roo.bootstrap.UploadCropbox} this
29706          * @param {Object} data
29707          */
29708         "upload" : true,
29709         /**
29710          * @event arrange
29711          * Fire when arrange the file data
29712          * @param {Roo.bootstrap.UploadCropbox} this
29713          * @param {Object} formData
29714          */
29715         "arrange" : true
29716     });
29717     
29718     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29719 };
29720
29721 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29722     
29723     emptyText : 'Click to upload image',
29724     rotateNotify : 'Image is too small to rotate',
29725     errorTimeout : 3000,
29726     scale : 0,
29727     baseScale : 1,
29728     rotate : 0,
29729     dragable : false,
29730     pinching : false,
29731     mouseX : 0,
29732     mouseY : 0,
29733     cropData : false,
29734     minWidth : 300,
29735     minHeight : 300,
29736     file : false,
29737     exif : {},
29738     baseRotate : 1,
29739     cropType : 'image/jpeg',
29740     buttons : false,
29741     canvasLoaded : false,
29742     isDocument : false,
29743     method : 'POST',
29744     paramName : 'imageUpload',
29745     loadMask : true,
29746     loadingText : 'Loading...',
29747     maskEl : false,
29748     
29749     getAutoCreate : function()
29750     {
29751         var cfg = {
29752             tag : 'div',
29753             cls : 'roo-upload-cropbox',
29754             cn : [
29755                 {
29756                     tag : 'input',
29757                     cls : 'roo-upload-cropbox-selector',
29758                     type : 'file'
29759                 },
29760                 {
29761                     tag : 'div',
29762                     cls : 'roo-upload-cropbox-body',
29763                     style : 'cursor:pointer',
29764                     cn : [
29765                         {
29766                             tag : 'div',
29767                             cls : 'roo-upload-cropbox-preview'
29768                         },
29769                         {
29770                             tag : 'div',
29771                             cls : 'roo-upload-cropbox-thumb'
29772                         },
29773                         {
29774                             tag : 'div',
29775                             cls : 'roo-upload-cropbox-empty-notify',
29776                             html : this.emptyText
29777                         },
29778                         {
29779                             tag : 'div',
29780                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29781                             html : this.rotateNotify
29782                         }
29783                     ]
29784                 },
29785                 {
29786                     tag : 'div',
29787                     cls : 'roo-upload-cropbox-footer',
29788                     cn : {
29789                         tag : 'div',
29790                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29791                         cn : []
29792                     }
29793                 }
29794             ]
29795         };
29796         
29797         return cfg;
29798     },
29799     
29800     onRender : function(ct, position)
29801     {
29802         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29803         
29804         if (this.buttons.length) {
29805             
29806             Roo.each(this.buttons, function(bb) {
29807                 
29808                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29809                 
29810                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29811                 
29812             }, this);
29813         }
29814         
29815         if(this.loadMask){
29816             this.maskEl = this.el;
29817         }
29818     },
29819     
29820     initEvents : function()
29821     {
29822         this.urlAPI = (window.createObjectURL && window) || 
29823                                 (window.URL && URL.revokeObjectURL && URL) || 
29824                                 (window.webkitURL && webkitURL);
29825                         
29826         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29827         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29828         
29829         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29830         this.selectorEl.hide();
29831         
29832         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29833         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29834         
29835         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29836         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29837         this.thumbEl.hide();
29838         
29839         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29840         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29841         
29842         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29843         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29844         this.errorEl.hide();
29845         
29846         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29847         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29848         this.footerEl.hide();
29849         
29850         this.setThumbBoxSize();
29851         
29852         this.bind();
29853         
29854         this.resize();
29855         
29856         this.fireEvent('initial', this);
29857     },
29858
29859     bind : function()
29860     {
29861         var _this = this;
29862         
29863         window.addEventListener("resize", function() { _this.resize(); } );
29864         
29865         this.bodyEl.on('click', this.beforeSelectFile, this);
29866         
29867         if(Roo.isTouch){
29868             this.bodyEl.on('touchstart', this.onTouchStart, this);
29869             this.bodyEl.on('touchmove', this.onTouchMove, this);
29870             this.bodyEl.on('touchend', this.onTouchEnd, this);
29871         }
29872         
29873         if(!Roo.isTouch){
29874             this.bodyEl.on('mousedown', this.onMouseDown, this);
29875             this.bodyEl.on('mousemove', this.onMouseMove, this);
29876             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29877             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29878             Roo.get(document).on('mouseup', this.onMouseUp, this);
29879         }
29880         
29881         this.selectorEl.on('change', this.onFileSelected, this);
29882     },
29883     
29884     reset : function()
29885     {    
29886         this.scale = 0;
29887         this.baseScale = 1;
29888         this.rotate = 0;
29889         this.baseRotate = 1;
29890         this.dragable = false;
29891         this.pinching = false;
29892         this.mouseX = 0;
29893         this.mouseY = 0;
29894         this.cropData = false;
29895         this.notifyEl.dom.innerHTML = this.emptyText;
29896         
29897         this.selectorEl.dom.value = '';
29898         
29899     },
29900     
29901     resize : function()
29902     {
29903         if(this.fireEvent('resize', this) != false){
29904             this.setThumbBoxPosition();
29905             this.setCanvasPosition();
29906         }
29907     },
29908     
29909     onFooterButtonClick : function(e, el, o, type)
29910     {
29911         switch (type) {
29912             case 'rotate-left' :
29913                 this.onRotateLeft(e);
29914                 break;
29915             case 'rotate-right' :
29916                 this.onRotateRight(e);
29917                 break;
29918             case 'picture' :
29919                 this.beforeSelectFile(e);
29920                 break;
29921             case 'trash' :
29922                 this.trash(e);
29923                 break;
29924             case 'crop' :
29925                 this.crop(e);
29926                 break;
29927             case 'download' :
29928                 this.download(e);
29929                 break;
29930             default :
29931                 break;
29932         }
29933         
29934         this.fireEvent('footerbuttonclick', this, type);
29935     },
29936     
29937     beforeSelectFile : function(e)
29938     {
29939         e.preventDefault();
29940         
29941         if(this.fireEvent('beforeselectfile', this) != false){
29942             this.selectorEl.dom.click();
29943         }
29944     },
29945     
29946     onFileSelected : function(e)
29947     {
29948         e.preventDefault();
29949         
29950         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29951             return;
29952         }
29953         
29954         var file = this.selectorEl.dom.files[0];
29955         
29956         if(this.fireEvent('inspect', this, file) != false){
29957             this.prepare(file);
29958         }
29959         
29960     },
29961     
29962     trash : function(e)
29963     {
29964         this.fireEvent('trash', this);
29965     },
29966     
29967     download : function(e)
29968     {
29969         this.fireEvent('download', this);
29970     },
29971     
29972     loadCanvas : function(src)
29973     {   
29974         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29975             
29976             this.reset();
29977             
29978             this.imageEl = document.createElement('img');
29979             
29980             var _this = this;
29981             
29982             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29983             
29984             this.imageEl.src = src;
29985         }
29986     },
29987     
29988     onLoadCanvas : function()
29989     {   
29990         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29991         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29992         
29993         this.bodyEl.un('click', this.beforeSelectFile, this);
29994         
29995         this.notifyEl.hide();
29996         this.thumbEl.show();
29997         this.footerEl.show();
29998         
29999         this.baseRotateLevel();
30000         
30001         if(this.isDocument){
30002             this.setThumbBoxSize();
30003         }
30004         
30005         this.setThumbBoxPosition();
30006         
30007         this.baseScaleLevel();
30008         
30009         this.draw();
30010         
30011         this.resize();
30012         
30013         this.canvasLoaded = true;
30014         
30015         if(this.loadMask){
30016             this.maskEl.unmask();
30017         }
30018         
30019     },
30020     
30021     setCanvasPosition : function()
30022     {   
30023         if(!this.canvasEl){
30024             return;
30025         }
30026         
30027         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30028         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30029         
30030         this.previewEl.setLeft(pw);
30031         this.previewEl.setTop(ph);
30032         
30033     },
30034     
30035     onMouseDown : function(e)
30036     {   
30037         e.stopEvent();
30038         
30039         this.dragable = true;
30040         this.pinching = false;
30041         
30042         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30043             this.dragable = false;
30044             return;
30045         }
30046         
30047         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30048         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30049         
30050     },
30051     
30052     onMouseMove : function(e)
30053     {   
30054         e.stopEvent();
30055         
30056         if(!this.canvasLoaded){
30057             return;
30058         }
30059         
30060         if (!this.dragable){
30061             return;
30062         }
30063         
30064         var minX = Math.ceil(this.thumbEl.getLeft(true));
30065         var minY = Math.ceil(this.thumbEl.getTop(true));
30066         
30067         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30068         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30069         
30070         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30071         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30072         
30073         x = x - this.mouseX;
30074         y = y - this.mouseY;
30075         
30076         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30077         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30078         
30079         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30080         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30081         
30082         this.previewEl.setLeft(bgX);
30083         this.previewEl.setTop(bgY);
30084         
30085         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30086         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30087     },
30088     
30089     onMouseUp : function(e)
30090     {   
30091         e.stopEvent();
30092         
30093         this.dragable = false;
30094     },
30095     
30096     onMouseWheel : function(e)
30097     {   
30098         e.stopEvent();
30099         
30100         this.startScale = this.scale;
30101         
30102         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30103         
30104         if(!this.zoomable()){
30105             this.scale = this.startScale;
30106             return;
30107         }
30108         
30109         this.draw();
30110         
30111         return;
30112     },
30113     
30114     zoomable : function()
30115     {
30116         var minScale = this.thumbEl.getWidth() / this.minWidth;
30117         
30118         if(this.minWidth < this.minHeight){
30119             minScale = this.thumbEl.getHeight() / this.minHeight;
30120         }
30121         
30122         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30123         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30124         
30125         if(
30126                 this.isDocument &&
30127                 (this.rotate == 0 || this.rotate == 180) && 
30128                 (
30129                     width > this.imageEl.OriginWidth || 
30130                     height > this.imageEl.OriginHeight ||
30131                     (width < this.minWidth && height < this.minHeight)
30132                 )
30133         ){
30134             return false;
30135         }
30136         
30137         if(
30138                 this.isDocument &&
30139                 (this.rotate == 90 || this.rotate == 270) && 
30140                 (
30141                     width > this.imageEl.OriginWidth || 
30142                     height > this.imageEl.OriginHeight ||
30143                     (width < this.minHeight && height < this.minWidth)
30144                 )
30145         ){
30146             return false;
30147         }
30148         
30149         if(
30150                 !this.isDocument &&
30151                 (this.rotate == 0 || this.rotate == 180) && 
30152                 (
30153                     width < this.minWidth || 
30154                     width > this.imageEl.OriginWidth || 
30155                     height < this.minHeight || 
30156                     height > this.imageEl.OriginHeight
30157                 )
30158         ){
30159             return false;
30160         }
30161         
30162         if(
30163                 !this.isDocument &&
30164                 (this.rotate == 90 || this.rotate == 270) && 
30165                 (
30166                     width < this.minHeight || 
30167                     width > this.imageEl.OriginWidth || 
30168                     height < this.minWidth || 
30169                     height > this.imageEl.OriginHeight
30170                 )
30171         ){
30172             return false;
30173         }
30174         
30175         return true;
30176         
30177     },
30178     
30179     onRotateLeft : function(e)
30180     {   
30181         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30182             
30183             var minScale = this.thumbEl.getWidth() / this.minWidth;
30184             
30185             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30186             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30187             
30188             this.startScale = this.scale;
30189             
30190             while (this.getScaleLevel() < minScale){
30191             
30192                 this.scale = this.scale + 1;
30193                 
30194                 if(!this.zoomable()){
30195                     break;
30196                 }
30197                 
30198                 if(
30199                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30200                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30201                 ){
30202                     continue;
30203                 }
30204                 
30205                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30206
30207                 this.draw();
30208                 
30209                 return;
30210             }
30211             
30212             this.scale = this.startScale;
30213             
30214             this.onRotateFail();
30215             
30216             return false;
30217         }
30218         
30219         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30220
30221         if(this.isDocument){
30222             this.setThumbBoxSize();
30223             this.setThumbBoxPosition();
30224             this.setCanvasPosition();
30225         }
30226         
30227         this.draw();
30228         
30229         this.fireEvent('rotate', this, 'left');
30230         
30231     },
30232     
30233     onRotateRight : function(e)
30234     {
30235         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30236             
30237             var minScale = this.thumbEl.getWidth() / this.minWidth;
30238         
30239             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30240             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30241             
30242             this.startScale = this.scale;
30243             
30244             while (this.getScaleLevel() < minScale){
30245             
30246                 this.scale = this.scale + 1;
30247                 
30248                 if(!this.zoomable()){
30249                     break;
30250                 }
30251                 
30252                 if(
30253                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30254                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30255                 ){
30256                     continue;
30257                 }
30258                 
30259                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30260
30261                 this.draw();
30262                 
30263                 return;
30264             }
30265             
30266             this.scale = this.startScale;
30267             
30268             this.onRotateFail();
30269             
30270             return false;
30271         }
30272         
30273         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30274
30275         if(this.isDocument){
30276             this.setThumbBoxSize();
30277             this.setThumbBoxPosition();
30278             this.setCanvasPosition();
30279         }
30280         
30281         this.draw();
30282         
30283         this.fireEvent('rotate', this, 'right');
30284     },
30285     
30286     onRotateFail : function()
30287     {
30288         this.errorEl.show(true);
30289         
30290         var _this = this;
30291         
30292         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30293     },
30294     
30295     draw : function()
30296     {
30297         this.previewEl.dom.innerHTML = '';
30298         
30299         var canvasEl = document.createElement("canvas");
30300         
30301         var contextEl = canvasEl.getContext("2d");
30302         
30303         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30304         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30305         var center = this.imageEl.OriginWidth / 2;
30306         
30307         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30308             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30309             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30310             center = this.imageEl.OriginHeight / 2;
30311         }
30312         
30313         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30314         
30315         contextEl.translate(center, center);
30316         contextEl.rotate(this.rotate * Math.PI / 180);
30317
30318         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30319         
30320         this.canvasEl = document.createElement("canvas");
30321         
30322         this.contextEl = this.canvasEl.getContext("2d");
30323         
30324         switch (this.rotate) {
30325             case 0 :
30326                 
30327                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30328                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30329                 
30330                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30331                 
30332                 break;
30333             case 90 : 
30334                 
30335                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30336                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30337                 
30338                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30339                     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);
30340                     break;
30341                 }
30342                 
30343                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30344                 
30345                 break;
30346             case 180 :
30347                 
30348                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30349                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30350                 
30351                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30352                     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);
30353                     break;
30354                 }
30355                 
30356                 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);
30357                 
30358                 break;
30359             case 270 :
30360                 
30361                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30362                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30363         
30364                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30365                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30366                     break;
30367                 }
30368                 
30369                 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);
30370                 
30371                 break;
30372             default : 
30373                 break;
30374         }
30375         
30376         this.previewEl.appendChild(this.canvasEl);
30377         
30378         this.setCanvasPosition();
30379     },
30380     
30381     crop : function()
30382     {
30383         if(!this.canvasLoaded){
30384             return;
30385         }
30386         
30387         var imageCanvas = document.createElement("canvas");
30388         
30389         var imageContext = imageCanvas.getContext("2d");
30390         
30391         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30392         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30393         
30394         var center = imageCanvas.width / 2;
30395         
30396         imageContext.translate(center, center);
30397         
30398         imageContext.rotate(this.rotate * Math.PI / 180);
30399         
30400         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30401         
30402         var canvas = document.createElement("canvas");
30403         
30404         var context = canvas.getContext("2d");
30405                 
30406         canvas.width = this.minWidth;
30407         canvas.height = this.minHeight;
30408
30409         switch (this.rotate) {
30410             case 0 :
30411                 
30412                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30413                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30414                 
30415                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30416                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30417                 
30418                 var targetWidth = this.minWidth - 2 * x;
30419                 var targetHeight = this.minHeight - 2 * y;
30420                 
30421                 var scale = 1;
30422                 
30423                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30424                     scale = targetWidth / width;
30425                 }
30426                 
30427                 if(x > 0 && y == 0){
30428                     scale = targetHeight / height;
30429                 }
30430                 
30431                 if(x > 0 && y > 0){
30432                     scale = targetWidth / width;
30433                     
30434                     if(width < height){
30435                         scale = targetHeight / height;
30436                     }
30437                 }
30438                 
30439                 context.scale(scale, scale);
30440                 
30441                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30442                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30443
30444                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30445                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30446
30447                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30448                 
30449                 break;
30450             case 90 : 
30451                 
30452                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30453                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30454                 
30455                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30456                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30457                 
30458                 var targetWidth = this.minWidth - 2 * x;
30459                 var targetHeight = this.minHeight - 2 * y;
30460                 
30461                 var scale = 1;
30462                 
30463                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30464                     scale = targetWidth / width;
30465                 }
30466                 
30467                 if(x > 0 && y == 0){
30468                     scale = targetHeight / height;
30469                 }
30470                 
30471                 if(x > 0 && y > 0){
30472                     scale = targetWidth / width;
30473                     
30474                     if(width < height){
30475                         scale = targetHeight / height;
30476                     }
30477                 }
30478                 
30479                 context.scale(scale, scale);
30480                 
30481                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30482                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30483
30484                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30485                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30486                 
30487                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30488                 
30489                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30490                 
30491                 break;
30492             case 180 :
30493                 
30494                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30495                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30496                 
30497                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30498                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30499                 
30500                 var targetWidth = this.minWidth - 2 * x;
30501                 var targetHeight = this.minHeight - 2 * y;
30502                 
30503                 var scale = 1;
30504                 
30505                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30506                     scale = targetWidth / width;
30507                 }
30508                 
30509                 if(x > 0 && y == 0){
30510                     scale = targetHeight / height;
30511                 }
30512                 
30513                 if(x > 0 && y > 0){
30514                     scale = targetWidth / width;
30515                     
30516                     if(width < height){
30517                         scale = targetHeight / height;
30518                     }
30519                 }
30520                 
30521                 context.scale(scale, scale);
30522                 
30523                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30524                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30525
30526                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30527                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30528
30529                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30530                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30531                 
30532                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30533                 
30534                 break;
30535             case 270 :
30536                 
30537                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30538                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30539                 
30540                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30541                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30542                 
30543                 var targetWidth = this.minWidth - 2 * x;
30544                 var targetHeight = this.minHeight - 2 * y;
30545                 
30546                 var scale = 1;
30547                 
30548                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30549                     scale = targetWidth / width;
30550                 }
30551                 
30552                 if(x > 0 && y == 0){
30553                     scale = targetHeight / height;
30554                 }
30555                 
30556                 if(x > 0 && y > 0){
30557                     scale = targetWidth / width;
30558                     
30559                     if(width < height){
30560                         scale = targetHeight / height;
30561                     }
30562                 }
30563                 
30564                 context.scale(scale, scale);
30565                 
30566                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30567                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30568
30569                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30570                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30571                 
30572                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30573                 
30574                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30575                 
30576                 break;
30577             default : 
30578                 break;
30579         }
30580         
30581         this.cropData = canvas.toDataURL(this.cropType);
30582         
30583         if(this.fireEvent('crop', this, this.cropData) !== false){
30584             this.process(this.file, this.cropData);
30585         }
30586         
30587         return;
30588         
30589     },
30590     
30591     setThumbBoxSize : function()
30592     {
30593         var width, height;
30594         
30595         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30596             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30597             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30598             
30599             this.minWidth = width;
30600             this.minHeight = height;
30601             
30602             if(this.rotate == 90 || this.rotate == 270){
30603                 this.minWidth = height;
30604                 this.minHeight = width;
30605             }
30606         }
30607         
30608         height = 300;
30609         width = Math.ceil(this.minWidth * height / this.minHeight);
30610         
30611         if(this.minWidth > this.minHeight){
30612             width = 300;
30613             height = Math.ceil(this.minHeight * width / this.minWidth);
30614         }
30615         
30616         this.thumbEl.setStyle({
30617             width : width + 'px',
30618             height : height + 'px'
30619         });
30620
30621         return;
30622             
30623     },
30624     
30625     setThumbBoxPosition : function()
30626     {
30627         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30628         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30629         
30630         this.thumbEl.setLeft(x);
30631         this.thumbEl.setTop(y);
30632         
30633     },
30634     
30635     baseRotateLevel : function()
30636     {
30637         this.baseRotate = 1;
30638         
30639         if(
30640                 typeof(this.exif) != 'undefined' &&
30641                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30642                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30643         ){
30644             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30645         }
30646         
30647         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30648         
30649     },
30650     
30651     baseScaleLevel : function()
30652     {
30653         var width, height;
30654         
30655         if(this.isDocument){
30656             
30657             if(this.baseRotate == 6 || this.baseRotate == 8){
30658             
30659                 height = this.thumbEl.getHeight();
30660                 this.baseScale = height / this.imageEl.OriginWidth;
30661
30662                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30663                     width = this.thumbEl.getWidth();
30664                     this.baseScale = width / this.imageEl.OriginHeight;
30665                 }
30666
30667                 return;
30668             }
30669
30670             height = this.thumbEl.getHeight();
30671             this.baseScale = height / this.imageEl.OriginHeight;
30672
30673             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30674                 width = this.thumbEl.getWidth();
30675                 this.baseScale = width / this.imageEl.OriginWidth;
30676             }
30677
30678             return;
30679         }
30680         
30681         if(this.baseRotate == 6 || this.baseRotate == 8){
30682             
30683             width = this.thumbEl.getHeight();
30684             this.baseScale = width / this.imageEl.OriginHeight;
30685             
30686             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30687                 height = this.thumbEl.getWidth();
30688                 this.baseScale = height / this.imageEl.OriginHeight;
30689             }
30690             
30691             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30692                 height = this.thumbEl.getWidth();
30693                 this.baseScale = height / this.imageEl.OriginHeight;
30694                 
30695                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30696                     width = this.thumbEl.getHeight();
30697                     this.baseScale = width / this.imageEl.OriginWidth;
30698                 }
30699             }
30700             
30701             return;
30702         }
30703         
30704         width = this.thumbEl.getWidth();
30705         this.baseScale = width / this.imageEl.OriginWidth;
30706         
30707         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30708             height = this.thumbEl.getHeight();
30709             this.baseScale = height / this.imageEl.OriginHeight;
30710         }
30711         
30712         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30713             
30714             height = this.thumbEl.getHeight();
30715             this.baseScale = height / this.imageEl.OriginHeight;
30716             
30717             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30718                 width = this.thumbEl.getWidth();
30719                 this.baseScale = width / this.imageEl.OriginWidth;
30720             }
30721             
30722         }
30723         
30724         return;
30725     },
30726     
30727     getScaleLevel : function()
30728     {
30729         return this.baseScale * Math.pow(1.1, this.scale);
30730     },
30731     
30732     onTouchStart : function(e)
30733     {
30734         if(!this.canvasLoaded){
30735             this.beforeSelectFile(e);
30736             return;
30737         }
30738         
30739         var touches = e.browserEvent.touches;
30740         
30741         if(!touches){
30742             return;
30743         }
30744         
30745         if(touches.length == 1){
30746             this.onMouseDown(e);
30747             return;
30748         }
30749         
30750         if(touches.length != 2){
30751             return;
30752         }
30753         
30754         var coords = [];
30755         
30756         for(var i = 0, finger; finger = touches[i]; i++){
30757             coords.push(finger.pageX, finger.pageY);
30758         }
30759         
30760         var x = Math.pow(coords[0] - coords[2], 2);
30761         var y = Math.pow(coords[1] - coords[3], 2);
30762         
30763         this.startDistance = Math.sqrt(x + y);
30764         
30765         this.startScale = this.scale;
30766         
30767         this.pinching = true;
30768         this.dragable = false;
30769         
30770     },
30771     
30772     onTouchMove : function(e)
30773     {
30774         if(!this.pinching && !this.dragable){
30775             return;
30776         }
30777         
30778         var touches = e.browserEvent.touches;
30779         
30780         if(!touches){
30781             return;
30782         }
30783         
30784         if(this.dragable){
30785             this.onMouseMove(e);
30786             return;
30787         }
30788         
30789         var coords = [];
30790         
30791         for(var i = 0, finger; finger = touches[i]; i++){
30792             coords.push(finger.pageX, finger.pageY);
30793         }
30794         
30795         var x = Math.pow(coords[0] - coords[2], 2);
30796         var y = Math.pow(coords[1] - coords[3], 2);
30797         
30798         this.endDistance = Math.sqrt(x + y);
30799         
30800         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30801         
30802         if(!this.zoomable()){
30803             this.scale = this.startScale;
30804             return;
30805         }
30806         
30807         this.draw();
30808         
30809     },
30810     
30811     onTouchEnd : function(e)
30812     {
30813         this.pinching = false;
30814         this.dragable = false;
30815         
30816     },
30817     
30818     process : function(file, crop)
30819     {
30820         if(this.loadMask){
30821             this.maskEl.mask(this.loadingText);
30822         }
30823         
30824         this.xhr = new XMLHttpRequest();
30825         
30826         file.xhr = this.xhr;
30827
30828         this.xhr.open(this.method, this.url, true);
30829         
30830         var headers = {
30831             "Accept": "application/json",
30832             "Cache-Control": "no-cache",
30833             "X-Requested-With": "XMLHttpRequest"
30834         };
30835         
30836         for (var headerName in headers) {
30837             var headerValue = headers[headerName];
30838             if (headerValue) {
30839                 this.xhr.setRequestHeader(headerName, headerValue);
30840             }
30841         }
30842         
30843         var _this = this;
30844         
30845         this.xhr.onload = function()
30846         {
30847             _this.xhrOnLoad(_this.xhr);
30848         }
30849         
30850         this.xhr.onerror = function()
30851         {
30852             _this.xhrOnError(_this.xhr);
30853         }
30854         
30855         var formData = new FormData();
30856
30857         formData.append('returnHTML', 'NO');
30858         
30859         if(crop){
30860             formData.append('crop', crop);
30861         }
30862         
30863         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30864             formData.append(this.paramName, file, file.name);
30865         }
30866         
30867         if(typeof(file.filename) != 'undefined'){
30868             formData.append('filename', file.filename);
30869         }
30870         
30871         if(typeof(file.mimetype) != 'undefined'){
30872             formData.append('mimetype', file.mimetype);
30873         }
30874         
30875         if(this.fireEvent('arrange', this, formData) != false){
30876             this.xhr.send(formData);
30877         };
30878     },
30879     
30880     xhrOnLoad : function(xhr)
30881     {
30882         if(this.loadMask){
30883             this.maskEl.unmask();
30884         }
30885         
30886         if (xhr.readyState !== 4) {
30887             this.fireEvent('exception', this, xhr);
30888             return;
30889         }
30890
30891         var response = Roo.decode(xhr.responseText);
30892         
30893         if(!response.success){
30894             this.fireEvent('exception', this, xhr);
30895             return;
30896         }
30897         
30898         var response = Roo.decode(xhr.responseText);
30899         
30900         this.fireEvent('upload', this, response);
30901         
30902     },
30903     
30904     xhrOnError : function()
30905     {
30906         if(this.loadMask){
30907             this.maskEl.unmask();
30908         }
30909         
30910         Roo.log('xhr on error');
30911         
30912         var response = Roo.decode(xhr.responseText);
30913           
30914         Roo.log(response);
30915         
30916     },
30917     
30918     prepare : function(file)
30919     {   
30920         if(this.loadMask){
30921             this.maskEl.mask(this.loadingText);
30922         }
30923         
30924         this.file = false;
30925         this.exif = {};
30926         
30927         if(typeof(file) === 'string'){
30928             this.loadCanvas(file);
30929             return;
30930         }
30931         
30932         if(!file || !this.urlAPI){
30933             return;
30934         }
30935         
30936         this.file = file;
30937         this.cropType = file.type;
30938         
30939         var _this = this;
30940         
30941         if(this.fireEvent('prepare', this, this.file) != false){
30942             
30943             var reader = new FileReader();
30944             
30945             reader.onload = function (e) {
30946                 if (e.target.error) {
30947                     Roo.log(e.target.error);
30948                     return;
30949                 }
30950                 
30951                 var buffer = e.target.result,
30952                     dataView = new DataView(buffer),
30953                     offset = 2,
30954                     maxOffset = dataView.byteLength - 4,
30955                     markerBytes,
30956                     markerLength;
30957                 
30958                 if (dataView.getUint16(0) === 0xffd8) {
30959                     while (offset < maxOffset) {
30960                         markerBytes = dataView.getUint16(offset);
30961                         
30962                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30963                             markerLength = dataView.getUint16(offset + 2) + 2;
30964                             if (offset + markerLength > dataView.byteLength) {
30965                                 Roo.log('Invalid meta data: Invalid segment size.');
30966                                 break;
30967                             }
30968                             
30969                             if(markerBytes == 0xffe1){
30970                                 _this.parseExifData(
30971                                     dataView,
30972                                     offset,
30973                                     markerLength
30974                                 );
30975                             }
30976                             
30977                             offset += markerLength;
30978                             
30979                             continue;
30980                         }
30981                         
30982                         break;
30983                     }
30984                     
30985                 }
30986                 
30987                 var url = _this.urlAPI.createObjectURL(_this.file);
30988                 
30989                 _this.loadCanvas(url);
30990                 
30991                 return;
30992             }
30993             
30994             reader.readAsArrayBuffer(this.file);
30995             
30996         }
30997         
30998     },
30999     
31000     parseExifData : function(dataView, offset, length)
31001     {
31002         var tiffOffset = offset + 10,
31003             littleEndian,
31004             dirOffset;
31005     
31006         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31007             // No Exif data, might be XMP data instead
31008             return;
31009         }
31010         
31011         // Check for the ASCII code for "Exif" (0x45786966):
31012         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31013             // No Exif data, might be XMP data instead
31014             return;
31015         }
31016         if (tiffOffset + 8 > dataView.byteLength) {
31017             Roo.log('Invalid Exif data: Invalid segment size.');
31018             return;
31019         }
31020         // Check for the two null bytes:
31021         if (dataView.getUint16(offset + 8) !== 0x0000) {
31022             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31023             return;
31024         }
31025         // Check the byte alignment:
31026         switch (dataView.getUint16(tiffOffset)) {
31027         case 0x4949:
31028             littleEndian = true;
31029             break;
31030         case 0x4D4D:
31031             littleEndian = false;
31032             break;
31033         default:
31034             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31035             return;
31036         }
31037         // Check for the TIFF tag marker (0x002A):
31038         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31039             Roo.log('Invalid Exif data: Missing TIFF marker.');
31040             return;
31041         }
31042         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31043         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31044         
31045         this.parseExifTags(
31046             dataView,
31047             tiffOffset,
31048             tiffOffset + dirOffset,
31049             littleEndian
31050         );
31051     },
31052     
31053     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31054     {
31055         var tagsNumber,
31056             dirEndOffset,
31057             i;
31058         if (dirOffset + 6 > dataView.byteLength) {
31059             Roo.log('Invalid Exif data: Invalid directory offset.');
31060             return;
31061         }
31062         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31063         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31064         if (dirEndOffset + 4 > dataView.byteLength) {
31065             Roo.log('Invalid Exif data: Invalid directory size.');
31066             return;
31067         }
31068         for (i = 0; i < tagsNumber; i += 1) {
31069             this.parseExifTag(
31070                 dataView,
31071                 tiffOffset,
31072                 dirOffset + 2 + 12 * i, // tag offset
31073                 littleEndian
31074             );
31075         }
31076         // Return the offset to the next directory:
31077         return dataView.getUint32(dirEndOffset, littleEndian);
31078     },
31079     
31080     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31081     {
31082         var tag = dataView.getUint16(offset, littleEndian);
31083         
31084         this.exif[tag] = this.getExifValue(
31085             dataView,
31086             tiffOffset,
31087             offset,
31088             dataView.getUint16(offset + 2, littleEndian), // tag type
31089             dataView.getUint32(offset + 4, littleEndian), // tag length
31090             littleEndian
31091         );
31092     },
31093     
31094     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31095     {
31096         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31097             tagSize,
31098             dataOffset,
31099             values,
31100             i,
31101             str,
31102             c;
31103     
31104         if (!tagType) {
31105             Roo.log('Invalid Exif data: Invalid tag type.');
31106             return;
31107         }
31108         
31109         tagSize = tagType.size * length;
31110         // Determine if the value is contained in the dataOffset bytes,
31111         // or if the value at the dataOffset is a pointer to the actual data:
31112         dataOffset = tagSize > 4 ?
31113                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31114         if (dataOffset + tagSize > dataView.byteLength) {
31115             Roo.log('Invalid Exif data: Invalid data offset.');
31116             return;
31117         }
31118         if (length === 1) {
31119             return tagType.getValue(dataView, dataOffset, littleEndian);
31120         }
31121         values = [];
31122         for (i = 0; i < length; i += 1) {
31123             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31124         }
31125         
31126         if (tagType.ascii) {
31127             str = '';
31128             // Concatenate the chars:
31129             for (i = 0; i < values.length; i += 1) {
31130                 c = values[i];
31131                 // Ignore the terminating NULL byte(s):
31132                 if (c === '\u0000') {
31133                     break;
31134                 }
31135                 str += c;
31136             }
31137             return str;
31138         }
31139         return values;
31140     }
31141     
31142 });
31143
31144 Roo.apply(Roo.bootstrap.UploadCropbox, {
31145     tags : {
31146         'Orientation': 0x0112
31147     },
31148     
31149     Orientation: {
31150             1: 0, //'top-left',
31151 //            2: 'top-right',
31152             3: 180, //'bottom-right',
31153 //            4: 'bottom-left',
31154 //            5: 'left-top',
31155             6: 90, //'right-top',
31156 //            7: 'right-bottom',
31157             8: 270 //'left-bottom'
31158     },
31159     
31160     exifTagTypes : {
31161         // byte, 8-bit unsigned int:
31162         1: {
31163             getValue: function (dataView, dataOffset) {
31164                 return dataView.getUint8(dataOffset);
31165             },
31166             size: 1
31167         },
31168         // ascii, 8-bit byte:
31169         2: {
31170             getValue: function (dataView, dataOffset) {
31171                 return String.fromCharCode(dataView.getUint8(dataOffset));
31172             },
31173             size: 1,
31174             ascii: true
31175         },
31176         // short, 16 bit int:
31177         3: {
31178             getValue: function (dataView, dataOffset, littleEndian) {
31179                 return dataView.getUint16(dataOffset, littleEndian);
31180             },
31181             size: 2
31182         },
31183         // long, 32 bit int:
31184         4: {
31185             getValue: function (dataView, dataOffset, littleEndian) {
31186                 return dataView.getUint32(dataOffset, littleEndian);
31187             },
31188             size: 4
31189         },
31190         // rational = two long values, first is numerator, second is denominator:
31191         5: {
31192             getValue: function (dataView, dataOffset, littleEndian) {
31193                 return dataView.getUint32(dataOffset, littleEndian) /
31194                     dataView.getUint32(dataOffset + 4, littleEndian);
31195             },
31196             size: 8
31197         },
31198         // slong, 32 bit signed int:
31199         9: {
31200             getValue: function (dataView, dataOffset, littleEndian) {
31201                 return dataView.getInt32(dataOffset, littleEndian);
31202             },
31203             size: 4
31204         },
31205         // srational, two slongs, first is numerator, second is denominator:
31206         10: {
31207             getValue: function (dataView, dataOffset, littleEndian) {
31208                 return dataView.getInt32(dataOffset, littleEndian) /
31209                     dataView.getInt32(dataOffset + 4, littleEndian);
31210             },
31211             size: 8
31212         }
31213     },
31214     
31215     footer : {
31216         STANDARD : [
31217             {
31218                 tag : 'div',
31219                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31220                 action : 'rotate-left',
31221                 cn : [
31222                     {
31223                         tag : 'button',
31224                         cls : 'btn btn-default',
31225                         html : '<i class="fa fa-undo"></i>'
31226                     }
31227                 ]
31228             },
31229             {
31230                 tag : 'div',
31231                 cls : 'btn-group roo-upload-cropbox-picture',
31232                 action : 'picture',
31233                 cn : [
31234                     {
31235                         tag : 'button',
31236                         cls : 'btn btn-default',
31237                         html : '<i class="fa fa-picture-o"></i>'
31238                     }
31239                 ]
31240             },
31241             {
31242                 tag : 'div',
31243                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31244                 action : 'rotate-right',
31245                 cn : [
31246                     {
31247                         tag : 'button',
31248                         cls : 'btn btn-default',
31249                         html : '<i class="fa fa-repeat"></i>'
31250                     }
31251                 ]
31252             }
31253         ],
31254         DOCUMENT : [
31255             {
31256                 tag : 'div',
31257                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31258                 action : 'rotate-left',
31259                 cn : [
31260                     {
31261                         tag : 'button',
31262                         cls : 'btn btn-default',
31263                         html : '<i class="fa fa-undo"></i>'
31264                     }
31265                 ]
31266             },
31267             {
31268                 tag : 'div',
31269                 cls : 'btn-group roo-upload-cropbox-download',
31270                 action : 'download',
31271                 cn : [
31272                     {
31273                         tag : 'button',
31274                         cls : 'btn btn-default',
31275                         html : '<i class="fa fa-download"></i>'
31276                     }
31277                 ]
31278             },
31279             {
31280                 tag : 'div',
31281                 cls : 'btn-group roo-upload-cropbox-crop',
31282                 action : 'crop',
31283                 cn : [
31284                     {
31285                         tag : 'button',
31286                         cls : 'btn btn-default',
31287                         html : '<i class="fa fa-crop"></i>'
31288                     }
31289                 ]
31290             },
31291             {
31292                 tag : 'div',
31293                 cls : 'btn-group roo-upload-cropbox-trash',
31294                 action : 'trash',
31295                 cn : [
31296                     {
31297                         tag : 'button',
31298                         cls : 'btn btn-default',
31299                         html : '<i class="fa fa-trash"></i>'
31300                     }
31301                 ]
31302             },
31303             {
31304                 tag : 'div',
31305                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31306                 action : 'rotate-right',
31307                 cn : [
31308                     {
31309                         tag : 'button',
31310                         cls : 'btn btn-default',
31311                         html : '<i class="fa fa-repeat"></i>'
31312                     }
31313                 ]
31314             }
31315         ],
31316         ROTATOR : [
31317             {
31318                 tag : 'div',
31319                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31320                 action : 'rotate-left',
31321                 cn : [
31322                     {
31323                         tag : 'button',
31324                         cls : 'btn btn-default',
31325                         html : '<i class="fa fa-undo"></i>'
31326                     }
31327                 ]
31328             },
31329             {
31330                 tag : 'div',
31331                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31332                 action : 'rotate-right',
31333                 cn : [
31334                     {
31335                         tag : 'button',
31336                         cls : 'btn btn-default',
31337                         html : '<i class="fa fa-repeat"></i>'
31338                     }
31339                 ]
31340             }
31341         ]
31342     }
31343 });
31344
31345 /*
31346 * Licence: LGPL
31347 */
31348
31349 /**
31350  * @class Roo.bootstrap.DocumentManager
31351  * @extends Roo.bootstrap.Component
31352  * Bootstrap DocumentManager class
31353  * @cfg {String} paramName default 'imageUpload'
31354  * @cfg {String} toolTipName default 'filename'
31355  * @cfg {String} method default POST
31356  * @cfg {String} url action url
31357  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31358  * @cfg {Boolean} multiple multiple upload default true
31359  * @cfg {Number} thumbSize default 300
31360  * @cfg {String} fieldLabel
31361  * @cfg {Number} labelWidth default 4
31362  * @cfg {String} labelAlign (left|top) default left
31363  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31364 * @cfg {Number} labellg set the width of label (1-12)
31365  * @cfg {Number} labelmd set the width of label (1-12)
31366  * @cfg {Number} labelsm set the width of label (1-12)
31367  * @cfg {Number} labelxs set the width of label (1-12)
31368  * 
31369  * @constructor
31370  * Create a new DocumentManager
31371  * @param {Object} config The config object
31372  */
31373
31374 Roo.bootstrap.DocumentManager = function(config){
31375     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31376     
31377     this.files = [];
31378     this.delegates = [];
31379     
31380     this.addEvents({
31381         /**
31382          * @event initial
31383          * Fire when initial the DocumentManager
31384          * @param {Roo.bootstrap.DocumentManager} this
31385          */
31386         "initial" : true,
31387         /**
31388          * @event inspect
31389          * inspect selected file
31390          * @param {Roo.bootstrap.DocumentManager} this
31391          * @param {File} file
31392          */
31393         "inspect" : true,
31394         /**
31395          * @event exception
31396          * Fire when xhr load exception
31397          * @param {Roo.bootstrap.DocumentManager} this
31398          * @param {XMLHttpRequest} xhr
31399          */
31400         "exception" : true,
31401         /**
31402          * @event afterupload
31403          * Fire when xhr load exception
31404          * @param {Roo.bootstrap.DocumentManager} this
31405          * @param {XMLHttpRequest} xhr
31406          */
31407         "afterupload" : true,
31408         /**
31409          * @event prepare
31410          * prepare the form data
31411          * @param {Roo.bootstrap.DocumentManager} this
31412          * @param {Object} formData
31413          */
31414         "prepare" : true,
31415         /**
31416          * @event remove
31417          * Fire when remove the file
31418          * @param {Roo.bootstrap.DocumentManager} this
31419          * @param {Object} file
31420          */
31421         "remove" : true,
31422         /**
31423          * @event refresh
31424          * Fire after refresh the file
31425          * @param {Roo.bootstrap.DocumentManager} this
31426          */
31427         "refresh" : true,
31428         /**
31429          * @event click
31430          * Fire after click the image
31431          * @param {Roo.bootstrap.DocumentManager} this
31432          * @param {Object} file
31433          */
31434         "click" : true,
31435         /**
31436          * @event edit
31437          * Fire when upload a image and editable set to true
31438          * @param {Roo.bootstrap.DocumentManager} this
31439          * @param {Object} file
31440          */
31441         "edit" : true,
31442         /**
31443          * @event beforeselectfile
31444          * Fire before select file
31445          * @param {Roo.bootstrap.DocumentManager} this
31446          */
31447         "beforeselectfile" : true,
31448         /**
31449          * @event process
31450          * Fire before process file
31451          * @param {Roo.bootstrap.DocumentManager} this
31452          * @param {Object} file
31453          */
31454         "process" : true,
31455         /**
31456          * @event previewrendered
31457          * Fire when preview rendered
31458          * @param {Roo.bootstrap.DocumentManager} this
31459          * @param {Object} file
31460          */
31461         "previewrendered" : true,
31462         /**
31463          */
31464         "previewResize" : true
31465         
31466     });
31467 };
31468
31469 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31470     
31471     boxes : 0,
31472     inputName : '',
31473     thumbSize : 300,
31474     multiple : true,
31475     files : false,
31476     method : 'POST',
31477     url : '',
31478     paramName : 'imageUpload',
31479     toolTipName : 'filename',
31480     fieldLabel : '',
31481     labelWidth : 4,
31482     labelAlign : 'left',
31483     editable : true,
31484     delegates : false,
31485     xhr : false, 
31486     
31487     labellg : 0,
31488     labelmd : 0,
31489     labelsm : 0,
31490     labelxs : 0,
31491     
31492     getAutoCreate : function()
31493     {   
31494         var managerWidget = {
31495             tag : 'div',
31496             cls : 'roo-document-manager',
31497             cn : [
31498                 {
31499                     tag : 'input',
31500                     cls : 'roo-document-manager-selector',
31501                     type : 'file'
31502                 },
31503                 {
31504                     tag : 'div',
31505                     cls : 'roo-document-manager-uploader',
31506                     cn : [
31507                         {
31508                             tag : 'div',
31509                             cls : 'roo-document-manager-upload-btn',
31510                             html : '<i class="fa fa-plus"></i>'
31511                         }
31512                     ]
31513                     
31514                 }
31515             ]
31516         };
31517         
31518         var content = [
31519             {
31520                 tag : 'div',
31521                 cls : 'column col-md-12',
31522                 cn : managerWidget
31523             }
31524         ];
31525         
31526         if(this.fieldLabel.length){
31527             
31528             content = [
31529                 {
31530                     tag : 'div',
31531                     cls : 'column col-md-12',
31532                     html : this.fieldLabel
31533                 },
31534                 {
31535                     tag : 'div',
31536                     cls : 'column col-md-12',
31537                     cn : managerWidget
31538                 }
31539             ];
31540
31541             if(this.labelAlign == 'left'){
31542                 content = [
31543                     {
31544                         tag : 'div',
31545                         cls : 'column',
31546                         html : this.fieldLabel
31547                     },
31548                     {
31549                         tag : 'div',
31550                         cls : 'column',
31551                         cn : managerWidget
31552                     }
31553                 ];
31554                 
31555                 if(this.labelWidth > 12){
31556                     content[0].style = "width: " + this.labelWidth + 'px';
31557                 }
31558
31559                 if(this.labelWidth < 13 && this.labelmd == 0){
31560                     this.labelmd = this.labelWidth;
31561                 }
31562
31563                 if(this.labellg > 0){
31564                     content[0].cls += ' col-lg-' + this.labellg;
31565                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31566                 }
31567
31568                 if(this.labelmd > 0){
31569                     content[0].cls += ' col-md-' + this.labelmd;
31570                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31571                 }
31572
31573                 if(this.labelsm > 0){
31574                     content[0].cls += ' col-sm-' + this.labelsm;
31575                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31576                 }
31577
31578                 if(this.labelxs > 0){
31579                     content[0].cls += ' col-xs-' + this.labelxs;
31580                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31581                 }
31582                 
31583             }
31584         }
31585         
31586         var cfg = {
31587             tag : 'div',
31588             cls : 'row clearfix',
31589             cn : content
31590         };
31591         
31592         return cfg;
31593         
31594     },
31595     
31596     initEvents : function()
31597     {
31598         this.managerEl = this.el.select('.roo-document-manager', true).first();
31599         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31600         
31601         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31602         this.selectorEl.hide();
31603         
31604         if(this.multiple){
31605             this.selectorEl.attr('multiple', 'multiple');
31606         }
31607         
31608         this.selectorEl.on('change', this.onFileSelected, this);
31609         
31610         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31611         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31612         
31613         this.uploader.on('click', this.onUploaderClick, this);
31614         
31615         this.renderProgressDialog();
31616         
31617         var _this = this;
31618         
31619         window.addEventListener("resize", function() { _this.refresh(); } );
31620         
31621         this.fireEvent('initial', this);
31622     },
31623     
31624     renderProgressDialog : function()
31625     {
31626         var _this = this;
31627         
31628         this.progressDialog = new Roo.bootstrap.Modal({
31629             cls : 'roo-document-manager-progress-dialog',
31630             allow_close : false,
31631             animate : false,
31632             title : '',
31633             buttons : [
31634                 {
31635                     name  :'cancel',
31636                     weight : 'danger',
31637                     html : 'Cancel'
31638                 }
31639             ], 
31640             listeners : { 
31641                 btnclick : function() {
31642                     _this.uploadCancel();
31643                     this.hide();
31644                 }
31645             }
31646         });
31647          
31648         this.progressDialog.render(Roo.get(document.body));
31649          
31650         this.progress = new Roo.bootstrap.Progress({
31651             cls : 'roo-document-manager-progress',
31652             active : true,
31653             striped : true
31654         });
31655         
31656         this.progress.render(this.progressDialog.getChildContainer());
31657         
31658         this.progressBar = new Roo.bootstrap.ProgressBar({
31659             cls : 'roo-document-manager-progress-bar',
31660             aria_valuenow : 0,
31661             aria_valuemin : 0,
31662             aria_valuemax : 12,
31663             panel : 'success'
31664         });
31665         
31666         this.progressBar.render(this.progress.getChildContainer());
31667     },
31668     
31669     onUploaderClick : function(e)
31670     {
31671         e.preventDefault();
31672      
31673         if(this.fireEvent('beforeselectfile', this) != false){
31674             this.selectorEl.dom.click();
31675         }
31676         
31677     },
31678     
31679     onFileSelected : function(e)
31680     {
31681         e.preventDefault();
31682         
31683         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31684             return;
31685         }
31686         
31687         Roo.each(this.selectorEl.dom.files, function(file){
31688             if(this.fireEvent('inspect', this, file) != false){
31689                 this.files.push(file);
31690             }
31691         }, this);
31692         
31693         this.queue();
31694         
31695     },
31696     
31697     queue : function()
31698     {
31699         this.selectorEl.dom.value = '';
31700         
31701         if(!this.files || !this.files.length){
31702             return;
31703         }
31704         
31705         if(this.boxes > 0 && this.files.length > this.boxes){
31706             this.files = this.files.slice(0, this.boxes);
31707         }
31708         
31709         this.uploader.show();
31710         
31711         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31712             this.uploader.hide();
31713         }
31714         
31715         var _this = this;
31716         
31717         var files = [];
31718         
31719         var docs = [];
31720         
31721         Roo.each(this.files, function(file){
31722             
31723             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31724                 var f = this.renderPreview(file);
31725                 files.push(f);
31726                 return;
31727             }
31728             
31729             if(file.type.indexOf('image') != -1){
31730                 this.delegates.push(
31731                     (function(){
31732                         _this.process(file);
31733                     }).createDelegate(this)
31734                 );
31735         
31736                 return;
31737             }
31738             
31739             docs.push(
31740                 (function(){
31741                     _this.process(file);
31742                 }).createDelegate(this)
31743             );
31744             
31745         }, this);
31746         
31747         this.files = files;
31748         
31749         this.delegates = this.delegates.concat(docs);
31750         
31751         if(!this.delegates.length){
31752             this.refresh();
31753             return;
31754         }
31755         
31756         this.progressBar.aria_valuemax = this.delegates.length;
31757         
31758         this.arrange();
31759         
31760         return;
31761     },
31762     
31763     arrange : function()
31764     {
31765         if(!this.delegates.length){
31766             this.progressDialog.hide();
31767             this.refresh();
31768             return;
31769         }
31770         
31771         var delegate = this.delegates.shift();
31772         
31773         this.progressDialog.show();
31774         
31775         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31776         
31777         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31778         
31779         delegate();
31780     },
31781     
31782     refresh : function()
31783     {
31784         this.uploader.show();
31785         
31786         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31787             this.uploader.hide();
31788         }
31789         
31790         Roo.isTouch ? this.closable(false) : this.closable(true);
31791         
31792         this.fireEvent('refresh', this);
31793     },
31794     
31795     onRemove : function(e, el, o)
31796     {
31797         e.preventDefault();
31798         
31799         this.fireEvent('remove', this, o);
31800         
31801     },
31802     
31803     remove : function(o)
31804     {
31805         var files = [];
31806         
31807         Roo.each(this.files, function(file){
31808             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31809                 files.push(file);
31810                 return;
31811             }
31812
31813             o.target.remove();
31814
31815         }, this);
31816         
31817         this.files = files;
31818         
31819         this.refresh();
31820     },
31821     
31822     clear : function()
31823     {
31824         Roo.each(this.files, function(file){
31825             if(!file.target){
31826                 return;
31827             }
31828             
31829             file.target.remove();
31830
31831         }, this);
31832         
31833         this.files = [];
31834         
31835         this.refresh();
31836     },
31837     
31838     onClick : function(e, el, o)
31839     {
31840         e.preventDefault();
31841         
31842         this.fireEvent('click', this, o);
31843         
31844     },
31845     
31846     closable : function(closable)
31847     {
31848         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31849             
31850             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31851             
31852             if(closable){
31853                 el.show();
31854                 return;
31855             }
31856             
31857             el.hide();
31858             
31859         }, this);
31860     },
31861     
31862     xhrOnLoad : function(xhr)
31863     {
31864         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31865             el.remove();
31866         }, this);
31867         
31868         if (xhr.readyState !== 4) {
31869             this.arrange();
31870             this.fireEvent('exception', this, xhr);
31871             return;
31872         }
31873
31874         var response = Roo.decode(xhr.responseText);
31875         
31876         if(!response.success){
31877             this.arrange();
31878             this.fireEvent('exception', this, xhr);
31879             return;
31880         }
31881         
31882         var file = this.renderPreview(response.data);
31883         
31884         this.files.push(file);
31885         
31886         this.arrange();
31887         
31888         this.fireEvent('afterupload', this, xhr);
31889         
31890     },
31891     
31892     xhrOnError : function(xhr)
31893     {
31894         Roo.log('xhr on error');
31895         
31896         var response = Roo.decode(xhr.responseText);
31897           
31898         Roo.log(response);
31899         
31900         this.arrange();
31901     },
31902     
31903     process : function(file)
31904     {
31905         if(this.fireEvent('process', this, file) !== false){
31906             if(this.editable && file.type.indexOf('image') != -1){
31907                 this.fireEvent('edit', this, file);
31908                 return;
31909             }
31910
31911             this.uploadStart(file, false);
31912
31913             return;
31914         }
31915         
31916     },
31917     
31918     uploadStart : function(file, crop)
31919     {
31920         this.xhr = new XMLHttpRequest();
31921         
31922         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31923             this.arrange();
31924             return;
31925         }
31926         
31927         file.xhr = this.xhr;
31928             
31929         this.managerEl.createChild({
31930             tag : 'div',
31931             cls : 'roo-document-manager-loading',
31932             cn : [
31933                 {
31934                     tag : 'div',
31935                     tooltip : file.name,
31936                     cls : 'roo-document-manager-thumb',
31937                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31938                 }
31939             ]
31940
31941         });
31942
31943         this.xhr.open(this.method, this.url, true);
31944         
31945         var headers = {
31946             "Accept": "application/json",
31947             "Cache-Control": "no-cache",
31948             "X-Requested-With": "XMLHttpRequest"
31949         };
31950         
31951         for (var headerName in headers) {
31952             var headerValue = headers[headerName];
31953             if (headerValue) {
31954                 this.xhr.setRequestHeader(headerName, headerValue);
31955             }
31956         }
31957         
31958         var _this = this;
31959         
31960         this.xhr.onload = function()
31961         {
31962             _this.xhrOnLoad(_this.xhr);
31963         }
31964         
31965         this.xhr.onerror = function()
31966         {
31967             _this.xhrOnError(_this.xhr);
31968         }
31969         
31970         var formData = new FormData();
31971
31972         formData.append('returnHTML', 'NO');
31973         
31974         if(crop){
31975             formData.append('crop', crop);
31976         }
31977         
31978         formData.append(this.paramName, file, file.name);
31979         
31980         var options = {
31981             file : file, 
31982             manually : false
31983         };
31984         
31985         if(this.fireEvent('prepare', this, formData, options) != false){
31986             
31987             if(options.manually){
31988                 return;
31989             }
31990             
31991             this.xhr.send(formData);
31992             return;
31993         };
31994         
31995         this.uploadCancel();
31996     },
31997     
31998     uploadCancel : function()
31999     {
32000         if (this.xhr) {
32001             this.xhr.abort();
32002         }
32003         
32004         this.delegates = [];
32005         
32006         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32007             el.remove();
32008         }, this);
32009         
32010         this.arrange();
32011     },
32012     
32013     renderPreview : function(file)
32014     {
32015         if(typeof(file.target) != 'undefined' && file.target){
32016             return file;
32017         }
32018         
32019         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32020         
32021         var previewEl = this.managerEl.createChild({
32022             tag : 'div',
32023             cls : 'roo-document-manager-preview',
32024             cn : [
32025                 {
32026                     tag : 'div',
32027                     tooltip : file[this.toolTipName],
32028                     cls : 'roo-document-manager-thumb',
32029                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32030                 },
32031                 {
32032                     tag : 'button',
32033                     cls : 'close',
32034                     html : '<i class="fa fa-times-circle"></i>'
32035                 }
32036             ]
32037         });
32038
32039         var close = previewEl.select('button.close', true).first();
32040
32041         close.on('click', this.onRemove, this, file);
32042
32043         file.target = previewEl;
32044
32045         var image = previewEl.select('img', true).first();
32046         
32047         var _this = this;
32048         
32049         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32050         
32051         image.on('click', this.onClick, this, file);
32052         
32053         this.fireEvent('previewrendered', this, file);
32054         
32055         return file;
32056         
32057     },
32058     
32059     onPreviewLoad : function(file, image)
32060     {
32061         if(typeof(file.target) == 'undefined' || !file.target){
32062             return;
32063         }
32064         
32065         var width = image.dom.naturalWidth || image.dom.width;
32066         var height = image.dom.naturalHeight || image.dom.height;
32067         
32068         if(!this.previewResize) {
32069             return;
32070         }
32071         
32072         if(width > height){
32073             file.target.addClass('wide');
32074             return;
32075         }
32076         
32077         file.target.addClass('tall');
32078         return;
32079         
32080     },
32081     
32082     uploadFromSource : function(file, crop)
32083     {
32084         this.xhr = new XMLHttpRequest();
32085         
32086         this.managerEl.createChild({
32087             tag : 'div',
32088             cls : 'roo-document-manager-loading',
32089             cn : [
32090                 {
32091                     tag : 'div',
32092                     tooltip : file.name,
32093                     cls : 'roo-document-manager-thumb',
32094                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32095                 }
32096             ]
32097
32098         });
32099
32100         this.xhr.open(this.method, this.url, true);
32101         
32102         var headers = {
32103             "Accept": "application/json",
32104             "Cache-Control": "no-cache",
32105             "X-Requested-With": "XMLHttpRequest"
32106         };
32107         
32108         for (var headerName in headers) {
32109             var headerValue = headers[headerName];
32110             if (headerValue) {
32111                 this.xhr.setRequestHeader(headerName, headerValue);
32112             }
32113         }
32114         
32115         var _this = this;
32116         
32117         this.xhr.onload = function()
32118         {
32119             _this.xhrOnLoad(_this.xhr);
32120         }
32121         
32122         this.xhr.onerror = function()
32123         {
32124             _this.xhrOnError(_this.xhr);
32125         }
32126         
32127         var formData = new FormData();
32128
32129         formData.append('returnHTML', 'NO');
32130         
32131         formData.append('crop', crop);
32132         
32133         if(typeof(file.filename) != 'undefined'){
32134             formData.append('filename', file.filename);
32135         }
32136         
32137         if(typeof(file.mimetype) != 'undefined'){
32138             formData.append('mimetype', file.mimetype);
32139         }
32140         
32141         Roo.log(formData);
32142         
32143         if(this.fireEvent('prepare', this, formData) != false){
32144             this.xhr.send(formData);
32145         };
32146     }
32147 });
32148
32149 /*
32150 * Licence: LGPL
32151 */
32152
32153 /**
32154  * @class Roo.bootstrap.DocumentViewer
32155  * @extends Roo.bootstrap.Component
32156  * Bootstrap DocumentViewer class
32157  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32158  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32159  * 
32160  * @constructor
32161  * Create a new DocumentViewer
32162  * @param {Object} config The config object
32163  */
32164
32165 Roo.bootstrap.DocumentViewer = function(config){
32166     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32167     
32168     this.addEvents({
32169         /**
32170          * @event initial
32171          * Fire after initEvent
32172          * @param {Roo.bootstrap.DocumentViewer} this
32173          */
32174         "initial" : true,
32175         /**
32176          * @event click
32177          * Fire after click
32178          * @param {Roo.bootstrap.DocumentViewer} this
32179          */
32180         "click" : true,
32181         /**
32182          * @event download
32183          * Fire after download button
32184          * @param {Roo.bootstrap.DocumentViewer} this
32185          */
32186         "download" : true,
32187         /**
32188          * @event trash
32189          * Fire after trash button
32190          * @param {Roo.bootstrap.DocumentViewer} this
32191          */
32192         "trash" : true
32193         
32194     });
32195 };
32196
32197 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32198     
32199     showDownload : true,
32200     
32201     showTrash : true,
32202     
32203     getAutoCreate : function()
32204     {
32205         var cfg = {
32206             tag : 'div',
32207             cls : 'roo-document-viewer',
32208             cn : [
32209                 {
32210                     tag : 'div',
32211                     cls : 'roo-document-viewer-body',
32212                     cn : [
32213                         {
32214                             tag : 'div',
32215                             cls : 'roo-document-viewer-thumb',
32216                             cn : [
32217                                 {
32218                                     tag : 'img',
32219                                     cls : 'roo-document-viewer-image'
32220                                 }
32221                             ]
32222                         }
32223                     ]
32224                 },
32225                 {
32226                     tag : 'div',
32227                     cls : 'roo-document-viewer-footer',
32228                     cn : {
32229                         tag : 'div',
32230                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32231                         cn : [
32232                             {
32233                                 tag : 'div',
32234                                 cls : 'btn-group roo-document-viewer-download',
32235                                 cn : [
32236                                     {
32237                                         tag : 'button',
32238                                         cls : 'btn btn-default',
32239                                         html : '<i class="fa fa-download"></i>'
32240                                     }
32241                                 ]
32242                             },
32243                             {
32244                                 tag : 'div',
32245                                 cls : 'btn-group roo-document-viewer-trash',
32246                                 cn : [
32247                                     {
32248                                         tag : 'button',
32249                                         cls : 'btn btn-default',
32250                                         html : '<i class="fa fa-trash"></i>'
32251                                     }
32252                                 ]
32253                             }
32254                         ]
32255                     }
32256                 }
32257             ]
32258         };
32259         
32260         return cfg;
32261     },
32262     
32263     initEvents : function()
32264     {
32265         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32266         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32267         
32268         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32269         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32270         
32271         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32272         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32273         
32274         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32275         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32276         
32277         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32278         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32279         
32280         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32281         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32282         
32283         this.bodyEl.on('click', this.onClick, this);
32284         this.downloadBtn.on('click', this.onDownload, this);
32285         this.trashBtn.on('click', this.onTrash, this);
32286         
32287         this.downloadBtn.hide();
32288         this.trashBtn.hide();
32289         
32290         if(this.showDownload){
32291             this.downloadBtn.show();
32292         }
32293         
32294         if(this.showTrash){
32295             this.trashBtn.show();
32296         }
32297         
32298         if(!this.showDownload && !this.showTrash) {
32299             this.footerEl.hide();
32300         }
32301         
32302     },
32303     
32304     initial : function()
32305     {
32306         this.fireEvent('initial', this);
32307         
32308     },
32309     
32310     onClick : function(e)
32311     {
32312         e.preventDefault();
32313         
32314         this.fireEvent('click', this);
32315     },
32316     
32317     onDownload : function(e)
32318     {
32319         e.preventDefault();
32320         
32321         this.fireEvent('download', this);
32322     },
32323     
32324     onTrash : function(e)
32325     {
32326         e.preventDefault();
32327         
32328         this.fireEvent('trash', this);
32329     }
32330     
32331 });
32332 /*
32333  * - LGPL
32334  *
32335  * nav progress bar
32336  * 
32337  */
32338
32339 /**
32340  * @class Roo.bootstrap.NavProgressBar
32341  * @extends Roo.bootstrap.Component
32342  * Bootstrap NavProgressBar class
32343  * 
32344  * @constructor
32345  * Create a new nav progress bar
32346  * @param {Object} config The config object
32347  */
32348
32349 Roo.bootstrap.NavProgressBar = function(config){
32350     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32351
32352     this.bullets = this.bullets || [];
32353    
32354 //    Roo.bootstrap.NavProgressBar.register(this);
32355      this.addEvents({
32356         /**
32357              * @event changed
32358              * Fires when the active item changes
32359              * @param {Roo.bootstrap.NavProgressBar} this
32360              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32361              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32362          */
32363         'changed': true
32364      });
32365     
32366 };
32367
32368 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32369     
32370     bullets : [],
32371     barItems : [],
32372     
32373     getAutoCreate : function()
32374     {
32375         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32376         
32377         cfg = {
32378             tag : 'div',
32379             cls : 'roo-navigation-bar-group',
32380             cn : [
32381                 {
32382                     tag : 'div',
32383                     cls : 'roo-navigation-top-bar'
32384                 },
32385                 {
32386                     tag : 'div',
32387                     cls : 'roo-navigation-bullets-bar',
32388                     cn : [
32389                         {
32390                             tag : 'ul',
32391                             cls : 'roo-navigation-bar'
32392                         }
32393                     ]
32394                 },
32395                 
32396                 {
32397                     tag : 'div',
32398                     cls : 'roo-navigation-bottom-bar'
32399                 }
32400             ]
32401             
32402         };
32403         
32404         return cfg;
32405         
32406     },
32407     
32408     initEvents: function() 
32409     {
32410         
32411     },
32412     
32413     onRender : function(ct, position) 
32414     {
32415         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32416         
32417         if(this.bullets.length){
32418             Roo.each(this.bullets, function(b){
32419                this.addItem(b);
32420             }, this);
32421         }
32422         
32423         this.format();
32424         
32425     },
32426     
32427     addItem : function(cfg)
32428     {
32429         var item = new Roo.bootstrap.NavProgressItem(cfg);
32430         
32431         item.parentId = this.id;
32432         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32433         
32434         if(cfg.html){
32435             var top = new Roo.bootstrap.Element({
32436                 tag : 'div',
32437                 cls : 'roo-navigation-bar-text'
32438             });
32439             
32440             var bottom = new Roo.bootstrap.Element({
32441                 tag : 'div',
32442                 cls : 'roo-navigation-bar-text'
32443             });
32444             
32445             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32446             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32447             
32448             var topText = new Roo.bootstrap.Element({
32449                 tag : 'span',
32450                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32451             });
32452             
32453             var bottomText = new Roo.bootstrap.Element({
32454                 tag : 'span',
32455                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32456             });
32457             
32458             topText.onRender(top.el, null);
32459             bottomText.onRender(bottom.el, null);
32460             
32461             item.topEl = top;
32462             item.bottomEl = bottom;
32463         }
32464         
32465         this.barItems.push(item);
32466         
32467         return item;
32468     },
32469     
32470     getActive : function()
32471     {
32472         var active = false;
32473         
32474         Roo.each(this.barItems, function(v){
32475             
32476             if (!v.isActive()) {
32477                 return;
32478             }
32479             
32480             active = v;
32481             return false;
32482             
32483         });
32484         
32485         return active;
32486     },
32487     
32488     setActiveItem : function(item)
32489     {
32490         var prev = false;
32491         
32492         Roo.each(this.barItems, function(v){
32493             if (v.rid == item.rid) {
32494                 return ;
32495             }
32496             
32497             if (v.isActive()) {
32498                 v.setActive(false);
32499                 prev = v;
32500             }
32501         });
32502
32503         item.setActive(true);
32504         
32505         this.fireEvent('changed', this, item, prev);
32506     },
32507     
32508     getBarItem: function(rid)
32509     {
32510         var ret = false;
32511         
32512         Roo.each(this.barItems, function(e) {
32513             if (e.rid != rid) {
32514                 return;
32515             }
32516             
32517             ret =  e;
32518             return false;
32519         });
32520         
32521         return ret;
32522     },
32523     
32524     indexOfItem : function(item)
32525     {
32526         var index = false;
32527         
32528         Roo.each(this.barItems, function(v, i){
32529             
32530             if (v.rid != item.rid) {
32531                 return;
32532             }
32533             
32534             index = i;
32535             return false
32536         });
32537         
32538         return index;
32539     },
32540     
32541     setActiveNext : function()
32542     {
32543         var i = this.indexOfItem(this.getActive());
32544         
32545         if (i > this.barItems.length) {
32546             return;
32547         }
32548         
32549         this.setActiveItem(this.barItems[i+1]);
32550     },
32551     
32552     setActivePrev : function()
32553     {
32554         var i = this.indexOfItem(this.getActive());
32555         
32556         if (i  < 1) {
32557             return;
32558         }
32559         
32560         this.setActiveItem(this.barItems[i-1]);
32561     },
32562     
32563     format : function()
32564     {
32565         if(!this.barItems.length){
32566             return;
32567         }
32568      
32569         var width = 100 / this.barItems.length;
32570         
32571         Roo.each(this.barItems, function(i){
32572             i.el.setStyle('width', width + '%');
32573             i.topEl.el.setStyle('width', width + '%');
32574             i.bottomEl.el.setStyle('width', width + '%');
32575         }, this);
32576         
32577     }
32578     
32579 });
32580 /*
32581  * - LGPL
32582  *
32583  * Nav Progress Item
32584  * 
32585  */
32586
32587 /**
32588  * @class Roo.bootstrap.NavProgressItem
32589  * @extends Roo.bootstrap.Component
32590  * Bootstrap NavProgressItem class
32591  * @cfg {String} rid the reference id
32592  * @cfg {Boolean} active (true|false) Is item active default false
32593  * @cfg {Boolean} disabled (true|false) Is item active default false
32594  * @cfg {String} html
32595  * @cfg {String} position (top|bottom) text position default bottom
32596  * @cfg {String} icon show icon instead of number
32597  * 
32598  * @constructor
32599  * Create a new NavProgressItem
32600  * @param {Object} config The config object
32601  */
32602 Roo.bootstrap.NavProgressItem = function(config){
32603     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32604     this.addEvents({
32605         // raw events
32606         /**
32607          * @event click
32608          * The raw click event for the entire grid.
32609          * @param {Roo.bootstrap.NavProgressItem} this
32610          * @param {Roo.EventObject} e
32611          */
32612         "click" : true
32613     });
32614    
32615 };
32616
32617 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32618     
32619     rid : '',
32620     active : false,
32621     disabled : false,
32622     html : '',
32623     position : 'bottom',
32624     icon : false,
32625     
32626     getAutoCreate : function()
32627     {
32628         var iconCls = 'roo-navigation-bar-item-icon';
32629         
32630         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32631         
32632         var cfg = {
32633             tag: 'li',
32634             cls: 'roo-navigation-bar-item',
32635             cn : [
32636                 {
32637                     tag : 'i',
32638                     cls : iconCls
32639                 }
32640             ]
32641         };
32642         
32643         if(this.active){
32644             cfg.cls += ' active';
32645         }
32646         if(this.disabled){
32647             cfg.cls += ' disabled';
32648         }
32649         
32650         return cfg;
32651     },
32652     
32653     disable : function()
32654     {
32655         this.setDisabled(true);
32656     },
32657     
32658     enable : function()
32659     {
32660         this.setDisabled(false);
32661     },
32662     
32663     initEvents: function() 
32664     {
32665         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32666         
32667         this.iconEl.on('click', this.onClick, this);
32668     },
32669     
32670     onClick : function(e)
32671     {
32672         e.preventDefault();
32673         
32674         if(this.disabled){
32675             return;
32676         }
32677         
32678         if(this.fireEvent('click', this, e) === false){
32679             return;
32680         };
32681         
32682         this.parent().setActiveItem(this);
32683     },
32684     
32685     isActive: function () 
32686     {
32687         return this.active;
32688     },
32689     
32690     setActive : function(state)
32691     {
32692         if(this.active == state){
32693             return;
32694         }
32695         
32696         this.active = state;
32697         
32698         if (state) {
32699             this.el.addClass('active');
32700             return;
32701         }
32702         
32703         this.el.removeClass('active');
32704         
32705         return;
32706     },
32707     
32708     setDisabled : function(state)
32709     {
32710         if(this.disabled == state){
32711             return;
32712         }
32713         
32714         this.disabled = state;
32715         
32716         if (state) {
32717             this.el.addClass('disabled');
32718             return;
32719         }
32720         
32721         this.el.removeClass('disabled');
32722     },
32723     
32724     tooltipEl : function()
32725     {
32726         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32727     }
32728 });
32729  
32730
32731  /*
32732  * - LGPL
32733  *
32734  * FieldLabel
32735  * 
32736  */
32737
32738 /**
32739  * @class Roo.bootstrap.FieldLabel
32740  * @extends Roo.bootstrap.Component
32741  * Bootstrap FieldLabel class
32742  * @cfg {String} html contents of the element
32743  * @cfg {String} tag tag of the element default label
32744  * @cfg {String} cls class of the element
32745  * @cfg {String} target label target 
32746  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32747  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32748  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32749  * @cfg {String} iconTooltip default "This field is required"
32750  * @cfg {String} indicatorpos (left|right) default left
32751  * 
32752  * @constructor
32753  * Create a new FieldLabel
32754  * @param {Object} config The config object
32755  */
32756
32757 Roo.bootstrap.FieldLabel = function(config){
32758     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32759     
32760     this.addEvents({
32761             /**
32762              * @event invalid
32763              * Fires after the field has been marked as invalid.
32764              * @param {Roo.form.FieldLabel} this
32765              * @param {String} msg The validation message
32766              */
32767             invalid : true,
32768             /**
32769              * @event valid
32770              * Fires after the field has been validated with no errors.
32771              * @param {Roo.form.FieldLabel} this
32772              */
32773             valid : true
32774         });
32775 };
32776
32777 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32778     
32779     tag: 'label',
32780     cls: '',
32781     html: '',
32782     target: '',
32783     allowBlank : true,
32784     invalidClass : 'has-warning',
32785     validClass : 'has-success',
32786     iconTooltip : 'This field is required',
32787     indicatorpos : 'left',
32788     
32789     getAutoCreate : function(){
32790         
32791         var cls = "";
32792         if (!this.allowBlank) {
32793             cls  = "visible";
32794         }
32795         
32796         var cfg = {
32797             tag : this.tag,
32798             cls : 'roo-bootstrap-field-label ' + this.cls,
32799             for : this.target,
32800             cn : [
32801                 {
32802                     tag : 'i',
32803                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32804                     tooltip : this.iconTooltip
32805                 },
32806                 {
32807                     tag : 'span',
32808                     html : this.html
32809                 }
32810             ] 
32811         };
32812         
32813         if(this.indicatorpos == 'right'){
32814             var cfg = {
32815                 tag : this.tag,
32816                 cls : 'roo-bootstrap-field-label ' + this.cls,
32817                 for : this.target,
32818                 cn : [
32819                     {
32820                         tag : 'span',
32821                         html : this.html
32822                     },
32823                     {
32824                         tag : 'i',
32825                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32826                         tooltip : this.iconTooltip
32827                     }
32828                 ] 
32829             };
32830         }
32831         
32832         return cfg;
32833     },
32834     
32835     initEvents: function() 
32836     {
32837         Roo.bootstrap.Element.superclass.initEvents.call(this);
32838         
32839         this.indicator = this.indicatorEl();
32840         
32841         if(this.indicator){
32842             this.indicator.removeClass('visible');
32843             this.indicator.addClass('invisible');
32844         }
32845         
32846         Roo.bootstrap.FieldLabel.register(this);
32847     },
32848     
32849     indicatorEl : function()
32850     {
32851         var indicator = this.el.select('i.roo-required-indicator',true).first();
32852         
32853         if(!indicator){
32854             return false;
32855         }
32856         
32857         return indicator;
32858         
32859     },
32860     
32861     /**
32862      * Mark this field as valid
32863      */
32864     markValid : function()
32865     {
32866         if(this.indicator){
32867             this.indicator.removeClass('visible');
32868             this.indicator.addClass('invisible');
32869         }
32870         if (Roo.bootstrap.version == 3) {
32871             this.el.removeClass(this.invalidClass);
32872             this.el.addClass(this.validClass);
32873         } else {
32874             this.el.removeClass('is-invalid');
32875             this.el.addClass('is-valid');
32876         }
32877         
32878         
32879         this.fireEvent('valid', this);
32880     },
32881     
32882     /**
32883      * Mark this field as invalid
32884      * @param {String} msg The validation message
32885      */
32886     markInvalid : function(msg)
32887     {
32888         if(this.indicator){
32889             this.indicator.removeClass('invisible');
32890             this.indicator.addClass('visible');
32891         }
32892           if (Roo.bootstrap.version == 3) {
32893             this.el.removeClass(this.validClass);
32894             this.el.addClass(this.invalidClass);
32895         } else {
32896             this.el.removeClass('is-valid');
32897             this.el.addClass('is-invalid');
32898         }
32899         
32900         
32901         this.fireEvent('invalid', this, msg);
32902     }
32903     
32904    
32905 });
32906
32907 Roo.apply(Roo.bootstrap.FieldLabel, {
32908     
32909     groups: {},
32910     
32911      /**
32912     * register a FieldLabel Group
32913     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32914     */
32915     register : function(label)
32916     {
32917         if(this.groups.hasOwnProperty(label.target)){
32918             return;
32919         }
32920      
32921         this.groups[label.target] = label;
32922         
32923     },
32924     /**
32925     * fetch a FieldLabel Group based on the target
32926     * @param {string} target
32927     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32928     */
32929     get: function(target) {
32930         if (typeof(this.groups[target]) == 'undefined') {
32931             return false;
32932         }
32933         
32934         return this.groups[target] ;
32935     }
32936 });
32937
32938  
32939
32940  /*
32941  * - LGPL
32942  *
32943  * page DateSplitField.
32944  * 
32945  */
32946
32947
32948 /**
32949  * @class Roo.bootstrap.DateSplitField
32950  * @extends Roo.bootstrap.Component
32951  * Bootstrap DateSplitField class
32952  * @cfg {string} fieldLabel - the label associated
32953  * @cfg {Number} labelWidth set the width of label (0-12)
32954  * @cfg {String} labelAlign (top|left)
32955  * @cfg {Boolean} dayAllowBlank (true|false) default false
32956  * @cfg {Boolean} monthAllowBlank (true|false) default false
32957  * @cfg {Boolean} yearAllowBlank (true|false) default false
32958  * @cfg {string} dayPlaceholder 
32959  * @cfg {string} monthPlaceholder
32960  * @cfg {string} yearPlaceholder
32961  * @cfg {string} dayFormat default 'd'
32962  * @cfg {string} monthFormat default 'm'
32963  * @cfg {string} yearFormat default 'Y'
32964  * @cfg {Number} labellg set the width of label (1-12)
32965  * @cfg {Number} labelmd set the width of label (1-12)
32966  * @cfg {Number} labelsm set the width of label (1-12)
32967  * @cfg {Number} labelxs set the width of label (1-12)
32968
32969  *     
32970  * @constructor
32971  * Create a new DateSplitField
32972  * @param {Object} config The config object
32973  */
32974
32975 Roo.bootstrap.DateSplitField = function(config){
32976     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32977     
32978     this.addEvents({
32979         // raw events
32980          /**
32981          * @event years
32982          * getting the data of years
32983          * @param {Roo.bootstrap.DateSplitField} this
32984          * @param {Object} years
32985          */
32986         "years" : true,
32987         /**
32988          * @event days
32989          * getting the data of days
32990          * @param {Roo.bootstrap.DateSplitField} this
32991          * @param {Object} days
32992          */
32993         "days" : true,
32994         /**
32995          * @event invalid
32996          * Fires after the field has been marked as invalid.
32997          * @param {Roo.form.Field} this
32998          * @param {String} msg The validation message
32999          */
33000         invalid : true,
33001        /**
33002          * @event valid
33003          * Fires after the field has been validated with no errors.
33004          * @param {Roo.form.Field} this
33005          */
33006         valid : true
33007     });
33008 };
33009
33010 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33011     
33012     fieldLabel : '',
33013     labelAlign : 'top',
33014     labelWidth : 3,
33015     dayAllowBlank : false,
33016     monthAllowBlank : false,
33017     yearAllowBlank : false,
33018     dayPlaceholder : '',
33019     monthPlaceholder : '',
33020     yearPlaceholder : '',
33021     dayFormat : 'd',
33022     monthFormat : 'm',
33023     yearFormat : 'Y',
33024     isFormField : true,
33025     labellg : 0,
33026     labelmd : 0,
33027     labelsm : 0,
33028     labelxs : 0,
33029     
33030     getAutoCreate : function()
33031     {
33032         var cfg = {
33033             tag : 'div',
33034             cls : 'row roo-date-split-field-group',
33035             cn : [
33036                 {
33037                     tag : 'input',
33038                     type : 'hidden',
33039                     cls : 'form-hidden-field roo-date-split-field-group-value',
33040                     name : this.name
33041                 }
33042             ]
33043         };
33044         
33045         var labelCls = 'col-md-12';
33046         var contentCls = 'col-md-4';
33047         
33048         if(this.fieldLabel){
33049             
33050             var label = {
33051                 tag : 'div',
33052                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33053                 cn : [
33054                     {
33055                         tag : 'label',
33056                         html : this.fieldLabel
33057                     }
33058                 ]
33059             };
33060             
33061             if(this.labelAlign == 'left'){
33062             
33063                 if(this.labelWidth > 12){
33064                     label.style = "width: " + this.labelWidth + 'px';
33065                 }
33066
33067                 if(this.labelWidth < 13 && this.labelmd == 0){
33068                     this.labelmd = this.labelWidth;
33069                 }
33070
33071                 if(this.labellg > 0){
33072                     labelCls = ' col-lg-' + this.labellg;
33073                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33074                 }
33075
33076                 if(this.labelmd > 0){
33077                     labelCls = ' col-md-' + this.labelmd;
33078                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33079                 }
33080
33081                 if(this.labelsm > 0){
33082                     labelCls = ' col-sm-' + this.labelsm;
33083                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33084                 }
33085
33086                 if(this.labelxs > 0){
33087                     labelCls = ' col-xs-' + this.labelxs;
33088                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33089                 }
33090             }
33091             
33092             label.cls += ' ' + labelCls;
33093             
33094             cfg.cn.push(label);
33095         }
33096         
33097         Roo.each(['day', 'month', 'year'], function(t){
33098             cfg.cn.push({
33099                 tag : 'div',
33100                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33101             });
33102         }, this);
33103         
33104         return cfg;
33105     },
33106     
33107     inputEl: function ()
33108     {
33109         return this.el.select('.roo-date-split-field-group-value', true).first();
33110     },
33111     
33112     onRender : function(ct, position) 
33113     {
33114         var _this = this;
33115         
33116         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33117         
33118         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33119         
33120         this.dayField = new Roo.bootstrap.ComboBox({
33121             allowBlank : this.dayAllowBlank,
33122             alwaysQuery : true,
33123             displayField : 'value',
33124             editable : false,
33125             fieldLabel : '',
33126             forceSelection : true,
33127             mode : 'local',
33128             placeholder : this.dayPlaceholder,
33129             selectOnFocus : true,
33130             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33131             triggerAction : 'all',
33132             typeAhead : true,
33133             valueField : 'value',
33134             store : new Roo.data.SimpleStore({
33135                 data : (function() {    
33136                     var days = [];
33137                     _this.fireEvent('days', _this, days);
33138                     return days;
33139                 })(),
33140                 fields : [ 'value' ]
33141             }),
33142             listeners : {
33143                 select : function (_self, record, index)
33144                 {
33145                     _this.setValue(_this.getValue());
33146                 }
33147             }
33148         });
33149
33150         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33151         
33152         this.monthField = new Roo.bootstrap.MonthField({
33153             after : '<i class=\"fa fa-calendar\"></i>',
33154             allowBlank : this.monthAllowBlank,
33155             placeholder : this.monthPlaceholder,
33156             readOnly : true,
33157             listeners : {
33158                 render : function (_self)
33159                 {
33160                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33161                         e.preventDefault();
33162                         _self.focus();
33163                     });
33164                 },
33165                 select : function (_self, oldvalue, newvalue)
33166                 {
33167                     _this.setValue(_this.getValue());
33168                 }
33169             }
33170         });
33171         
33172         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33173         
33174         this.yearField = new Roo.bootstrap.ComboBox({
33175             allowBlank : this.yearAllowBlank,
33176             alwaysQuery : true,
33177             displayField : 'value',
33178             editable : false,
33179             fieldLabel : '',
33180             forceSelection : true,
33181             mode : 'local',
33182             placeholder : this.yearPlaceholder,
33183             selectOnFocus : true,
33184             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33185             triggerAction : 'all',
33186             typeAhead : true,
33187             valueField : 'value',
33188             store : new Roo.data.SimpleStore({
33189                 data : (function() {
33190                     var years = [];
33191                     _this.fireEvent('years', _this, years);
33192                     return years;
33193                 })(),
33194                 fields : [ 'value' ]
33195             }),
33196             listeners : {
33197                 select : function (_self, record, index)
33198                 {
33199                     _this.setValue(_this.getValue());
33200                 }
33201             }
33202         });
33203
33204         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33205     },
33206     
33207     setValue : function(v, format)
33208     {
33209         this.inputEl.dom.value = v;
33210         
33211         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33212         
33213         var d = Date.parseDate(v, f);
33214         
33215         if(!d){
33216             this.validate();
33217             return;
33218         }
33219         
33220         this.setDay(d.format(this.dayFormat));
33221         this.setMonth(d.format(this.monthFormat));
33222         this.setYear(d.format(this.yearFormat));
33223         
33224         this.validate();
33225         
33226         return;
33227     },
33228     
33229     setDay : function(v)
33230     {
33231         this.dayField.setValue(v);
33232         this.inputEl.dom.value = this.getValue();
33233         this.validate();
33234         return;
33235     },
33236     
33237     setMonth : function(v)
33238     {
33239         this.monthField.setValue(v, true);
33240         this.inputEl.dom.value = this.getValue();
33241         this.validate();
33242         return;
33243     },
33244     
33245     setYear : function(v)
33246     {
33247         this.yearField.setValue(v);
33248         this.inputEl.dom.value = this.getValue();
33249         this.validate();
33250         return;
33251     },
33252     
33253     getDay : function()
33254     {
33255         return this.dayField.getValue();
33256     },
33257     
33258     getMonth : function()
33259     {
33260         return this.monthField.getValue();
33261     },
33262     
33263     getYear : function()
33264     {
33265         return this.yearField.getValue();
33266     },
33267     
33268     getValue : function()
33269     {
33270         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33271         
33272         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33273         
33274         return date;
33275     },
33276     
33277     reset : function()
33278     {
33279         this.setDay('');
33280         this.setMonth('');
33281         this.setYear('');
33282         this.inputEl.dom.value = '';
33283         this.validate();
33284         return;
33285     },
33286     
33287     validate : function()
33288     {
33289         var d = this.dayField.validate();
33290         var m = this.monthField.validate();
33291         var y = this.yearField.validate();
33292         
33293         var valid = true;
33294         
33295         if(
33296                 (!this.dayAllowBlank && !d) ||
33297                 (!this.monthAllowBlank && !m) ||
33298                 (!this.yearAllowBlank && !y)
33299         ){
33300             valid = false;
33301         }
33302         
33303         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33304             return valid;
33305         }
33306         
33307         if(valid){
33308             this.markValid();
33309             return valid;
33310         }
33311         
33312         this.markInvalid();
33313         
33314         return valid;
33315     },
33316     
33317     markValid : function()
33318     {
33319         
33320         var label = this.el.select('label', true).first();
33321         var icon = this.el.select('i.fa-star', true).first();
33322
33323         if(label && icon){
33324             icon.remove();
33325         }
33326         
33327         this.fireEvent('valid', this);
33328     },
33329     
33330      /**
33331      * Mark this field as invalid
33332      * @param {String} msg The validation message
33333      */
33334     markInvalid : function(msg)
33335     {
33336         
33337         var label = this.el.select('label', true).first();
33338         var icon = this.el.select('i.fa-star', true).first();
33339
33340         if(label && !icon){
33341             this.el.select('.roo-date-split-field-label', true).createChild({
33342                 tag : 'i',
33343                 cls : 'text-danger fa fa-lg fa-star',
33344                 tooltip : 'This field is required',
33345                 style : 'margin-right:5px;'
33346             }, label, true);
33347         }
33348         
33349         this.fireEvent('invalid', this, msg);
33350     },
33351     
33352     clearInvalid : function()
33353     {
33354         var label = this.el.select('label', true).first();
33355         var icon = this.el.select('i.fa-star', true).first();
33356
33357         if(label && icon){
33358             icon.remove();
33359         }
33360         
33361         this.fireEvent('valid', this);
33362     },
33363     
33364     getName: function()
33365     {
33366         return this.name;
33367     }
33368     
33369 });
33370
33371  /**
33372  *
33373  * This is based on 
33374  * http://masonry.desandro.com
33375  *
33376  * The idea is to render all the bricks based on vertical width...
33377  *
33378  * The original code extends 'outlayer' - we might need to use that....
33379  * 
33380  */
33381
33382
33383 /**
33384  * @class Roo.bootstrap.LayoutMasonry
33385  * @extends Roo.bootstrap.Component
33386  * Bootstrap Layout Masonry class
33387  * 
33388  * @constructor
33389  * Create a new Element
33390  * @param {Object} config The config object
33391  */
33392
33393 Roo.bootstrap.LayoutMasonry = function(config){
33394     
33395     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33396     
33397     this.bricks = [];
33398     
33399     Roo.bootstrap.LayoutMasonry.register(this);
33400     
33401     this.addEvents({
33402         // raw events
33403         /**
33404          * @event layout
33405          * Fire after layout the items
33406          * @param {Roo.bootstrap.LayoutMasonry} this
33407          * @param {Roo.EventObject} e
33408          */
33409         "layout" : true
33410     });
33411     
33412 };
33413
33414 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33415     
33416     /**
33417      * @cfg {Boolean} isLayoutInstant = no animation?
33418      */   
33419     isLayoutInstant : false, // needed?
33420    
33421     /**
33422      * @cfg {Number} boxWidth  width of the columns
33423      */   
33424     boxWidth : 450,
33425     
33426       /**
33427      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33428      */   
33429     boxHeight : 0,
33430     
33431     /**
33432      * @cfg {Number} padWidth padding below box..
33433      */   
33434     padWidth : 10, 
33435     
33436     /**
33437      * @cfg {Number} gutter gutter width..
33438      */   
33439     gutter : 10,
33440     
33441      /**
33442      * @cfg {Number} maxCols maximum number of columns
33443      */   
33444     
33445     maxCols: 0,
33446     
33447     /**
33448      * @cfg {Boolean} isAutoInitial defalut true
33449      */   
33450     isAutoInitial : true, 
33451     
33452     containerWidth: 0,
33453     
33454     /**
33455      * @cfg {Boolean} isHorizontal defalut false
33456      */   
33457     isHorizontal : false, 
33458
33459     currentSize : null,
33460     
33461     tag: 'div',
33462     
33463     cls: '',
33464     
33465     bricks: null, //CompositeElement
33466     
33467     cols : 1,
33468     
33469     _isLayoutInited : false,
33470     
33471 //    isAlternative : false, // only use for vertical layout...
33472     
33473     /**
33474      * @cfg {Number} alternativePadWidth padding below box..
33475      */   
33476     alternativePadWidth : 50,
33477     
33478     selectedBrick : [],
33479     
33480     getAutoCreate : function(){
33481         
33482         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33483         
33484         var cfg = {
33485             tag: this.tag,
33486             cls: 'blog-masonary-wrapper ' + this.cls,
33487             cn : {
33488                 cls : 'mas-boxes masonary'
33489             }
33490         };
33491         
33492         return cfg;
33493     },
33494     
33495     getChildContainer: function( )
33496     {
33497         if (this.boxesEl) {
33498             return this.boxesEl;
33499         }
33500         
33501         this.boxesEl = this.el.select('.mas-boxes').first();
33502         
33503         return this.boxesEl;
33504     },
33505     
33506     
33507     initEvents : function()
33508     {
33509         var _this = this;
33510         
33511         if(this.isAutoInitial){
33512             Roo.log('hook children rendered');
33513             this.on('childrenrendered', function() {
33514                 Roo.log('children rendered');
33515                 _this.initial();
33516             } ,this);
33517         }
33518     },
33519     
33520     initial : function()
33521     {
33522         this.selectedBrick = [];
33523         
33524         this.currentSize = this.el.getBox(true);
33525         
33526         Roo.EventManager.onWindowResize(this.resize, this); 
33527
33528         if(!this.isAutoInitial){
33529             this.layout();
33530             return;
33531         }
33532         
33533         this.layout();
33534         
33535         return;
33536         //this.layout.defer(500,this);
33537         
33538     },
33539     
33540     resize : function()
33541     {
33542         var cs = this.el.getBox(true);
33543         
33544         if (
33545                 this.currentSize.width == cs.width && 
33546                 this.currentSize.x == cs.x && 
33547                 this.currentSize.height == cs.height && 
33548                 this.currentSize.y == cs.y 
33549         ) {
33550             Roo.log("no change in with or X or Y");
33551             return;
33552         }
33553         
33554         this.currentSize = cs;
33555         
33556         this.layout();
33557         
33558     },
33559     
33560     layout : function()
33561     {   
33562         this._resetLayout();
33563         
33564         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33565         
33566         this.layoutItems( isInstant );
33567       
33568         this._isLayoutInited = true;
33569         
33570         this.fireEvent('layout', this);
33571         
33572     },
33573     
33574     _resetLayout : function()
33575     {
33576         if(this.isHorizontal){
33577             this.horizontalMeasureColumns();
33578             return;
33579         }
33580         
33581         this.verticalMeasureColumns();
33582         
33583     },
33584     
33585     verticalMeasureColumns : function()
33586     {
33587         this.getContainerWidth();
33588         
33589 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33590 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33591 //            return;
33592 //        }
33593         
33594         var boxWidth = this.boxWidth + this.padWidth;
33595         
33596         if(this.containerWidth < this.boxWidth){
33597             boxWidth = this.containerWidth
33598         }
33599         
33600         var containerWidth = this.containerWidth;
33601         
33602         var cols = Math.floor(containerWidth / boxWidth);
33603         
33604         this.cols = Math.max( cols, 1 );
33605         
33606         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33607         
33608         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33609         
33610         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33611         
33612         this.colWidth = boxWidth + avail - this.padWidth;
33613         
33614         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33615         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33616     },
33617     
33618     horizontalMeasureColumns : function()
33619     {
33620         this.getContainerWidth();
33621         
33622         var boxWidth = this.boxWidth;
33623         
33624         if(this.containerWidth < boxWidth){
33625             boxWidth = this.containerWidth;
33626         }
33627         
33628         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33629         
33630         this.el.setHeight(boxWidth);
33631         
33632     },
33633     
33634     getContainerWidth : function()
33635     {
33636         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33637     },
33638     
33639     layoutItems : function( isInstant )
33640     {
33641         Roo.log(this.bricks);
33642         
33643         var items = Roo.apply([], this.bricks);
33644         
33645         if(this.isHorizontal){
33646             this._horizontalLayoutItems( items , isInstant );
33647             return;
33648         }
33649         
33650 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33651 //            this._verticalAlternativeLayoutItems( items , isInstant );
33652 //            return;
33653 //        }
33654         
33655         this._verticalLayoutItems( items , isInstant );
33656         
33657     },
33658     
33659     _verticalLayoutItems : function ( items , isInstant)
33660     {
33661         if ( !items || !items.length ) {
33662             return;
33663         }
33664         
33665         var standard = [
33666             ['xs', 'xs', 'xs', 'tall'],
33667             ['xs', 'xs', 'tall'],
33668             ['xs', 'xs', 'sm'],
33669             ['xs', 'xs', 'xs'],
33670             ['xs', 'tall'],
33671             ['xs', 'sm'],
33672             ['xs', 'xs'],
33673             ['xs'],
33674             
33675             ['sm', 'xs', 'xs'],
33676             ['sm', 'xs'],
33677             ['sm'],
33678             
33679             ['tall', 'xs', 'xs', 'xs'],
33680             ['tall', 'xs', 'xs'],
33681             ['tall', 'xs'],
33682             ['tall']
33683             
33684         ];
33685         
33686         var queue = [];
33687         
33688         var boxes = [];
33689         
33690         var box = [];
33691         
33692         Roo.each(items, function(item, k){
33693             
33694             switch (item.size) {
33695                 // these layouts take up a full box,
33696                 case 'md' :
33697                 case 'md-left' :
33698                 case 'md-right' :
33699                 case 'wide' :
33700                     
33701                     if(box.length){
33702                         boxes.push(box);
33703                         box = [];
33704                     }
33705                     
33706                     boxes.push([item]);
33707                     
33708                     break;
33709                     
33710                 case 'xs' :
33711                 case 'sm' :
33712                 case 'tall' :
33713                     
33714                     box.push(item);
33715                     
33716                     break;
33717                 default :
33718                     break;
33719                     
33720             }
33721             
33722         }, this);
33723         
33724         if(box.length){
33725             boxes.push(box);
33726             box = [];
33727         }
33728         
33729         var filterPattern = function(box, length)
33730         {
33731             if(!box.length){
33732                 return;
33733             }
33734             
33735             var match = false;
33736             
33737             var pattern = box.slice(0, length);
33738             
33739             var format = [];
33740             
33741             Roo.each(pattern, function(i){
33742                 format.push(i.size);
33743             }, this);
33744             
33745             Roo.each(standard, function(s){
33746                 
33747                 if(String(s) != String(format)){
33748                     return;
33749                 }
33750                 
33751                 match = true;
33752                 return false;
33753                 
33754             }, this);
33755             
33756             if(!match && length == 1){
33757                 return;
33758             }
33759             
33760             if(!match){
33761                 filterPattern(box, length - 1);
33762                 return;
33763             }
33764                 
33765             queue.push(pattern);
33766
33767             box = box.slice(length, box.length);
33768
33769             filterPattern(box, 4);
33770
33771             return;
33772             
33773         }
33774         
33775         Roo.each(boxes, function(box, k){
33776             
33777             if(!box.length){
33778                 return;
33779             }
33780             
33781             if(box.length == 1){
33782                 queue.push(box);
33783                 return;
33784             }
33785             
33786             filterPattern(box, 4);
33787             
33788         }, this);
33789         
33790         this._processVerticalLayoutQueue( queue, isInstant );
33791         
33792     },
33793     
33794 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33795 //    {
33796 //        if ( !items || !items.length ) {
33797 //            return;
33798 //        }
33799 //
33800 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33801 //        
33802 //    },
33803     
33804     _horizontalLayoutItems : function ( items , isInstant)
33805     {
33806         if ( !items || !items.length || items.length < 3) {
33807             return;
33808         }
33809         
33810         items.reverse();
33811         
33812         var eItems = items.slice(0, 3);
33813         
33814         items = items.slice(3, items.length);
33815         
33816         var standard = [
33817             ['xs', 'xs', 'xs', 'wide'],
33818             ['xs', 'xs', 'wide'],
33819             ['xs', 'xs', 'sm'],
33820             ['xs', 'xs', 'xs'],
33821             ['xs', 'wide'],
33822             ['xs', 'sm'],
33823             ['xs', 'xs'],
33824             ['xs'],
33825             
33826             ['sm', 'xs', 'xs'],
33827             ['sm', 'xs'],
33828             ['sm'],
33829             
33830             ['wide', 'xs', 'xs', 'xs'],
33831             ['wide', 'xs', 'xs'],
33832             ['wide', 'xs'],
33833             ['wide'],
33834             
33835             ['wide-thin']
33836         ];
33837         
33838         var queue = [];
33839         
33840         var boxes = [];
33841         
33842         var box = [];
33843         
33844         Roo.each(items, function(item, k){
33845             
33846             switch (item.size) {
33847                 case 'md' :
33848                 case 'md-left' :
33849                 case 'md-right' :
33850                 case 'tall' :
33851                     
33852                     if(box.length){
33853                         boxes.push(box);
33854                         box = [];
33855                     }
33856                     
33857                     boxes.push([item]);
33858                     
33859                     break;
33860                     
33861                 case 'xs' :
33862                 case 'sm' :
33863                 case 'wide' :
33864                 case 'wide-thin' :
33865                     
33866                     box.push(item);
33867                     
33868                     break;
33869                 default :
33870                     break;
33871                     
33872             }
33873             
33874         }, this);
33875         
33876         if(box.length){
33877             boxes.push(box);
33878             box = [];
33879         }
33880         
33881         var filterPattern = function(box, length)
33882         {
33883             if(!box.length){
33884                 return;
33885             }
33886             
33887             var match = false;
33888             
33889             var pattern = box.slice(0, length);
33890             
33891             var format = [];
33892             
33893             Roo.each(pattern, function(i){
33894                 format.push(i.size);
33895             }, this);
33896             
33897             Roo.each(standard, function(s){
33898                 
33899                 if(String(s) != String(format)){
33900                     return;
33901                 }
33902                 
33903                 match = true;
33904                 return false;
33905                 
33906             }, this);
33907             
33908             if(!match && length == 1){
33909                 return;
33910             }
33911             
33912             if(!match){
33913                 filterPattern(box, length - 1);
33914                 return;
33915             }
33916                 
33917             queue.push(pattern);
33918
33919             box = box.slice(length, box.length);
33920
33921             filterPattern(box, 4);
33922
33923             return;
33924             
33925         }
33926         
33927         Roo.each(boxes, function(box, k){
33928             
33929             if(!box.length){
33930                 return;
33931             }
33932             
33933             if(box.length == 1){
33934                 queue.push(box);
33935                 return;
33936             }
33937             
33938             filterPattern(box, 4);
33939             
33940         }, this);
33941         
33942         
33943         var prune = [];
33944         
33945         var pos = this.el.getBox(true);
33946         
33947         var minX = pos.x;
33948         
33949         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33950         
33951         var hit_end = false;
33952         
33953         Roo.each(queue, function(box){
33954             
33955             if(hit_end){
33956                 
33957                 Roo.each(box, function(b){
33958                 
33959                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33960                     b.el.hide();
33961
33962                 }, this);
33963
33964                 return;
33965             }
33966             
33967             var mx = 0;
33968             
33969             Roo.each(box, function(b){
33970                 
33971                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33972                 b.el.show();
33973
33974                 mx = Math.max(mx, b.x);
33975                 
33976             }, this);
33977             
33978             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33979             
33980             if(maxX < minX){
33981                 
33982                 Roo.each(box, function(b){
33983                 
33984                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33985                     b.el.hide();
33986                     
33987                 }, this);
33988                 
33989                 hit_end = true;
33990                 
33991                 return;
33992             }
33993             
33994             prune.push(box);
33995             
33996         }, this);
33997         
33998         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33999     },
34000     
34001     /** Sets position of item in DOM
34002     * @param {Element} item
34003     * @param {Number} x - horizontal position
34004     * @param {Number} y - vertical position
34005     * @param {Boolean} isInstant - disables transitions
34006     */
34007     _processVerticalLayoutQueue : function( queue, isInstant )
34008     {
34009         var pos = this.el.getBox(true);
34010         var x = pos.x;
34011         var y = pos.y;
34012         var maxY = [];
34013         
34014         for (var i = 0; i < this.cols; i++){
34015             maxY[i] = pos.y;
34016         }
34017         
34018         Roo.each(queue, function(box, k){
34019             
34020             var col = k % this.cols;
34021             
34022             Roo.each(box, function(b,kk){
34023                 
34024                 b.el.position('absolute');
34025                 
34026                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34027                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34028                 
34029                 if(b.size == 'md-left' || b.size == 'md-right'){
34030                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34031                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34032                 }
34033                 
34034                 b.el.setWidth(width);
34035                 b.el.setHeight(height);
34036                 // iframe?
34037                 b.el.select('iframe',true).setSize(width,height);
34038                 
34039             }, this);
34040             
34041             for (var i = 0; i < this.cols; i++){
34042                 
34043                 if(maxY[i] < maxY[col]){
34044                     col = i;
34045                     continue;
34046                 }
34047                 
34048                 col = Math.min(col, i);
34049                 
34050             }
34051             
34052             x = pos.x + col * (this.colWidth + this.padWidth);
34053             
34054             y = maxY[col];
34055             
34056             var positions = [];
34057             
34058             switch (box.length){
34059                 case 1 :
34060                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34061                     break;
34062                 case 2 :
34063                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34064                     break;
34065                 case 3 :
34066                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34067                     break;
34068                 case 4 :
34069                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34070                     break;
34071                 default :
34072                     break;
34073             }
34074             
34075             Roo.each(box, function(b,kk){
34076                 
34077                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34078                 
34079                 var sz = b.el.getSize();
34080                 
34081                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34082                 
34083             }, this);
34084             
34085         }, this);
34086         
34087         var mY = 0;
34088         
34089         for (var i = 0; i < this.cols; i++){
34090             mY = Math.max(mY, maxY[i]);
34091         }
34092         
34093         this.el.setHeight(mY - pos.y);
34094         
34095     },
34096     
34097 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34098 //    {
34099 //        var pos = this.el.getBox(true);
34100 //        var x = pos.x;
34101 //        var y = pos.y;
34102 //        var maxX = pos.right;
34103 //        
34104 //        var maxHeight = 0;
34105 //        
34106 //        Roo.each(items, function(item, k){
34107 //            
34108 //            var c = k % 2;
34109 //            
34110 //            item.el.position('absolute');
34111 //                
34112 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34113 //
34114 //            item.el.setWidth(width);
34115 //
34116 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34117 //
34118 //            item.el.setHeight(height);
34119 //            
34120 //            if(c == 0){
34121 //                item.el.setXY([x, y], isInstant ? false : true);
34122 //            } else {
34123 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34124 //            }
34125 //            
34126 //            y = y + height + this.alternativePadWidth;
34127 //            
34128 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34129 //            
34130 //        }, this);
34131 //        
34132 //        this.el.setHeight(maxHeight);
34133 //        
34134 //    },
34135     
34136     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34137     {
34138         var pos = this.el.getBox(true);
34139         
34140         var minX = pos.x;
34141         var minY = pos.y;
34142         
34143         var maxX = pos.right;
34144         
34145         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34146         
34147         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34148         
34149         Roo.each(queue, function(box, k){
34150             
34151             Roo.each(box, function(b, kk){
34152                 
34153                 b.el.position('absolute');
34154                 
34155                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34156                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34157                 
34158                 if(b.size == 'md-left' || b.size == 'md-right'){
34159                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34160                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34161                 }
34162                 
34163                 b.el.setWidth(width);
34164                 b.el.setHeight(height);
34165                 
34166             }, this);
34167             
34168             if(!box.length){
34169                 return;
34170             }
34171             
34172             var positions = [];
34173             
34174             switch (box.length){
34175                 case 1 :
34176                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34177                     break;
34178                 case 2 :
34179                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34180                     break;
34181                 case 3 :
34182                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34183                     break;
34184                 case 4 :
34185                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34186                     break;
34187                 default :
34188                     break;
34189             }
34190             
34191             Roo.each(box, function(b,kk){
34192                 
34193                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34194                 
34195                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34196                 
34197             }, this);
34198             
34199         }, this);
34200         
34201     },
34202     
34203     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34204     {
34205         Roo.each(eItems, function(b,k){
34206             
34207             b.size = (k == 0) ? 'sm' : 'xs';
34208             b.x = (k == 0) ? 2 : 1;
34209             b.y = (k == 0) ? 2 : 1;
34210             
34211             b.el.position('absolute');
34212             
34213             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34214                 
34215             b.el.setWidth(width);
34216             
34217             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34218             
34219             b.el.setHeight(height);
34220             
34221         }, this);
34222
34223         var positions = [];
34224         
34225         positions.push({
34226             x : maxX - this.unitWidth * 2 - this.gutter,
34227             y : minY
34228         });
34229         
34230         positions.push({
34231             x : maxX - this.unitWidth,
34232             y : minY + (this.unitWidth + this.gutter) * 2
34233         });
34234         
34235         positions.push({
34236             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34237             y : minY
34238         });
34239         
34240         Roo.each(eItems, function(b,k){
34241             
34242             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34243
34244         }, this);
34245         
34246     },
34247     
34248     getVerticalOneBoxColPositions : function(x, y, box)
34249     {
34250         var pos = [];
34251         
34252         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34253         
34254         if(box[0].size == 'md-left'){
34255             rand = 0;
34256         }
34257         
34258         if(box[0].size == 'md-right'){
34259             rand = 1;
34260         }
34261         
34262         pos.push({
34263             x : x + (this.unitWidth + this.gutter) * rand,
34264             y : y
34265         });
34266         
34267         return pos;
34268     },
34269     
34270     getVerticalTwoBoxColPositions : function(x, y, box)
34271     {
34272         var pos = [];
34273         
34274         if(box[0].size == 'xs'){
34275             
34276             pos.push({
34277                 x : x,
34278                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34279             });
34280
34281             pos.push({
34282                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34283                 y : y
34284             });
34285             
34286             return pos;
34287             
34288         }
34289         
34290         pos.push({
34291             x : x,
34292             y : y
34293         });
34294
34295         pos.push({
34296             x : x + (this.unitWidth + this.gutter) * 2,
34297             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34298         });
34299         
34300         return pos;
34301         
34302     },
34303     
34304     getVerticalThreeBoxColPositions : function(x, y, box)
34305     {
34306         var pos = [];
34307         
34308         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34309             
34310             pos.push({
34311                 x : x,
34312                 y : y
34313             });
34314
34315             pos.push({
34316                 x : x + (this.unitWidth + this.gutter) * 1,
34317                 y : y
34318             });
34319             
34320             pos.push({
34321                 x : x + (this.unitWidth + this.gutter) * 2,
34322                 y : y
34323             });
34324             
34325             return pos;
34326             
34327         }
34328         
34329         if(box[0].size == 'xs' && box[1].size == 'xs'){
34330             
34331             pos.push({
34332                 x : x,
34333                 y : y
34334             });
34335
34336             pos.push({
34337                 x : x,
34338                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34339             });
34340             
34341             pos.push({
34342                 x : x + (this.unitWidth + this.gutter) * 1,
34343                 y : y
34344             });
34345             
34346             return pos;
34347             
34348         }
34349         
34350         pos.push({
34351             x : x,
34352             y : y
34353         });
34354
34355         pos.push({
34356             x : x + (this.unitWidth + this.gutter) * 2,
34357             y : y
34358         });
34359
34360         pos.push({
34361             x : x + (this.unitWidth + this.gutter) * 2,
34362             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34363         });
34364             
34365         return pos;
34366         
34367     },
34368     
34369     getVerticalFourBoxColPositions : function(x, y, box)
34370     {
34371         var pos = [];
34372         
34373         if(box[0].size == 'xs'){
34374             
34375             pos.push({
34376                 x : x,
34377                 y : y
34378             });
34379
34380             pos.push({
34381                 x : x,
34382                 y : y + (this.unitHeight + this.gutter) * 1
34383             });
34384             
34385             pos.push({
34386                 x : x,
34387                 y : y + (this.unitHeight + this.gutter) * 2
34388             });
34389             
34390             pos.push({
34391                 x : x + (this.unitWidth + this.gutter) * 1,
34392                 y : y
34393             });
34394             
34395             return pos;
34396             
34397         }
34398         
34399         pos.push({
34400             x : x,
34401             y : y
34402         });
34403
34404         pos.push({
34405             x : x + (this.unitWidth + this.gutter) * 2,
34406             y : y
34407         });
34408
34409         pos.push({
34410             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34411             y : y + (this.unitHeight + this.gutter) * 1
34412         });
34413
34414         pos.push({
34415             x : x + (this.unitWidth + this.gutter) * 2,
34416             y : y + (this.unitWidth + this.gutter) * 2
34417         });
34418
34419         return pos;
34420         
34421     },
34422     
34423     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34424     {
34425         var pos = [];
34426         
34427         if(box[0].size == 'md-left'){
34428             pos.push({
34429                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34430                 y : minY
34431             });
34432             
34433             return pos;
34434         }
34435         
34436         if(box[0].size == 'md-right'){
34437             pos.push({
34438                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34439                 y : minY + (this.unitWidth + this.gutter) * 1
34440             });
34441             
34442             return pos;
34443         }
34444         
34445         var rand = Math.floor(Math.random() * (4 - box[0].y));
34446         
34447         pos.push({
34448             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34449             y : minY + (this.unitWidth + this.gutter) * rand
34450         });
34451         
34452         return pos;
34453         
34454     },
34455     
34456     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34457     {
34458         var pos = [];
34459         
34460         if(box[0].size == 'xs'){
34461             
34462             pos.push({
34463                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34464                 y : minY
34465             });
34466
34467             pos.push({
34468                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34469                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34470             });
34471             
34472             return pos;
34473             
34474         }
34475         
34476         pos.push({
34477             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34478             y : minY
34479         });
34480
34481         pos.push({
34482             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34483             y : minY + (this.unitWidth + this.gutter) * 2
34484         });
34485         
34486         return pos;
34487         
34488     },
34489     
34490     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34491     {
34492         var pos = [];
34493         
34494         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34495             
34496             pos.push({
34497                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34498                 y : minY
34499             });
34500
34501             pos.push({
34502                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34503                 y : minY + (this.unitWidth + this.gutter) * 1
34504             });
34505             
34506             pos.push({
34507                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34508                 y : minY + (this.unitWidth + this.gutter) * 2
34509             });
34510             
34511             return pos;
34512             
34513         }
34514         
34515         if(box[0].size == 'xs' && box[1].size == 'xs'){
34516             
34517             pos.push({
34518                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34519                 y : minY
34520             });
34521
34522             pos.push({
34523                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34524                 y : minY
34525             });
34526             
34527             pos.push({
34528                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34529                 y : minY + (this.unitWidth + this.gutter) * 1
34530             });
34531             
34532             return pos;
34533             
34534         }
34535         
34536         pos.push({
34537             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34538             y : minY
34539         });
34540
34541         pos.push({
34542             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34543             y : minY + (this.unitWidth + this.gutter) * 2
34544         });
34545
34546         pos.push({
34547             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34548             y : minY + (this.unitWidth + this.gutter) * 2
34549         });
34550             
34551         return pos;
34552         
34553     },
34554     
34555     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34556     {
34557         var pos = [];
34558         
34559         if(box[0].size == 'xs'){
34560             
34561             pos.push({
34562                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34563                 y : minY
34564             });
34565
34566             pos.push({
34567                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34568                 y : minY
34569             });
34570             
34571             pos.push({
34572                 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),
34573                 y : minY
34574             });
34575             
34576             pos.push({
34577                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34578                 y : minY + (this.unitWidth + this.gutter) * 1
34579             });
34580             
34581             return pos;
34582             
34583         }
34584         
34585         pos.push({
34586             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34587             y : minY
34588         });
34589         
34590         pos.push({
34591             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34592             y : minY + (this.unitWidth + this.gutter) * 2
34593         });
34594         
34595         pos.push({
34596             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34597             y : minY + (this.unitWidth + this.gutter) * 2
34598         });
34599         
34600         pos.push({
34601             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),
34602             y : minY + (this.unitWidth + this.gutter) * 2
34603         });
34604
34605         return pos;
34606         
34607     },
34608     
34609     /**
34610     * remove a Masonry Brick
34611     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34612     */
34613     removeBrick : function(brick_id)
34614     {
34615         if (!brick_id) {
34616             return;
34617         }
34618         
34619         for (var i = 0; i<this.bricks.length; i++) {
34620             if (this.bricks[i].id == brick_id) {
34621                 this.bricks.splice(i,1);
34622                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34623                 this.initial();
34624             }
34625         }
34626     },
34627     
34628     /**
34629     * adds a Masonry Brick
34630     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34631     */
34632     addBrick : function(cfg)
34633     {
34634         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34635         //this.register(cn);
34636         cn.parentId = this.id;
34637         cn.render(this.el);
34638         return cn;
34639     },
34640     
34641     /**
34642     * register a Masonry Brick
34643     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34644     */
34645     
34646     register : function(brick)
34647     {
34648         this.bricks.push(brick);
34649         brick.masonryId = this.id;
34650     },
34651     
34652     /**
34653     * clear all the Masonry Brick
34654     */
34655     clearAll : function()
34656     {
34657         this.bricks = [];
34658         //this.getChildContainer().dom.innerHTML = "";
34659         this.el.dom.innerHTML = '';
34660     },
34661     
34662     getSelected : function()
34663     {
34664         if (!this.selectedBrick) {
34665             return false;
34666         }
34667         
34668         return this.selectedBrick;
34669     }
34670 });
34671
34672 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34673     
34674     groups: {},
34675      /**
34676     * register a Masonry Layout
34677     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34678     */
34679     
34680     register : function(layout)
34681     {
34682         this.groups[layout.id] = layout;
34683     },
34684     /**
34685     * fetch a  Masonry Layout based on the masonry layout ID
34686     * @param {string} the masonry layout to add
34687     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34688     */
34689     
34690     get: function(layout_id) {
34691         if (typeof(this.groups[layout_id]) == 'undefined') {
34692             return false;
34693         }
34694         return this.groups[layout_id] ;
34695     }
34696     
34697     
34698     
34699 });
34700
34701  
34702
34703  /**
34704  *
34705  * This is based on 
34706  * http://masonry.desandro.com
34707  *
34708  * The idea is to render all the bricks based on vertical width...
34709  *
34710  * The original code extends 'outlayer' - we might need to use that....
34711  * 
34712  */
34713
34714
34715 /**
34716  * @class Roo.bootstrap.LayoutMasonryAuto
34717  * @extends Roo.bootstrap.Component
34718  * Bootstrap Layout Masonry class
34719  * 
34720  * @constructor
34721  * Create a new Element
34722  * @param {Object} config The config object
34723  */
34724
34725 Roo.bootstrap.LayoutMasonryAuto = function(config){
34726     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34727 };
34728
34729 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34730     
34731       /**
34732      * @cfg {Boolean} isFitWidth  - resize the width..
34733      */   
34734     isFitWidth : false,  // options..
34735     /**
34736      * @cfg {Boolean} isOriginLeft = left align?
34737      */   
34738     isOriginLeft : true,
34739     /**
34740      * @cfg {Boolean} isOriginTop = top align?
34741      */   
34742     isOriginTop : false,
34743     /**
34744      * @cfg {Boolean} isLayoutInstant = no animation?
34745      */   
34746     isLayoutInstant : false, // needed?
34747     /**
34748      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34749      */   
34750     isResizingContainer : true,
34751     /**
34752      * @cfg {Number} columnWidth  width of the columns 
34753      */   
34754     
34755     columnWidth : 0,
34756     
34757     /**
34758      * @cfg {Number} maxCols maximum number of columns
34759      */   
34760     
34761     maxCols: 0,
34762     /**
34763      * @cfg {Number} padHeight padding below box..
34764      */   
34765     
34766     padHeight : 10, 
34767     
34768     /**
34769      * @cfg {Boolean} isAutoInitial defalut true
34770      */   
34771     
34772     isAutoInitial : true, 
34773     
34774     // private?
34775     gutter : 0,
34776     
34777     containerWidth: 0,
34778     initialColumnWidth : 0,
34779     currentSize : null,
34780     
34781     colYs : null, // array.
34782     maxY : 0,
34783     padWidth: 10,
34784     
34785     
34786     tag: 'div',
34787     cls: '',
34788     bricks: null, //CompositeElement
34789     cols : 0, // array?
34790     // element : null, // wrapped now this.el
34791     _isLayoutInited : null, 
34792     
34793     
34794     getAutoCreate : function(){
34795         
34796         var cfg = {
34797             tag: this.tag,
34798             cls: 'blog-masonary-wrapper ' + this.cls,
34799             cn : {
34800                 cls : 'mas-boxes masonary'
34801             }
34802         };
34803         
34804         return cfg;
34805     },
34806     
34807     getChildContainer: function( )
34808     {
34809         if (this.boxesEl) {
34810             return this.boxesEl;
34811         }
34812         
34813         this.boxesEl = this.el.select('.mas-boxes').first();
34814         
34815         return this.boxesEl;
34816     },
34817     
34818     
34819     initEvents : function()
34820     {
34821         var _this = this;
34822         
34823         if(this.isAutoInitial){
34824             Roo.log('hook children rendered');
34825             this.on('childrenrendered', function() {
34826                 Roo.log('children rendered');
34827                 _this.initial();
34828             } ,this);
34829         }
34830         
34831     },
34832     
34833     initial : function()
34834     {
34835         this.reloadItems();
34836
34837         this.currentSize = this.el.getBox(true);
34838
34839         /// was window resize... - let's see if this works..
34840         Roo.EventManager.onWindowResize(this.resize, this); 
34841
34842         if(!this.isAutoInitial){
34843             this.layout();
34844             return;
34845         }
34846         
34847         this.layout.defer(500,this);
34848     },
34849     
34850     reloadItems: function()
34851     {
34852         this.bricks = this.el.select('.masonry-brick', true);
34853         
34854         this.bricks.each(function(b) {
34855             //Roo.log(b.getSize());
34856             if (!b.attr('originalwidth')) {
34857                 b.attr('originalwidth',  b.getSize().width);
34858             }
34859             
34860         });
34861         
34862         Roo.log(this.bricks.elements.length);
34863     },
34864     
34865     resize : function()
34866     {
34867         Roo.log('resize');
34868         var cs = this.el.getBox(true);
34869         
34870         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34871             Roo.log("no change in with or X");
34872             return;
34873         }
34874         this.currentSize = cs;
34875         this.layout();
34876     },
34877     
34878     layout : function()
34879     {
34880          Roo.log('layout');
34881         this._resetLayout();
34882         //this._manageStamps();
34883       
34884         // don't animate first layout
34885         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34886         this.layoutItems( isInstant );
34887       
34888         // flag for initalized
34889         this._isLayoutInited = true;
34890     },
34891     
34892     layoutItems : function( isInstant )
34893     {
34894         //var items = this._getItemsForLayout( this.items );
34895         // original code supports filtering layout items.. we just ignore it..
34896         
34897         this._layoutItems( this.bricks , isInstant );
34898       
34899         this._postLayout();
34900     },
34901     _layoutItems : function ( items , isInstant)
34902     {
34903        //this.fireEvent( 'layout', this, items );
34904     
34905
34906         if ( !items || !items.elements.length ) {
34907           // no items, emit event with empty array
34908             return;
34909         }
34910
34911         var queue = [];
34912         items.each(function(item) {
34913             Roo.log("layout item");
34914             Roo.log(item);
34915             // get x/y object from method
34916             var position = this._getItemLayoutPosition( item );
34917             // enqueue
34918             position.item = item;
34919             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34920             queue.push( position );
34921         }, this);
34922       
34923         this._processLayoutQueue( queue );
34924     },
34925     /** Sets position of item in DOM
34926     * @param {Element} item
34927     * @param {Number} x - horizontal position
34928     * @param {Number} y - vertical position
34929     * @param {Boolean} isInstant - disables transitions
34930     */
34931     _processLayoutQueue : function( queue )
34932     {
34933         for ( var i=0, len = queue.length; i < len; i++ ) {
34934             var obj = queue[i];
34935             obj.item.position('absolute');
34936             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34937         }
34938     },
34939       
34940     
34941     /**
34942     * Any logic you want to do after each layout,
34943     * i.e. size the container
34944     */
34945     _postLayout : function()
34946     {
34947         this.resizeContainer();
34948     },
34949     
34950     resizeContainer : function()
34951     {
34952         if ( !this.isResizingContainer ) {
34953             return;
34954         }
34955         var size = this._getContainerSize();
34956         if ( size ) {
34957             this.el.setSize(size.width,size.height);
34958             this.boxesEl.setSize(size.width,size.height);
34959         }
34960     },
34961     
34962     
34963     
34964     _resetLayout : function()
34965     {
34966         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34967         this.colWidth = this.el.getWidth();
34968         //this.gutter = this.el.getWidth(); 
34969         
34970         this.measureColumns();
34971
34972         // reset column Y
34973         var i = this.cols;
34974         this.colYs = [];
34975         while (i--) {
34976             this.colYs.push( 0 );
34977         }
34978     
34979         this.maxY = 0;
34980     },
34981
34982     measureColumns : function()
34983     {
34984         this.getContainerWidth();
34985       // if columnWidth is 0, default to outerWidth of first item
34986         if ( !this.columnWidth ) {
34987             var firstItem = this.bricks.first();
34988             Roo.log(firstItem);
34989             this.columnWidth  = this.containerWidth;
34990             if (firstItem && firstItem.attr('originalwidth') ) {
34991                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34992             }
34993             // columnWidth fall back to item of first element
34994             Roo.log("set column width?");
34995                         this.initialColumnWidth = this.columnWidth  ;
34996
34997             // if first elem has no width, default to size of container
34998             
34999         }
35000         
35001         
35002         if (this.initialColumnWidth) {
35003             this.columnWidth = this.initialColumnWidth;
35004         }
35005         
35006         
35007             
35008         // column width is fixed at the top - however if container width get's smaller we should
35009         // reduce it...
35010         
35011         // this bit calcs how man columns..
35012             
35013         var columnWidth = this.columnWidth += this.gutter;
35014       
35015         // calculate columns
35016         var containerWidth = this.containerWidth + this.gutter;
35017         
35018         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35019         // fix rounding errors, typically with gutters
35020         var excess = columnWidth - containerWidth % columnWidth;
35021         
35022         
35023         // if overshoot is less than a pixel, round up, otherwise floor it
35024         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35025         cols = Math[ mathMethod ]( cols );
35026         this.cols = Math.max( cols, 1 );
35027         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35028         
35029          // padding positioning..
35030         var totalColWidth = this.cols * this.columnWidth;
35031         var padavail = this.containerWidth - totalColWidth;
35032         // so for 2 columns - we need 3 'pads'
35033         
35034         var padNeeded = (1+this.cols) * this.padWidth;
35035         
35036         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35037         
35038         this.columnWidth += padExtra
35039         //this.padWidth = Math.floor(padavail /  ( this.cols));
35040         
35041         // adjust colum width so that padding is fixed??
35042         
35043         // we have 3 columns ... total = width * 3
35044         // we have X left over... that should be used by 
35045         
35046         //if (this.expandC) {
35047             
35048         //}
35049         
35050         
35051         
35052     },
35053     
35054     getContainerWidth : function()
35055     {
35056        /* // container is parent if fit width
35057         var container = this.isFitWidth ? this.element.parentNode : this.element;
35058         // check that this.size and size are there
35059         // IE8 triggers resize on body size change, so they might not be
35060         
35061         var size = getSize( container );  //FIXME
35062         this.containerWidth = size && size.innerWidth; //FIXME
35063         */
35064          
35065         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35066         
35067     },
35068     
35069     _getItemLayoutPosition : function( item )  // what is item?
35070     {
35071         // we resize the item to our columnWidth..
35072       
35073         item.setWidth(this.columnWidth);
35074         item.autoBoxAdjust  = false;
35075         
35076         var sz = item.getSize();
35077  
35078         // how many columns does this brick span
35079         var remainder = this.containerWidth % this.columnWidth;
35080         
35081         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35082         // round if off by 1 pixel, otherwise use ceil
35083         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35084         colSpan = Math.min( colSpan, this.cols );
35085         
35086         // normally this should be '1' as we dont' currently allow multi width columns..
35087         
35088         var colGroup = this._getColGroup( colSpan );
35089         // get the minimum Y value from the columns
35090         var minimumY = Math.min.apply( Math, colGroup );
35091         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35092         
35093         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35094          
35095         // position the brick
35096         var position = {
35097             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35098             y: this.currentSize.y + minimumY + this.padHeight
35099         };
35100         
35101         Roo.log(position);
35102         // apply setHeight to necessary columns
35103         var setHeight = minimumY + sz.height + this.padHeight;
35104         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35105         
35106         var setSpan = this.cols + 1 - colGroup.length;
35107         for ( var i = 0; i < setSpan; i++ ) {
35108           this.colYs[ shortColIndex + i ] = setHeight ;
35109         }
35110       
35111         return position;
35112     },
35113     
35114     /**
35115      * @param {Number} colSpan - number of columns the element spans
35116      * @returns {Array} colGroup
35117      */
35118     _getColGroup : function( colSpan )
35119     {
35120         if ( colSpan < 2 ) {
35121           // if brick spans only one column, use all the column Ys
35122           return this.colYs;
35123         }
35124       
35125         var colGroup = [];
35126         // how many different places could this brick fit horizontally
35127         var groupCount = this.cols + 1 - colSpan;
35128         // for each group potential horizontal position
35129         for ( var i = 0; i < groupCount; i++ ) {
35130           // make an array of colY values for that one group
35131           var groupColYs = this.colYs.slice( i, i + colSpan );
35132           // and get the max value of the array
35133           colGroup[i] = Math.max.apply( Math, groupColYs );
35134         }
35135         return colGroup;
35136     },
35137     /*
35138     _manageStamp : function( stamp )
35139     {
35140         var stampSize =  stamp.getSize();
35141         var offset = stamp.getBox();
35142         // get the columns that this stamp affects
35143         var firstX = this.isOriginLeft ? offset.x : offset.right;
35144         var lastX = firstX + stampSize.width;
35145         var firstCol = Math.floor( firstX / this.columnWidth );
35146         firstCol = Math.max( 0, firstCol );
35147         
35148         var lastCol = Math.floor( lastX / this.columnWidth );
35149         // lastCol should not go over if multiple of columnWidth #425
35150         lastCol -= lastX % this.columnWidth ? 0 : 1;
35151         lastCol = Math.min( this.cols - 1, lastCol );
35152         
35153         // set colYs to bottom of the stamp
35154         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35155             stampSize.height;
35156             
35157         for ( var i = firstCol; i <= lastCol; i++ ) {
35158           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35159         }
35160     },
35161     */
35162     
35163     _getContainerSize : function()
35164     {
35165         this.maxY = Math.max.apply( Math, this.colYs );
35166         var size = {
35167             height: this.maxY
35168         };
35169       
35170         if ( this.isFitWidth ) {
35171             size.width = this._getContainerFitWidth();
35172         }
35173       
35174         return size;
35175     },
35176     
35177     _getContainerFitWidth : function()
35178     {
35179         var unusedCols = 0;
35180         // count unused columns
35181         var i = this.cols;
35182         while ( --i ) {
35183           if ( this.colYs[i] !== 0 ) {
35184             break;
35185           }
35186           unusedCols++;
35187         }
35188         // fit container to columns that have been used
35189         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35190     },
35191     
35192     needsResizeLayout : function()
35193     {
35194         var previousWidth = this.containerWidth;
35195         this.getContainerWidth();
35196         return previousWidth !== this.containerWidth;
35197     }
35198  
35199 });
35200
35201  
35202
35203  /*
35204  * - LGPL
35205  *
35206  * element
35207  * 
35208  */
35209
35210 /**
35211  * @class Roo.bootstrap.MasonryBrick
35212  * @extends Roo.bootstrap.Component
35213  * Bootstrap MasonryBrick class
35214  * 
35215  * @constructor
35216  * Create a new MasonryBrick
35217  * @param {Object} config The config object
35218  */
35219
35220 Roo.bootstrap.MasonryBrick = function(config){
35221     
35222     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35223     
35224     Roo.bootstrap.MasonryBrick.register(this);
35225     
35226     this.addEvents({
35227         // raw events
35228         /**
35229          * @event click
35230          * When a MasonryBrick is clcik
35231          * @param {Roo.bootstrap.MasonryBrick} this
35232          * @param {Roo.EventObject} e
35233          */
35234         "click" : true
35235     });
35236 };
35237
35238 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35239     
35240     /**
35241      * @cfg {String} title
35242      */   
35243     title : '',
35244     /**
35245      * @cfg {String} html
35246      */   
35247     html : '',
35248     /**
35249      * @cfg {String} bgimage
35250      */   
35251     bgimage : '',
35252     /**
35253      * @cfg {String} videourl
35254      */   
35255     videourl : '',
35256     /**
35257      * @cfg {String} cls
35258      */   
35259     cls : '',
35260     /**
35261      * @cfg {String} href
35262      */   
35263     href : '',
35264     /**
35265      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35266      */   
35267     size : 'xs',
35268     
35269     /**
35270      * @cfg {String} placetitle (center|bottom)
35271      */   
35272     placetitle : '',
35273     
35274     /**
35275      * @cfg {Boolean} isFitContainer defalut true
35276      */   
35277     isFitContainer : true, 
35278     
35279     /**
35280      * @cfg {Boolean} preventDefault defalut false
35281      */   
35282     preventDefault : false, 
35283     
35284     /**
35285      * @cfg {Boolean} inverse defalut false
35286      */   
35287     maskInverse : false, 
35288     
35289     getAutoCreate : function()
35290     {
35291         if(!this.isFitContainer){
35292             return this.getSplitAutoCreate();
35293         }
35294         
35295         var cls = 'masonry-brick masonry-brick-full';
35296         
35297         if(this.href.length){
35298             cls += ' masonry-brick-link';
35299         }
35300         
35301         if(this.bgimage.length){
35302             cls += ' masonry-brick-image';
35303         }
35304         
35305         if(this.maskInverse){
35306             cls += ' mask-inverse';
35307         }
35308         
35309         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35310             cls += ' enable-mask';
35311         }
35312         
35313         if(this.size){
35314             cls += ' masonry-' + this.size + '-brick';
35315         }
35316         
35317         if(this.placetitle.length){
35318             
35319             switch (this.placetitle) {
35320                 case 'center' :
35321                     cls += ' masonry-center-title';
35322                     break;
35323                 case 'bottom' :
35324                     cls += ' masonry-bottom-title';
35325                     break;
35326                 default:
35327                     break;
35328             }
35329             
35330         } else {
35331             if(!this.html.length && !this.bgimage.length){
35332                 cls += ' masonry-center-title';
35333             }
35334
35335             if(!this.html.length && this.bgimage.length){
35336                 cls += ' masonry-bottom-title';
35337             }
35338         }
35339         
35340         if(this.cls){
35341             cls += ' ' + this.cls;
35342         }
35343         
35344         var cfg = {
35345             tag: (this.href.length) ? 'a' : 'div',
35346             cls: cls,
35347             cn: [
35348                 {
35349                     tag: 'div',
35350                     cls: 'masonry-brick-mask'
35351                 },
35352                 {
35353                     tag: 'div',
35354                     cls: 'masonry-brick-paragraph',
35355                     cn: []
35356                 }
35357             ]
35358         };
35359         
35360         if(this.href.length){
35361             cfg.href = this.href;
35362         }
35363         
35364         var cn = cfg.cn[1].cn;
35365         
35366         if(this.title.length){
35367             cn.push({
35368                 tag: 'h4',
35369                 cls: 'masonry-brick-title',
35370                 html: this.title
35371             });
35372         }
35373         
35374         if(this.html.length){
35375             cn.push({
35376                 tag: 'p',
35377                 cls: 'masonry-brick-text',
35378                 html: this.html
35379             });
35380         }
35381         
35382         if (!this.title.length && !this.html.length) {
35383             cfg.cn[1].cls += ' hide';
35384         }
35385         
35386         if(this.bgimage.length){
35387             cfg.cn.push({
35388                 tag: 'img',
35389                 cls: 'masonry-brick-image-view',
35390                 src: this.bgimage
35391             });
35392         }
35393         
35394         if(this.videourl.length){
35395             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35396             // youtube support only?
35397             cfg.cn.push({
35398                 tag: 'iframe',
35399                 cls: 'masonry-brick-image-view',
35400                 src: vurl,
35401                 frameborder : 0,
35402                 allowfullscreen : true
35403             });
35404         }
35405         
35406         return cfg;
35407         
35408     },
35409     
35410     getSplitAutoCreate : function()
35411     {
35412         var cls = 'masonry-brick masonry-brick-split';
35413         
35414         if(this.href.length){
35415             cls += ' masonry-brick-link';
35416         }
35417         
35418         if(this.bgimage.length){
35419             cls += ' masonry-brick-image';
35420         }
35421         
35422         if(this.size){
35423             cls += ' masonry-' + this.size + '-brick';
35424         }
35425         
35426         switch (this.placetitle) {
35427             case 'center' :
35428                 cls += ' masonry-center-title';
35429                 break;
35430             case 'bottom' :
35431                 cls += ' masonry-bottom-title';
35432                 break;
35433             default:
35434                 if(!this.bgimage.length){
35435                     cls += ' masonry-center-title';
35436                 }
35437
35438                 if(this.bgimage.length){
35439                     cls += ' masonry-bottom-title';
35440                 }
35441                 break;
35442         }
35443         
35444         if(this.cls){
35445             cls += ' ' + this.cls;
35446         }
35447         
35448         var cfg = {
35449             tag: (this.href.length) ? 'a' : 'div',
35450             cls: cls,
35451             cn: [
35452                 {
35453                     tag: 'div',
35454                     cls: 'masonry-brick-split-head',
35455                     cn: [
35456                         {
35457                             tag: 'div',
35458                             cls: 'masonry-brick-paragraph',
35459                             cn: []
35460                         }
35461                     ]
35462                 },
35463                 {
35464                     tag: 'div',
35465                     cls: 'masonry-brick-split-body',
35466                     cn: []
35467                 }
35468             ]
35469         };
35470         
35471         if(this.href.length){
35472             cfg.href = this.href;
35473         }
35474         
35475         if(this.title.length){
35476             cfg.cn[0].cn[0].cn.push({
35477                 tag: 'h4',
35478                 cls: 'masonry-brick-title',
35479                 html: this.title
35480             });
35481         }
35482         
35483         if(this.html.length){
35484             cfg.cn[1].cn.push({
35485                 tag: 'p',
35486                 cls: 'masonry-brick-text',
35487                 html: this.html
35488             });
35489         }
35490
35491         if(this.bgimage.length){
35492             cfg.cn[0].cn.push({
35493                 tag: 'img',
35494                 cls: 'masonry-brick-image-view',
35495                 src: this.bgimage
35496             });
35497         }
35498         
35499         if(this.videourl.length){
35500             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35501             // youtube support only?
35502             cfg.cn[0].cn.cn.push({
35503                 tag: 'iframe',
35504                 cls: 'masonry-brick-image-view',
35505                 src: vurl,
35506                 frameborder : 0,
35507                 allowfullscreen : true
35508             });
35509         }
35510         
35511         return cfg;
35512     },
35513     
35514     initEvents: function() 
35515     {
35516         switch (this.size) {
35517             case 'xs' :
35518                 this.x = 1;
35519                 this.y = 1;
35520                 break;
35521             case 'sm' :
35522                 this.x = 2;
35523                 this.y = 2;
35524                 break;
35525             case 'md' :
35526             case 'md-left' :
35527             case 'md-right' :
35528                 this.x = 3;
35529                 this.y = 3;
35530                 break;
35531             case 'tall' :
35532                 this.x = 2;
35533                 this.y = 3;
35534                 break;
35535             case 'wide' :
35536                 this.x = 3;
35537                 this.y = 2;
35538                 break;
35539             case 'wide-thin' :
35540                 this.x = 3;
35541                 this.y = 1;
35542                 break;
35543                         
35544             default :
35545                 break;
35546         }
35547         
35548         if(Roo.isTouch){
35549             this.el.on('touchstart', this.onTouchStart, this);
35550             this.el.on('touchmove', this.onTouchMove, this);
35551             this.el.on('touchend', this.onTouchEnd, this);
35552             this.el.on('contextmenu', this.onContextMenu, this);
35553         } else {
35554             this.el.on('mouseenter'  ,this.enter, this);
35555             this.el.on('mouseleave', this.leave, this);
35556             this.el.on('click', this.onClick, this);
35557         }
35558         
35559         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35560             this.parent().bricks.push(this);   
35561         }
35562         
35563     },
35564     
35565     onClick: function(e, el)
35566     {
35567         var time = this.endTimer - this.startTimer;
35568         // Roo.log(e.preventDefault());
35569         if(Roo.isTouch){
35570             if(time > 1000){
35571                 e.preventDefault();
35572                 return;
35573             }
35574         }
35575         
35576         if(!this.preventDefault){
35577             return;
35578         }
35579         
35580         e.preventDefault();
35581         
35582         if (this.activeClass != '') {
35583             this.selectBrick();
35584         }
35585         
35586         this.fireEvent('click', this, e);
35587     },
35588     
35589     enter: function(e, el)
35590     {
35591         e.preventDefault();
35592         
35593         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35594             return;
35595         }
35596         
35597         if(this.bgimage.length && this.html.length){
35598             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35599         }
35600     },
35601     
35602     leave: function(e, el)
35603     {
35604         e.preventDefault();
35605         
35606         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35607             return;
35608         }
35609         
35610         if(this.bgimage.length && this.html.length){
35611             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35612         }
35613     },
35614     
35615     onTouchStart: function(e, el)
35616     {
35617 //        e.preventDefault();
35618         
35619         this.touchmoved = false;
35620         
35621         if(!this.isFitContainer){
35622             return;
35623         }
35624         
35625         if(!this.bgimage.length || !this.html.length){
35626             return;
35627         }
35628         
35629         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35630         
35631         this.timer = new Date().getTime();
35632         
35633     },
35634     
35635     onTouchMove: function(e, el)
35636     {
35637         this.touchmoved = true;
35638     },
35639     
35640     onContextMenu : function(e,el)
35641     {
35642         e.preventDefault();
35643         e.stopPropagation();
35644         return false;
35645     },
35646     
35647     onTouchEnd: function(e, el)
35648     {
35649 //        e.preventDefault();
35650         
35651         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35652         
35653             this.leave(e,el);
35654             
35655             return;
35656         }
35657         
35658         if(!this.bgimage.length || !this.html.length){
35659             
35660             if(this.href.length){
35661                 window.location.href = this.href;
35662             }
35663             
35664             return;
35665         }
35666         
35667         if(!this.isFitContainer){
35668             return;
35669         }
35670         
35671         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35672         
35673         window.location.href = this.href;
35674     },
35675     
35676     //selection on single brick only
35677     selectBrick : function() {
35678         
35679         if (!this.parentId) {
35680             return;
35681         }
35682         
35683         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35684         var index = m.selectedBrick.indexOf(this.id);
35685         
35686         if ( index > -1) {
35687             m.selectedBrick.splice(index,1);
35688             this.el.removeClass(this.activeClass);
35689             return;
35690         }
35691         
35692         for(var i = 0; i < m.selectedBrick.length; i++) {
35693             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35694             b.el.removeClass(b.activeClass);
35695         }
35696         
35697         m.selectedBrick = [];
35698         
35699         m.selectedBrick.push(this.id);
35700         this.el.addClass(this.activeClass);
35701         return;
35702     },
35703     
35704     isSelected : function(){
35705         return this.el.hasClass(this.activeClass);
35706         
35707     }
35708 });
35709
35710 Roo.apply(Roo.bootstrap.MasonryBrick, {
35711     
35712     //groups: {},
35713     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35714      /**
35715     * register a Masonry Brick
35716     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35717     */
35718     
35719     register : function(brick)
35720     {
35721         //this.groups[brick.id] = brick;
35722         this.groups.add(brick.id, brick);
35723     },
35724     /**
35725     * fetch a  masonry brick based on the masonry brick ID
35726     * @param {string} the masonry brick to add
35727     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35728     */
35729     
35730     get: function(brick_id) 
35731     {
35732         // if (typeof(this.groups[brick_id]) == 'undefined') {
35733         //     return false;
35734         // }
35735         // return this.groups[brick_id] ;
35736         
35737         if(this.groups.key(brick_id)) {
35738             return this.groups.key(brick_id);
35739         }
35740         
35741         return false;
35742     }
35743     
35744     
35745     
35746 });
35747
35748  /*
35749  * - LGPL
35750  *
35751  * element
35752  * 
35753  */
35754
35755 /**
35756  * @class Roo.bootstrap.Brick
35757  * @extends Roo.bootstrap.Component
35758  * Bootstrap Brick class
35759  * 
35760  * @constructor
35761  * Create a new Brick
35762  * @param {Object} config The config object
35763  */
35764
35765 Roo.bootstrap.Brick = function(config){
35766     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35767     
35768     this.addEvents({
35769         // raw events
35770         /**
35771          * @event click
35772          * When a Brick is click
35773          * @param {Roo.bootstrap.Brick} this
35774          * @param {Roo.EventObject} e
35775          */
35776         "click" : true
35777     });
35778 };
35779
35780 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35781     
35782     /**
35783      * @cfg {String} title
35784      */   
35785     title : '',
35786     /**
35787      * @cfg {String} html
35788      */   
35789     html : '',
35790     /**
35791      * @cfg {String} bgimage
35792      */   
35793     bgimage : '',
35794     /**
35795      * @cfg {String} cls
35796      */   
35797     cls : '',
35798     /**
35799      * @cfg {String} href
35800      */   
35801     href : '',
35802     /**
35803      * @cfg {String} video
35804      */   
35805     video : '',
35806     /**
35807      * @cfg {Boolean} square
35808      */   
35809     square : true,
35810     
35811     getAutoCreate : function()
35812     {
35813         var cls = 'roo-brick';
35814         
35815         if(this.href.length){
35816             cls += ' roo-brick-link';
35817         }
35818         
35819         if(this.bgimage.length){
35820             cls += ' roo-brick-image';
35821         }
35822         
35823         if(!this.html.length && !this.bgimage.length){
35824             cls += ' roo-brick-center-title';
35825         }
35826         
35827         if(!this.html.length && this.bgimage.length){
35828             cls += ' roo-brick-bottom-title';
35829         }
35830         
35831         if(this.cls){
35832             cls += ' ' + this.cls;
35833         }
35834         
35835         var cfg = {
35836             tag: (this.href.length) ? 'a' : 'div',
35837             cls: cls,
35838             cn: [
35839                 {
35840                     tag: 'div',
35841                     cls: 'roo-brick-paragraph',
35842                     cn: []
35843                 }
35844             ]
35845         };
35846         
35847         if(this.href.length){
35848             cfg.href = this.href;
35849         }
35850         
35851         var cn = cfg.cn[0].cn;
35852         
35853         if(this.title.length){
35854             cn.push({
35855                 tag: 'h4',
35856                 cls: 'roo-brick-title',
35857                 html: this.title
35858             });
35859         }
35860         
35861         if(this.html.length){
35862             cn.push({
35863                 tag: 'p',
35864                 cls: 'roo-brick-text',
35865                 html: this.html
35866             });
35867         } else {
35868             cn.cls += ' hide';
35869         }
35870         
35871         if(this.bgimage.length){
35872             cfg.cn.push({
35873                 tag: 'img',
35874                 cls: 'roo-brick-image-view',
35875                 src: this.bgimage
35876             });
35877         }
35878         
35879         return cfg;
35880     },
35881     
35882     initEvents: function() 
35883     {
35884         if(this.title.length || this.html.length){
35885             this.el.on('mouseenter'  ,this.enter, this);
35886             this.el.on('mouseleave', this.leave, this);
35887         }
35888         
35889         Roo.EventManager.onWindowResize(this.resize, this); 
35890         
35891         if(this.bgimage.length){
35892             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35893             this.imageEl.on('load', this.onImageLoad, this);
35894             return;
35895         }
35896         
35897         this.resize();
35898     },
35899     
35900     onImageLoad : function()
35901     {
35902         this.resize();
35903     },
35904     
35905     resize : function()
35906     {
35907         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35908         
35909         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35910         
35911         if(this.bgimage.length){
35912             var image = this.el.select('.roo-brick-image-view', true).first();
35913             
35914             image.setWidth(paragraph.getWidth());
35915             
35916             if(this.square){
35917                 image.setHeight(paragraph.getWidth());
35918             }
35919             
35920             this.el.setHeight(image.getHeight());
35921             paragraph.setHeight(image.getHeight());
35922             
35923         }
35924         
35925     },
35926     
35927     enter: function(e, el)
35928     {
35929         e.preventDefault();
35930         
35931         if(this.bgimage.length){
35932             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35933             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35934         }
35935     },
35936     
35937     leave: function(e, el)
35938     {
35939         e.preventDefault();
35940         
35941         if(this.bgimage.length){
35942             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35943             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35944         }
35945     }
35946     
35947 });
35948
35949  
35950
35951  /*
35952  * - LGPL
35953  *
35954  * Number field 
35955  */
35956
35957 /**
35958  * @class Roo.bootstrap.NumberField
35959  * @extends Roo.bootstrap.Input
35960  * Bootstrap NumberField class
35961  * 
35962  * 
35963  * 
35964  * 
35965  * @constructor
35966  * Create a new NumberField
35967  * @param {Object} config The config object
35968  */
35969
35970 Roo.bootstrap.NumberField = function(config){
35971     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35972 };
35973
35974 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35975     
35976     /**
35977      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35978      */
35979     allowDecimals : true,
35980     /**
35981      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35982      */
35983     decimalSeparator : ".",
35984     /**
35985      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35986      */
35987     decimalPrecision : 2,
35988     /**
35989      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35990      */
35991     allowNegative : true,
35992     
35993     /**
35994      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35995      */
35996     allowZero: true,
35997     /**
35998      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35999      */
36000     minValue : Number.NEGATIVE_INFINITY,
36001     /**
36002      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36003      */
36004     maxValue : Number.MAX_VALUE,
36005     /**
36006      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36007      */
36008     minText : "The minimum value for this field is {0}",
36009     /**
36010      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36011      */
36012     maxText : "The maximum value for this field is {0}",
36013     /**
36014      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36015      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36016      */
36017     nanText : "{0} is not a valid number",
36018     /**
36019      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36020      */
36021     thousandsDelimiter : false,
36022     /**
36023      * @cfg {String} valueAlign alignment of value
36024      */
36025     valueAlign : "left",
36026
36027     getAutoCreate : function()
36028     {
36029         var hiddenInput = {
36030             tag: 'input',
36031             type: 'hidden',
36032             id: Roo.id(),
36033             cls: 'hidden-number-input'
36034         };
36035         
36036         if (this.name) {
36037             hiddenInput.name = this.name;
36038         }
36039         
36040         this.name = '';
36041         
36042         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36043         
36044         this.name = hiddenInput.name;
36045         
36046         if(cfg.cn.length > 0) {
36047             cfg.cn.push(hiddenInput);
36048         }
36049         
36050         return cfg;
36051     },
36052
36053     // private
36054     initEvents : function()
36055     {   
36056         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36057         
36058         var allowed = "0123456789";
36059         
36060         if(this.allowDecimals){
36061             allowed += this.decimalSeparator;
36062         }
36063         
36064         if(this.allowNegative){
36065             allowed += "-";
36066         }
36067         
36068         if(this.thousandsDelimiter) {
36069             allowed += ",";
36070         }
36071         
36072         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36073         
36074         var keyPress = function(e){
36075             
36076             var k = e.getKey();
36077             
36078             var c = e.getCharCode();
36079             
36080             if(
36081                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36082                     allowed.indexOf(String.fromCharCode(c)) === -1
36083             ){
36084                 e.stopEvent();
36085                 return;
36086             }
36087             
36088             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36089                 return;
36090             }
36091             
36092             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36093                 e.stopEvent();
36094             }
36095         };
36096         
36097         this.el.on("keypress", keyPress, this);
36098     },
36099     
36100     validateValue : function(value)
36101     {
36102         
36103         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36104             return false;
36105         }
36106         
36107         var num = this.parseValue(value);
36108         
36109         if(isNaN(num)){
36110             this.markInvalid(String.format(this.nanText, value));
36111             return false;
36112         }
36113         
36114         if(num < this.minValue){
36115             this.markInvalid(String.format(this.minText, this.minValue));
36116             return false;
36117         }
36118         
36119         if(num > this.maxValue){
36120             this.markInvalid(String.format(this.maxText, this.maxValue));
36121             return false;
36122         }
36123         
36124         return true;
36125     },
36126
36127     getValue : function()
36128     {
36129         var v = this.hiddenEl().getValue();
36130         
36131         return this.fixPrecision(this.parseValue(v));
36132     },
36133
36134     parseValue : function(value)
36135     {
36136         if(this.thousandsDelimiter) {
36137             value += "";
36138             r = new RegExp(",", "g");
36139             value = value.replace(r, "");
36140         }
36141         
36142         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36143         return isNaN(value) ? '' : value;
36144     },
36145
36146     fixPrecision : function(value)
36147     {
36148         if(this.thousandsDelimiter) {
36149             value += "";
36150             r = new RegExp(",", "g");
36151             value = value.replace(r, "");
36152         }
36153         
36154         var nan = isNaN(value);
36155         
36156         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36157             return nan ? '' : value;
36158         }
36159         return parseFloat(value).toFixed(this.decimalPrecision);
36160     },
36161
36162     setValue : function(v)
36163     {
36164         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36165         
36166         this.value = v;
36167         
36168         if(this.rendered){
36169             
36170             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36171             
36172             this.inputEl().dom.value = (v == '') ? '' :
36173                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36174             
36175             if(!this.allowZero && v === '0') {
36176                 this.hiddenEl().dom.value = '';
36177                 this.inputEl().dom.value = '';
36178             }
36179             
36180             this.validate();
36181         }
36182     },
36183
36184     decimalPrecisionFcn : function(v)
36185     {
36186         return Math.floor(v);
36187     },
36188
36189     beforeBlur : function()
36190     {
36191         var v = this.parseValue(this.getRawValue());
36192         
36193         if(v || v === 0 || v === ''){
36194             this.setValue(v);
36195         }
36196     },
36197     
36198     hiddenEl : function()
36199     {
36200         return this.el.select('input.hidden-number-input',true).first();
36201     }
36202     
36203 });
36204
36205  
36206
36207 /*
36208 * Licence: LGPL
36209 */
36210
36211 /**
36212  * @class Roo.bootstrap.DocumentSlider
36213  * @extends Roo.bootstrap.Component
36214  * Bootstrap DocumentSlider class
36215  * 
36216  * @constructor
36217  * Create a new DocumentViewer
36218  * @param {Object} config The config object
36219  */
36220
36221 Roo.bootstrap.DocumentSlider = function(config){
36222     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36223     
36224     this.files = [];
36225     
36226     this.addEvents({
36227         /**
36228          * @event initial
36229          * Fire after initEvent
36230          * @param {Roo.bootstrap.DocumentSlider} this
36231          */
36232         "initial" : true,
36233         /**
36234          * @event update
36235          * Fire after update
36236          * @param {Roo.bootstrap.DocumentSlider} this
36237          */
36238         "update" : true,
36239         /**
36240          * @event click
36241          * Fire after click
36242          * @param {Roo.bootstrap.DocumentSlider} this
36243          */
36244         "click" : true
36245     });
36246 };
36247
36248 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36249     
36250     files : false,
36251     
36252     indicator : 0,
36253     
36254     getAutoCreate : function()
36255     {
36256         var cfg = {
36257             tag : 'div',
36258             cls : 'roo-document-slider',
36259             cn : [
36260                 {
36261                     tag : 'div',
36262                     cls : 'roo-document-slider-header',
36263                     cn : [
36264                         {
36265                             tag : 'div',
36266                             cls : 'roo-document-slider-header-title'
36267                         }
36268                     ]
36269                 },
36270                 {
36271                     tag : 'div',
36272                     cls : 'roo-document-slider-body',
36273                     cn : [
36274                         {
36275                             tag : 'div',
36276                             cls : 'roo-document-slider-prev',
36277                             cn : [
36278                                 {
36279                                     tag : 'i',
36280                                     cls : 'fa fa-chevron-left'
36281                                 }
36282                             ]
36283                         },
36284                         {
36285                             tag : 'div',
36286                             cls : 'roo-document-slider-thumb',
36287                             cn : [
36288                                 {
36289                                     tag : 'img',
36290                                     cls : 'roo-document-slider-image'
36291                                 }
36292                             ]
36293                         },
36294                         {
36295                             tag : 'div',
36296                             cls : 'roo-document-slider-next',
36297                             cn : [
36298                                 {
36299                                     tag : 'i',
36300                                     cls : 'fa fa-chevron-right'
36301                                 }
36302                             ]
36303                         }
36304                     ]
36305                 }
36306             ]
36307         };
36308         
36309         return cfg;
36310     },
36311     
36312     initEvents : function()
36313     {
36314         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36315         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36316         
36317         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36318         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36319         
36320         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36321         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36322         
36323         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36324         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36325         
36326         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36327         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36328         
36329         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36330         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36331         
36332         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36333         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36334         
36335         this.thumbEl.on('click', this.onClick, this);
36336         
36337         this.prevIndicator.on('click', this.prev, this);
36338         
36339         this.nextIndicator.on('click', this.next, this);
36340         
36341     },
36342     
36343     initial : function()
36344     {
36345         if(this.files.length){
36346             this.indicator = 1;
36347             this.update()
36348         }
36349         
36350         this.fireEvent('initial', this);
36351     },
36352     
36353     update : function()
36354     {
36355         this.imageEl.attr('src', this.files[this.indicator - 1]);
36356         
36357         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36358         
36359         this.prevIndicator.show();
36360         
36361         if(this.indicator == 1){
36362             this.prevIndicator.hide();
36363         }
36364         
36365         this.nextIndicator.show();
36366         
36367         if(this.indicator == this.files.length){
36368             this.nextIndicator.hide();
36369         }
36370         
36371         this.thumbEl.scrollTo('top');
36372         
36373         this.fireEvent('update', this);
36374     },
36375     
36376     onClick : function(e)
36377     {
36378         e.preventDefault();
36379         
36380         this.fireEvent('click', this);
36381     },
36382     
36383     prev : function(e)
36384     {
36385         e.preventDefault();
36386         
36387         this.indicator = Math.max(1, this.indicator - 1);
36388         
36389         this.update();
36390     },
36391     
36392     next : function(e)
36393     {
36394         e.preventDefault();
36395         
36396         this.indicator = Math.min(this.files.length, this.indicator + 1);
36397         
36398         this.update();
36399     }
36400 });
36401 /*
36402  * - LGPL
36403  *
36404  * RadioSet
36405  *
36406  *
36407  */
36408
36409 /**
36410  * @class Roo.bootstrap.RadioSet
36411  * @extends Roo.bootstrap.Input
36412  * Bootstrap RadioSet class
36413  * @cfg {String} indicatorpos (left|right) default left
36414  * @cfg {Boolean} inline (true|false) inline the element (default true)
36415  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36416  * @constructor
36417  * Create a new RadioSet
36418  * @param {Object} config The config object
36419  */
36420
36421 Roo.bootstrap.RadioSet = function(config){
36422     
36423     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36424     
36425     this.radioes = [];
36426     
36427     Roo.bootstrap.RadioSet.register(this);
36428     
36429     this.addEvents({
36430         /**
36431         * @event check
36432         * Fires when the element is checked or unchecked.
36433         * @param {Roo.bootstrap.RadioSet} this This radio
36434         * @param {Roo.bootstrap.Radio} item The checked item
36435         */
36436        check : true,
36437        /**
36438         * @event click
36439         * Fires when the element is click.
36440         * @param {Roo.bootstrap.RadioSet} this This radio set
36441         * @param {Roo.bootstrap.Radio} item The checked item
36442         * @param {Roo.EventObject} e The event object
36443         */
36444        click : true
36445     });
36446     
36447 };
36448
36449 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36450
36451     radioes : false,
36452     
36453     inline : true,
36454     
36455     weight : '',
36456     
36457     indicatorpos : 'left',
36458     
36459     getAutoCreate : function()
36460     {
36461         var label = {
36462             tag : 'label',
36463             cls : 'roo-radio-set-label',
36464             cn : [
36465                 {
36466                     tag : 'span',
36467                     html : this.fieldLabel
36468                 }
36469             ]
36470         };
36471         if (Roo.bootstrap.version == 3) {
36472             
36473             
36474             if(this.indicatorpos == 'left'){
36475                 label.cn.unshift({
36476                     tag : 'i',
36477                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36478                     tooltip : 'This field is required'
36479                 });
36480             } else {
36481                 label.cn.push({
36482                     tag : 'i',
36483                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36484                     tooltip : 'This field is required'
36485                 });
36486             }
36487         }
36488         var items = {
36489             tag : 'div',
36490             cls : 'roo-radio-set-items'
36491         };
36492         
36493         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36494         
36495         if (align === 'left' && this.fieldLabel.length) {
36496             
36497             items = {
36498                 cls : "roo-radio-set-right", 
36499                 cn: [
36500                     items
36501                 ]
36502             };
36503             
36504             if(this.labelWidth > 12){
36505                 label.style = "width: " + this.labelWidth + 'px';
36506             }
36507             
36508             if(this.labelWidth < 13 && this.labelmd == 0){
36509                 this.labelmd = this.labelWidth;
36510             }
36511             
36512             if(this.labellg > 0){
36513                 label.cls += ' col-lg-' + this.labellg;
36514                 items.cls += ' col-lg-' + (12 - this.labellg);
36515             }
36516             
36517             if(this.labelmd > 0){
36518                 label.cls += ' col-md-' + this.labelmd;
36519                 items.cls += ' col-md-' + (12 - this.labelmd);
36520             }
36521             
36522             if(this.labelsm > 0){
36523                 label.cls += ' col-sm-' + this.labelsm;
36524                 items.cls += ' col-sm-' + (12 - this.labelsm);
36525             }
36526             
36527             if(this.labelxs > 0){
36528                 label.cls += ' col-xs-' + this.labelxs;
36529                 items.cls += ' col-xs-' + (12 - this.labelxs);
36530             }
36531         }
36532         
36533         var cfg = {
36534             tag : 'div',
36535             cls : 'roo-radio-set',
36536             cn : [
36537                 {
36538                     tag : 'input',
36539                     cls : 'roo-radio-set-input',
36540                     type : 'hidden',
36541                     name : this.name,
36542                     value : this.value ? this.value :  ''
36543                 },
36544                 label,
36545                 items
36546             ]
36547         };
36548         
36549         if(this.weight.length){
36550             cfg.cls += ' roo-radio-' + this.weight;
36551         }
36552         
36553         if(this.inline) {
36554             cfg.cls += ' roo-radio-set-inline';
36555         }
36556         
36557         var settings=this;
36558         ['xs','sm','md','lg'].map(function(size){
36559             if (settings[size]) {
36560                 cfg.cls += ' col-' + size + '-' + settings[size];
36561             }
36562         });
36563         
36564         return cfg;
36565         
36566     },
36567
36568     initEvents : function()
36569     {
36570         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36571         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36572         
36573         if(!this.fieldLabel.length){
36574             this.labelEl.hide();
36575         }
36576         
36577         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36578         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36579         
36580         this.indicator = this.indicatorEl();
36581         
36582         if(this.indicator){
36583             this.indicator.addClass('invisible');
36584         }
36585         
36586         this.originalValue = this.getValue();
36587         
36588     },
36589     
36590     inputEl: function ()
36591     {
36592         return this.el.select('.roo-radio-set-input', true).first();
36593     },
36594     
36595     getChildContainer : function()
36596     {
36597         return this.itemsEl;
36598     },
36599     
36600     register : function(item)
36601     {
36602         this.radioes.push(item);
36603         
36604     },
36605     
36606     validate : function()
36607     {   
36608         if(this.getVisibilityEl().hasClass('hidden')){
36609             return true;
36610         }
36611         
36612         var valid = false;
36613         
36614         Roo.each(this.radioes, function(i){
36615             if(!i.checked){
36616                 return;
36617             }
36618             
36619             valid = true;
36620             return false;
36621         });
36622         
36623         if(this.allowBlank) {
36624             return true;
36625         }
36626         
36627         if(this.disabled || valid){
36628             this.markValid();
36629             return true;
36630         }
36631         
36632         this.markInvalid();
36633         return false;
36634         
36635     },
36636     
36637     markValid : function()
36638     {
36639         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36640             this.indicatorEl().removeClass('visible');
36641             this.indicatorEl().addClass('invisible');
36642         }
36643         
36644         
36645         if (Roo.bootstrap.version == 3) {
36646             this.el.removeClass([this.invalidClass, this.validClass]);
36647             this.el.addClass(this.validClass);
36648         } else {
36649             this.el.removeClass(['is-invalid','is-valid']);
36650             this.el.addClass(['is-valid']);
36651         }
36652         this.fireEvent('valid', this);
36653     },
36654     
36655     markInvalid : function(msg)
36656     {
36657         if(this.allowBlank || this.disabled){
36658             return;
36659         }
36660         
36661         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36662             this.indicatorEl().removeClass('invisible');
36663             this.indicatorEl().addClass('visible');
36664         }
36665         if (Roo.bootstrap.version == 3) {
36666             this.el.removeClass([this.invalidClass, this.validClass]);
36667             this.el.addClass(this.invalidClass);
36668         } else {
36669             this.el.removeClass(['is-invalid','is-valid']);
36670             this.el.addClass(['is-invalid']);
36671         }
36672         
36673         this.fireEvent('invalid', this, msg);
36674         
36675     },
36676     
36677     setValue : function(v, suppressEvent)
36678     {   
36679         if(this.value === v){
36680             return;
36681         }
36682         
36683         this.value = v;
36684         
36685         if(this.rendered){
36686             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36687         }
36688         
36689         Roo.each(this.radioes, function(i){
36690             i.checked = false;
36691             i.el.removeClass('checked');
36692         });
36693         
36694         Roo.each(this.radioes, function(i){
36695             
36696             if(i.value === v || i.value.toString() === v.toString()){
36697                 i.checked = true;
36698                 i.el.addClass('checked');
36699                 
36700                 if(suppressEvent !== true){
36701                     this.fireEvent('check', this, i);
36702                 }
36703                 
36704                 return false;
36705             }
36706             
36707         }, this);
36708         
36709         this.validate();
36710     },
36711     
36712     clearInvalid : function(){
36713         
36714         if(!this.el || this.preventMark){
36715             return;
36716         }
36717         
36718         this.el.removeClass([this.invalidClass]);
36719         
36720         this.fireEvent('valid', this);
36721     }
36722     
36723 });
36724
36725 Roo.apply(Roo.bootstrap.RadioSet, {
36726     
36727     groups: {},
36728     
36729     register : function(set)
36730     {
36731         this.groups[set.name] = set;
36732     },
36733     
36734     get: function(name) 
36735     {
36736         if (typeof(this.groups[name]) == 'undefined') {
36737             return false;
36738         }
36739         
36740         return this.groups[name] ;
36741     }
36742     
36743 });
36744 /*
36745  * Based on:
36746  * Ext JS Library 1.1.1
36747  * Copyright(c) 2006-2007, Ext JS, LLC.
36748  *
36749  * Originally Released Under LGPL - original licence link has changed is not relivant.
36750  *
36751  * Fork - LGPL
36752  * <script type="text/javascript">
36753  */
36754
36755
36756 /**
36757  * @class Roo.bootstrap.SplitBar
36758  * @extends Roo.util.Observable
36759  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36760  * <br><br>
36761  * Usage:
36762  * <pre><code>
36763 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36764                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36765 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36766 split.minSize = 100;
36767 split.maxSize = 600;
36768 split.animate = true;
36769 split.on('moved', splitterMoved);
36770 </code></pre>
36771  * @constructor
36772  * Create a new SplitBar
36773  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36774  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36775  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36776  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36777                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36778                         position of the SplitBar).
36779  */
36780 Roo.bootstrap.SplitBar = function(cfg){
36781     
36782     /** @private */
36783     
36784     //{
36785     //  dragElement : elm
36786     //  resizingElement: el,
36787         // optional..
36788     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36789     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36790         // existingProxy ???
36791     //}
36792     
36793     this.el = Roo.get(cfg.dragElement, true);
36794     this.el.dom.unselectable = "on";
36795     /** @private */
36796     this.resizingEl = Roo.get(cfg.resizingElement, true);
36797
36798     /**
36799      * @private
36800      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36801      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36802      * @type Number
36803      */
36804     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36805     
36806     /**
36807      * The minimum size of the resizing element. (Defaults to 0)
36808      * @type Number
36809      */
36810     this.minSize = 0;
36811     
36812     /**
36813      * The maximum size of the resizing element. (Defaults to 2000)
36814      * @type Number
36815      */
36816     this.maxSize = 2000;
36817     
36818     /**
36819      * Whether to animate the transition to the new size
36820      * @type Boolean
36821      */
36822     this.animate = false;
36823     
36824     /**
36825      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36826      * @type Boolean
36827      */
36828     this.useShim = false;
36829     
36830     /** @private */
36831     this.shim = null;
36832     
36833     if(!cfg.existingProxy){
36834         /** @private */
36835         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36836     }else{
36837         this.proxy = Roo.get(cfg.existingProxy).dom;
36838     }
36839     /** @private */
36840     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36841     
36842     /** @private */
36843     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36844     
36845     /** @private */
36846     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36847     
36848     /** @private */
36849     this.dragSpecs = {};
36850     
36851     /**
36852      * @private The adapter to use to positon and resize elements
36853      */
36854     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36855     this.adapter.init(this);
36856     
36857     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36858         /** @private */
36859         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36860         this.el.addClass("roo-splitbar-h");
36861     }else{
36862         /** @private */
36863         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36864         this.el.addClass("roo-splitbar-v");
36865     }
36866     
36867     this.addEvents({
36868         /**
36869          * @event resize
36870          * Fires when the splitter is moved (alias for {@link #event-moved})
36871          * @param {Roo.bootstrap.SplitBar} this
36872          * @param {Number} newSize the new width or height
36873          */
36874         "resize" : true,
36875         /**
36876          * @event moved
36877          * Fires when the splitter is moved
36878          * @param {Roo.bootstrap.SplitBar} this
36879          * @param {Number} newSize the new width or height
36880          */
36881         "moved" : true,
36882         /**
36883          * @event beforeresize
36884          * Fires before the splitter is dragged
36885          * @param {Roo.bootstrap.SplitBar} this
36886          */
36887         "beforeresize" : true,
36888
36889         "beforeapply" : true
36890     });
36891
36892     Roo.util.Observable.call(this);
36893 };
36894
36895 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36896     onStartProxyDrag : function(x, y){
36897         this.fireEvent("beforeresize", this);
36898         if(!this.overlay){
36899             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36900             o.unselectable();
36901             o.enableDisplayMode("block");
36902             // all splitbars share the same overlay
36903             Roo.bootstrap.SplitBar.prototype.overlay = o;
36904         }
36905         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36906         this.overlay.show();
36907         Roo.get(this.proxy).setDisplayed("block");
36908         var size = this.adapter.getElementSize(this);
36909         this.activeMinSize = this.getMinimumSize();;
36910         this.activeMaxSize = this.getMaximumSize();;
36911         var c1 = size - this.activeMinSize;
36912         var c2 = Math.max(this.activeMaxSize - size, 0);
36913         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36914             this.dd.resetConstraints();
36915             this.dd.setXConstraint(
36916                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36917                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36918             );
36919             this.dd.setYConstraint(0, 0);
36920         }else{
36921             this.dd.resetConstraints();
36922             this.dd.setXConstraint(0, 0);
36923             this.dd.setYConstraint(
36924                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36925                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36926             );
36927          }
36928         this.dragSpecs.startSize = size;
36929         this.dragSpecs.startPoint = [x, y];
36930         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36931     },
36932     
36933     /** 
36934      * @private Called after the drag operation by the DDProxy
36935      */
36936     onEndProxyDrag : function(e){
36937         Roo.get(this.proxy).setDisplayed(false);
36938         var endPoint = Roo.lib.Event.getXY(e);
36939         if(this.overlay){
36940             this.overlay.hide();
36941         }
36942         var newSize;
36943         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36944             newSize = this.dragSpecs.startSize + 
36945                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36946                     endPoint[0] - this.dragSpecs.startPoint[0] :
36947                     this.dragSpecs.startPoint[0] - endPoint[0]
36948                 );
36949         }else{
36950             newSize = this.dragSpecs.startSize + 
36951                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36952                     endPoint[1] - this.dragSpecs.startPoint[1] :
36953                     this.dragSpecs.startPoint[1] - endPoint[1]
36954                 );
36955         }
36956         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36957         if(newSize != this.dragSpecs.startSize){
36958             if(this.fireEvent('beforeapply', this, newSize) !== false){
36959                 this.adapter.setElementSize(this, newSize);
36960                 this.fireEvent("moved", this, newSize);
36961                 this.fireEvent("resize", this, newSize);
36962             }
36963         }
36964     },
36965     
36966     /**
36967      * Get the adapter this SplitBar uses
36968      * @return The adapter object
36969      */
36970     getAdapter : function(){
36971         return this.adapter;
36972     },
36973     
36974     /**
36975      * Set the adapter this SplitBar uses
36976      * @param {Object} adapter A SplitBar adapter object
36977      */
36978     setAdapter : function(adapter){
36979         this.adapter = adapter;
36980         this.adapter.init(this);
36981     },
36982     
36983     /**
36984      * Gets the minimum size for the resizing element
36985      * @return {Number} The minimum size
36986      */
36987     getMinimumSize : function(){
36988         return this.minSize;
36989     },
36990     
36991     /**
36992      * Sets the minimum size for the resizing element
36993      * @param {Number} minSize The minimum size
36994      */
36995     setMinimumSize : function(minSize){
36996         this.minSize = minSize;
36997     },
36998     
36999     /**
37000      * Gets the maximum size for the resizing element
37001      * @return {Number} The maximum size
37002      */
37003     getMaximumSize : function(){
37004         return this.maxSize;
37005     },
37006     
37007     /**
37008      * Sets the maximum size for the resizing element
37009      * @param {Number} maxSize The maximum size
37010      */
37011     setMaximumSize : function(maxSize){
37012         this.maxSize = maxSize;
37013     },
37014     
37015     /**
37016      * Sets the initialize size for the resizing element
37017      * @param {Number} size The initial size
37018      */
37019     setCurrentSize : function(size){
37020         var oldAnimate = this.animate;
37021         this.animate = false;
37022         this.adapter.setElementSize(this, size);
37023         this.animate = oldAnimate;
37024     },
37025     
37026     /**
37027      * Destroy this splitbar. 
37028      * @param {Boolean} removeEl True to remove the element
37029      */
37030     destroy : function(removeEl){
37031         if(this.shim){
37032             this.shim.remove();
37033         }
37034         this.dd.unreg();
37035         this.proxy.parentNode.removeChild(this.proxy);
37036         if(removeEl){
37037             this.el.remove();
37038         }
37039     }
37040 });
37041
37042 /**
37043  * @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.
37044  */
37045 Roo.bootstrap.SplitBar.createProxy = function(dir){
37046     var proxy = new Roo.Element(document.createElement("div"));
37047     proxy.unselectable();
37048     var cls = 'roo-splitbar-proxy';
37049     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37050     document.body.appendChild(proxy.dom);
37051     return proxy.dom;
37052 };
37053
37054 /** 
37055  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37056  * Default Adapter. It assumes the splitter and resizing element are not positioned
37057  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37058  */
37059 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37060 };
37061
37062 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37063     // do nothing for now
37064     init : function(s){
37065     
37066     },
37067     /**
37068      * Called before drag operations to get the current size of the resizing element. 
37069      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37070      */
37071      getElementSize : function(s){
37072         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37073             return s.resizingEl.getWidth();
37074         }else{
37075             return s.resizingEl.getHeight();
37076         }
37077     },
37078     
37079     /**
37080      * Called after drag operations to set the size of the resizing element.
37081      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37082      * @param {Number} newSize The new size to set
37083      * @param {Function} onComplete A function to be invoked when resizing is complete
37084      */
37085     setElementSize : function(s, newSize, onComplete){
37086         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37087             if(!s.animate){
37088                 s.resizingEl.setWidth(newSize);
37089                 if(onComplete){
37090                     onComplete(s, newSize);
37091                 }
37092             }else{
37093                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37094             }
37095         }else{
37096             
37097             if(!s.animate){
37098                 s.resizingEl.setHeight(newSize);
37099                 if(onComplete){
37100                     onComplete(s, newSize);
37101                 }
37102             }else{
37103                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37104             }
37105         }
37106     }
37107 };
37108
37109 /** 
37110  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37111  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37112  * Adapter that  moves the splitter element to align with the resized sizing element. 
37113  * Used with an absolute positioned SplitBar.
37114  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37115  * document.body, make sure you assign an id to the body element.
37116  */
37117 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37118     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37119     this.container = Roo.get(container);
37120 };
37121
37122 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37123     init : function(s){
37124         this.basic.init(s);
37125     },
37126     
37127     getElementSize : function(s){
37128         return this.basic.getElementSize(s);
37129     },
37130     
37131     setElementSize : function(s, newSize, onComplete){
37132         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37133     },
37134     
37135     moveSplitter : function(s){
37136         var yes = Roo.bootstrap.SplitBar;
37137         switch(s.placement){
37138             case yes.LEFT:
37139                 s.el.setX(s.resizingEl.getRight());
37140                 break;
37141             case yes.RIGHT:
37142                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37143                 break;
37144             case yes.TOP:
37145                 s.el.setY(s.resizingEl.getBottom());
37146                 break;
37147             case yes.BOTTOM:
37148                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37149                 break;
37150         }
37151     }
37152 };
37153
37154 /**
37155  * Orientation constant - Create a vertical SplitBar
37156  * @static
37157  * @type Number
37158  */
37159 Roo.bootstrap.SplitBar.VERTICAL = 1;
37160
37161 /**
37162  * Orientation constant - Create a horizontal SplitBar
37163  * @static
37164  * @type Number
37165  */
37166 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37167
37168 /**
37169  * Placement constant - The resizing element is to the left of the splitter element
37170  * @static
37171  * @type Number
37172  */
37173 Roo.bootstrap.SplitBar.LEFT = 1;
37174
37175 /**
37176  * Placement constant - The resizing element is to the right of the splitter element
37177  * @static
37178  * @type Number
37179  */
37180 Roo.bootstrap.SplitBar.RIGHT = 2;
37181
37182 /**
37183  * Placement constant - The resizing element is positioned above the splitter element
37184  * @static
37185  * @type Number
37186  */
37187 Roo.bootstrap.SplitBar.TOP = 3;
37188
37189 /**
37190  * Placement constant - The resizing element is positioned under splitter element
37191  * @static
37192  * @type Number
37193  */
37194 Roo.bootstrap.SplitBar.BOTTOM = 4;
37195 Roo.namespace("Roo.bootstrap.layout");/*
37196  * Based on:
37197  * Ext JS Library 1.1.1
37198  * Copyright(c) 2006-2007, Ext JS, LLC.
37199  *
37200  * Originally Released Under LGPL - original licence link has changed is not relivant.
37201  *
37202  * Fork - LGPL
37203  * <script type="text/javascript">
37204  */
37205
37206 /**
37207  * @class Roo.bootstrap.layout.Manager
37208  * @extends Roo.bootstrap.Component
37209  * Base class for layout managers.
37210  */
37211 Roo.bootstrap.layout.Manager = function(config)
37212 {
37213     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37214
37215
37216
37217
37218
37219     /** false to disable window resize monitoring @type Boolean */
37220     this.monitorWindowResize = true;
37221     this.regions = {};
37222     this.addEvents({
37223         /**
37224          * @event layout
37225          * Fires when a layout is performed.
37226          * @param {Roo.LayoutManager} this
37227          */
37228         "layout" : true,
37229         /**
37230          * @event regionresized
37231          * Fires when the user resizes a region.
37232          * @param {Roo.LayoutRegion} region The resized region
37233          * @param {Number} newSize The new size (width for east/west, height for north/south)
37234          */
37235         "regionresized" : true,
37236         /**
37237          * @event regioncollapsed
37238          * Fires when a region is collapsed.
37239          * @param {Roo.LayoutRegion} region The collapsed region
37240          */
37241         "regioncollapsed" : true,
37242         /**
37243          * @event regionexpanded
37244          * Fires when a region is expanded.
37245          * @param {Roo.LayoutRegion} region The expanded region
37246          */
37247         "regionexpanded" : true
37248     });
37249     this.updating = false;
37250
37251     if (config.el) {
37252         this.el = Roo.get(config.el);
37253         this.initEvents();
37254     }
37255
37256 };
37257
37258 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37259
37260
37261     regions : null,
37262
37263     monitorWindowResize : true,
37264
37265
37266     updating : false,
37267
37268
37269     onRender : function(ct, position)
37270     {
37271         if(!this.el){
37272             this.el = Roo.get(ct);
37273             this.initEvents();
37274         }
37275         //this.fireEvent('render',this);
37276     },
37277
37278
37279     initEvents: function()
37280     {
37281
37282
37283         // ie scrollbar fix
37284         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37285             document.body.scroll = "no";
37286         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37287             this.el.position('relative');
37288         }
37289         this.id = this.el.id;
37290         this.el.addClass("roo-layout-container");
37291         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37292         if(this.el.dom != document.body ) {
37293             this.el.on('resize', this.layout,this);
37294             this.el.on('show', this.layout,this);
37295         }
37296
37297     },
37298
37299     /**
37300      * Returns true if this layout is currently being updated
37301      * @return {Boolean}
37302      */
37303     isUpdating : function(){
37304         return this.updating;
37305     },
37306
37307     /**
37308      * Suspend the LayoutManager from doing auto-layouts while
37309      * making multiple add or remove calls
37310      */
37311     beginUpdate : function(){
37312         this.updating = true;
37313     },
37314
37315     /**
37316      * Restore auto-layouts and optionally disable the manager from performing a layout
37317      * @param {Boolean} noLayout true to disable a layout update
37318      */
37319     endUpdate : function(noLayout){
37320         this.updating = false;
37321         if(!noLayout){
37322             this.layout();
37323         }
37324     },
37325
37326     layout: function(){
37327         // abstract...
37328     },
37329
37330     onRegionResized : function(region, newSize){
37331         this.fireEvent("regionresized", region, newSize);
37332         this.layout();
37333     },
37334
37335     onRegionCollapsed : function(region){
37336         this.fireEvent("regioncollapsed", region);
37337     },
37338
37339     onRegionExpanded : function(region){
37340         this.fireEvent("regionexpanded", region);
37341     },
37342
37343     /**
37344      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37345      * performs box-model adjustments.
37346      * @return {Object} The size as an object {width: (the width), height: (the height)}
37347      */
37348     getViewSize : function()
37349     {
37350         var size;
37351         if(this.el.dom != document.body){
37352             size = this.el.getSize();
37353         }else{
37354             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37355         }
37356         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37357         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37358         return size;
37359     },
37360
37361     /**
37362      * Returns the Element this layout is bound to.
37363      * @return {Roo.Element}
37364      */
37365     getEl : function(){
37366         return this.el;
37367     },
37368
37369     /**
37370      * Returns the specified region.
37371      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37372      * @return {Roo.LayoutRegion}
37373      */
37374     getRegion : function(target){
37375         return this.regions[target.toLowerCase()];
37376     },
37377
37378     onWindowResize : function(){
37379         if(this.monitorWindowResize){
37380             this.layout();
37381         }
37382     }
37383 });
37384 /*
37385  * Based on:
37386  * Ext JS Library 1.1.1
37387  * Copyright(c) 2006-2007, Ext JS, LLC.
37388  *
37389  * Originally Released Under LGPL - original licence link has changed is not relivant.
37390  *
37391  * Fork - LGPL
37392  * <script type="text/javascript">
37393  */
37394 /**
37395  * @class Roo.bootstrap.layout.Border
37396  * @extends Roo.bootstrap.layout.Manager
37397  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37398  * please see: examples/bootstrap/nested.html<br><br>
37399  
37400 <b>The container the layout is rendered into can be either the body element or any other element.
37401 If it is not the body element, the container needs to either be an absolute positioned element,
37402 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37403 the container size if it is not the body element.</b>
37404
37405 * @constructor
37406 * Create a new Border
37407 * @param {Object} config Configuration options
37408  */
37409 Roo.bootstrap.layout.Border = function(config){
37410     config = config || {};
37411     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37412     
37413     
37414     
37415     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37416         if(config[region]){
37417             config[region].region = region;
37418             this.addRegion(config[region]);
37419         }
37420     },this);
37421     
37422 };
37423
37424 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37425
37426 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37427     
37428     parent : false, // this might point to a 'nest' or a ???
37429     
37430     /**
37431      * Creates and adds a new region if it doesn't already exist.
37432      * @param {String} target The target region key (north, south, east, west or center).
37433      * @param {Object} config The regions config object
37434      * @return {BorderLayoutRegion} The new region
37435      */
37436     addRegion : function(config)
37437     {
37438         if(!this.regions[config.region]){
37439             var r = this.factory(config);
37440             this.bindRegion(r);
37441         }
37442         return this.regions[config.region];
37443     },
37444
37445     // private (kinda)
37446     bindRegion : function(r){
37447         this.regions[r.config.region] = r;
37448         
37449         r.on("visibilitychange",    this.layout, this);
37450         r.on("paneladded",          this.layout, this);
37451         r.on("panelremoved",        this.layout, this);
37452         r.on("invalidated",         this.layout, this);
37453         r.on("resized",             this.onRegionResized, this);
37454         r.on("collapsed",           this.onRegionCollapsed, this);
37455         r.on("expanded",            this.onRegionExpanded, this);
37456     },
37457
37458     /**
37459      * Performs a layout update.
37460      */
37461     layout : function()
37462     {
37463         if(this.updating) {
37464             return;
37465         }
37466         
37467         // render all the rebions if they have not been done alreayd?
37468         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37469             if(this.regions[region] && !this.regions[region].bodyEl){
37470                 this.regions[region].onRender(this.el)
37471             }
37472         },this);
37473         
37474         var size = this.getViewSize();
37475         var w = size.width;
37476         var h = size.height;
37477         var centerW = w;
37478         var centerH = h;
37479         var centerY = 0;
37480         var centerX = 0;
37481         //var x = 0, y = 0;
37482
37483         var rs = this.regions;
37484         var north = rs["north"];
37485         var south = rs["south"]; 
37486         var west = rs["west"];
37487         var east = rs["east"];
37488         var center = rs["center"];
37489         //if(this.hideOnLayout){ // not supported anymore
37490             //c.el.setStyle("display", "none");
37491         //}
37492         if(north && north.isVisible()){
37493             var b = north.getBox();
37494             var m = north.getMargins();
37495             b.width = w - (m.left+m.right);
37496             b.x = m.left;
37497             b.y = m.top;
37498             centerY = b.height + b.y + m.bottom;
37499             centerH -= centerY;
37500             north.updateBox(this.safeBox(b));
37501         }
37502         if(south && south.isVisible()){
37503             var b = south.getBox();
37504             var m = south.getMargins();
37505             b.width = w - (m.left+m.right);
37506             b.x = m.left;
37507             var totalHeight = (b.height + m.top + m.bottom);
37508             b.y = h - totalHeight + m.top;
37509             centerH -= totalHeight;
37510             south.updateBox(this.safeBox(b));
37511         }
37512         if(west && west.isVisible()){
37513             var b = west.getBox();
37514             var m = west.getMargins();
37515             b.height = centerH - (m.top+m.bottom);
37516             b.x = m.left;
37517             b.y = centerY + m.top;
37518             var totalWidth = (b.width + m.left + m.right);
37519             centerX += totalWidth;
37520             centerW -= totalWidth;
37521             west.updateBox(this.safeBox(b));
37522         }
37523         if(east && east.isVisible()){
37524             var b = east.getBox();
37525             var m = east.getMargins();
37526             b.height = centerH - (m.top+m.bottom);
37527             var totalWidth = (b.width + m.left + m.right);
37528             b.x = w - totalWidth + m.left;
37529             b.y = centerY + m.top;
37530             centerW -= totalWidth;
37531             east.updateBox(this.safeBox(b));
37532         }
37533         if(center){
37534             var m = center.getMargins();
37535             var centerBox = {
37536                 x: centerX + m.left,
37537                 y: centerY + m.top,
37538                 width: centerW - (m.left+m.right),
37539                 height: centerH - (m.top+m.bottom)
37540             };
37541             //if(this.hideOnLayout){
37542                 //center.el.setStyle("display", "block");
37543             //}
37544             center.updateBox(this.safeBox(centerBox));
37545         }
37546         this.el.repaint();
37547         this.fireEvent("layout", this);
37548     },
37549
37550     // private
37551     safeBox : function(box){
37552         box.width = Math.max(0, box.width);
37553         box.height = Math.max(0, box.height);
37554         return box;
37555     },
37556
37557     /**
37558      * Adds a ContentPanel (or subclass) to this layout.
37559      * @param {String} target The target region key (north, south, east, west or center).
37560      * @param {Roo.ContentPanel} panel The panel to add
37561      * @return {Roo.ContentPanel} The added panel
37562      */
37563     add : function(target, panel){
37564          
37565         target = target.toLowerCase();
37566         return this.regions[target].add(panel);
37567     },
37568
37569     /**
37570      * Remove a ContentPanel (or subclass) to this layout.
37571      * @param {String} target The target region key (north, south, east, west or center).
37572      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37573      * @return {Roo.ContentPanel} The removed panel
37574      */
37575     remove : function(target, panel){
37576         target = target.toLowerCase();
37577         return this.regions[target].remove(panel);
37578     },
37579
37580     /**
37581      * Searches all regions for a panel with the specified id
37582      * @param {String} panelId
37583      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37584      */
37585     findPanel : function(panelId){
37586         var rs = this.regions;
37587         for(var target in rs){
37588             if(typeof rs[target] != "function"){
37589                 var p = rs[target].getPanel(panelId);
37590                 if(p){
37591                     return p;
37592                 }
37593             }
37594         }
37595         return null;
37596     },
37597
37598     /**
37599      * Searches all regions for a panel with the specified id and activates (shows) it.
37600      * @param {String/ContentPanel} panelId The panels id or the panel itself
37601      * @return {Roo.ContentPanel} The shown panel or null
37602      */
37603     showPanel : function(panelId) {
37604       var rs = this.regions;
37605       for(var target in rs){
37606          var r = rs[target];
37607          if(typeof r != "function"){
37608             if(r.hasPanel(panelId)){
37609                return r.showPanel(panelId);
37610             }
37611          }
37612       }
37613       return null;
37614    },
37615
37616    /**
37617      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37618      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37619      */
37620    /*
37621     restoreState : function(provider){
37622         if(!provider){
37623             provider = Roo.state.Manager;
37624         }
37625         var sm = new Roo.LayoutStateManager();
37626         sm.init(this, provider);
37627     },
37628 */
37629  
37630  
37631     /**
37632      * Adds a xtype elements to the layout.
37633      * <pre><code>
37634
37635 layout.addxtype({
37636        xtype : 'ContentPanel',
37637        region: 'west',
37638        items: [ .... ]
37639    }
37640 );
37641
37642 layout.addxtype({
37643         xtype : 'NestedLayoutPanel',
37644         region: 'west',
37645         layout: {
37646            center: { },
37647            west: { }   
37648         },
37649         items : [ ... list of content panels or nested layout panels.. ]
37650    }
37651 );
37652 </code></pre>
37653      * @param {Object} cfg Xtype definition of item to add.
37654      */
37655     addxtype : function(cfg)
37656     {
37657         // basically accepts a pannel...
37658         // can accept a layout region..!?!?
37659         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37660         
37661         
37662         // theory?  children can only be panels??
37663         
37664         //if (!cfg.xtype.match(/Panel$/)) {
37665         //    return false;
37666         //}
37667         var ret = false;
37668         
37669         if (typeof(cfg.region) == 'undefined') {
37670             Roo.log("Failed to add Panel, region was not set");
37671             Roo.log(cfg);
37672             return false;
37673         }
37674         var region = cfg.region;
37675         delete cfg.region;
37676         
37677           
37678         var xitems = [];
37679         if (cfg.items) {
37680             xitems = cfg.items;
37681             delete cfg.items;
37682         }
37683         var nb = false;
37684         
37685         if ( region == 'center') {
37686             Roo.log("Center: " + cfg.title);
37687         }
37688         
37689         
37690         switch(cfg.xtype) 
37691         {
37692             case 'Content':  // ContentPanel (el, cfg)
37693             case 'Scroll':  // ContentPanel (el, cfg)
37694             case 'View': 
37695                 cfg.autoCreate = cfg.autoCreate || true;
37696                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37697                 //} else {
37698                 //    var el = this.el.createChild();
37699                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37700                 //}
37701                 
37702                 this.add(region, ret);
37703                 break;
37704             
37705             /*
37706             case 'TreePanel': // our new panel!
37707                 cfg.el = this.el.createChild();
37708                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37709                 this.add(region, ret);
37710                 break;
37711             */
37712             
37713             case 'Nest': 
37714                 // create a new Layout (which is  a Border Layout...
37715                 
37716                 var clayout = cfg.layout;
37717                 clayout.el  = this.el.createChild();
37718                 clayout.items   = clayout.items  || [];
37719                 
37720                 delete cfg.layout;
37721                 
37722                 // replace this exitems with the clayout ones..
37723                 xitems = clayout.items;
37724                  
37725                 // force background off if it's in center...
37726                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37727                     cfg.background = false;
37728                 }
37729                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37730                 
37731                 
37732                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37733                 //console.log('adding nested layout panel '  + cfg.toSource());
37734                 this.add(region, ret);
37735                 nb = {}; /// find first...
37736                 break;
37737             
37738             case 'Grid':
37739                 
37740                 // needs grid and region
37741                 
37742                 //var el = this.getRegion(region).el.createChild();
37743                 /*
37744                  *var el = this.el.createChild();
37745                 // create the grid first...
37746                 cfg.grid.container = el;
37747                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37748                 */
37749                 
37750                 if (region == 'center' && this.active ) {
37751                     cfg.background = false;
37752                 }
37753                 
37754                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37755                 
37756                 this.add(region, ret);
37757                 /*
37758                 if (cfg.background) {
37759                     // render grid on panel activation (if panel background)
37760                     ret.on('activate', function(gp) {
37761                         if (!gp.grid.rendered) {
37762                     //        gp.grid.render(el);
37763                         }
37764                     });
37765                 } else {
37766                   //  cfg.grid.render(el);
37767                 }
37768                 */
37769                 break;
37770            
37771            
37772             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37773                 // it was the old xcomponent building that caused this before.
37774                 // espeically if border is the top element in the tree.
37775                 ret = this;
37776                 break; 
37777                 
37778                     
37779                 
37780                 
37781                 
37782             default:
37783                 /*
37784                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37785                     
37786                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37787                     this.add(region, ret);
37788                 } else {
37789                 */
37790                     Roo.log(cfg);
37791                     throw "Can not add '" + cfg.xtype + "' to Border";
37792                     return null;
37793              
37794                                 
37795              
37796         }
37797         this.beginUpdate();
37798         // add children..
37799         var region = '';
37800         var abn = {};
37801         Roo.each(xitems, function(i)  {
37802             region = nb && i.region ? i.region : false;
37803             
37804             var add = ret.addxtype(i);
37805            
37806             if (region) {
37807                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37808                 if (!i.background) {
37809                     abn[region] = nb[region] ;
37810                 }
37811             }
37812             
37813         });
37814         this.endUpdate();
37815
37816         // make the last non-background panel active..
37817         //if (nb) { Roo.log(abn); }
37818         if (nb) {
37819             
37820             for(var r in abn) {
37821                 region = this.getRegion(r);
37822                 if (region) {
37823                     // tried using nb[r], but it does not work..
37824                      
37825                     region.showPanel(abn[r]);
37826                    
37827                 }
37828             }
37829         }
37830         return ret;
37831         
37832     },
37833     
37834     
37835 // private
37836     factory : function(cfg)
37837     {
37838         
37839         var validRegions = Roo.bootstrap.layout.Border.regions;
37840
37841         var target = cfg.region;
37842         cfg.mgr = this;
37843         
37844         var r = Roo.bootstrap.layout;
37845         Roo.log(target);
37846         switch(target){
37847             case "north":
37848                 return new r.North(cfg);
37849             case "south":
37850                 return new r.South(cfg);
37851             case "east":
37852                 return new r.East(cfg);
37853             case "west":
37854                 return new r.West(cfg);
37855             case "center":
37856                 return new r.Center(cfg);
37857         }
37858         throw 'Layout region "'+target+'" not supported.';
37859     }
37860     
37861     
37862 });
37863  /*
37864  * Based on:
37865  * Ext JS Library 1.1.1
37866  * Copyright(c) 2006-2007, Ext JS, LLC.
37867  *
37868  * Originally Released Under LGPL - original licence link has changed is not relivant.
37869  *
37870  * Fork - LGPL
37871  * <script type="text/javascript">
37872  */
37873  
37874 /**
37875  * @class Roo.bootstrap.layout.Basic
37876  * @extends Roo.util.Observable
37877  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37878  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37879  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37880  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37881  * @cfg {string}   region  the region that it inhabits..
37882  * @cfg {bool}   skipConfig skip config?
37883  * 
37884
37885  */
37886 Roo.bootstrap.layout.Basic = function(config){
37887     
37888     this.mgr = config.mgr;
37889     
37890     this.position = config.region;
37891     
37892     var skipConfig = config.skipConfig;
37893     
37894     this.events = {
37895         /**
37896          * @scope Roo.BasicLayoutRegion
37897          */
37898         
37899         /**
37900          * @event beforeremove
37901          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37902          * @param {Roo.LayoutRegion} this
37903          * @param {Roo.ContentPanel} panel The panel
37904          * @param {Object} e The cancel event object
37905          */
37906         "beforeremove" : true,
37907         /**
37908          * @event invalidated
37909          * Fires when the layout for this region is changed.
37910          * @param {Roo.LayoutRegion} this
37911          */
37912         "invalidated" : true,
37913         /**
37914          * @event visibilitychange
37915          * Fires when this region is shown or hidden 
37916          * @param {Roo.LayoutRegion} this
37917          * @param {Boolean} visibility true or false
37918          */
37919         "visibilitychange" : true,
37920         /**
37921          * @event paneladded
37922          * Fires when a panel is added. 
37923          * @param {Roo.LayoutRegion} this
37924          * @param {Roo.ContentPanel} panel The panel
37925          */
37926         "paneladded" : true,
37927         /**
37928          * @event panelremoved
37929          * Fires when a panel is removed. 
37930          * @param {Roo.LayoutRegion} this
37931          * @param {Roo.ContentPanel} panel The panel
37932          */
37933         "panelremoved" : true,
37934         /**
37935          * @event beforecollapse
37936          * Fires when this region before collapse.
37937          * @param {Roo.LayoutRegion} this
37938          */
37939         "beforecollapse" : true,
37940         /**
37941          * @event collapsed
37942          * Fires when this region is collapsed.
37943          * @param {Roo.LayoutRegion} this
37944          */
37945         "collapsed" : true,
37946         /**
37947          * @event expanded
37948          * Fires when this region is expanded.
37949          * @param {Roo.LayoutRegion} this
37950          */
37951         "expanded" : true,
37952         /**
37953          * @event slideshow
37954          * Fires when this region is slid into view.
37955          * @param {Roo.LayoutRegion} this
37956          */
37957         "slideshow" : true,
37958         /**
37959          * @event slidehide
37960          * Fires when this region slides out of view. 
37961          * @param {Roo.LayoutRegion} this
37962          */
37963         "slidehide" : true,
37964         /**
37965          * @event panelactivated
37966          * Fires when a panel is activated. 
37967          * @param {Roo.LayoutRegion} this
37968          * @param {Roo.ContentPanel} panel The activated panel
37969          */
37970         "panelactivated" : true,
37971         /**
37972          * @event resized
37973          * Fires when the user resizes this region. 
37974          * @param {Roo.LayoutRegion} this
37975          * @param {Number} newSize The new size (width for east/west, height for north/south)
37976          */
37977         "resized" : true
37978     };
37979     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37980     this.panels = new Roo.util.MixedCollection();
37981     this.panels.getKey = this.getPanelId.createDelegate(this);
37982     this.box = null;
37983     this.activePanel = null;
37984     // ensure listeners are added...
37985     
37986     if (config.listeners || config.events) {
37987         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37988             listeners : config.listeners || {},
37989             events : config.events || {}
37990         });
37991     }
37992     
37993     if(skipConfig !== true){
37994         this.applyConfig(config);
37995     }
37996 };
37997
37998 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37999 {
38000     getPanelId : function(p){
38001         return p.getId();
38002     },
38003     
38004     applyConfig : function(config){
38005         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38006         this.config = config;
38007         
38008     },
38009     
38010     /**
38011      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38012      * the width, for horizontal (north, south) the height.
38013      * @param {Number} newSize The new width or height
38014      */
38015     resizeTo : function(newSize){
38016         var el = this.el ? this.el :
38017                  (this.activePanel ? this.activePanel.getEl() : null);
38018         if(el){
38019             switch(this.position){
38020                 case "east":
38021                 case "west":
38022                     el.setWidth(newSize);
38023                     this.fireEvent("resized", this, newSize);
38024                 break;
38025                 case "north":
38026                 case "south":
38027                     el.setHeight(newSize);
38028                     this.fireEvent("resized", this, newSize);
38029                 break;                
38030             }
38031         }
38032     },
38033     
38034     getBox : function(){
38035         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38036     },
38037     
38038     getMargins : function(){
38039         return this.margins;
38040     },
38041     
38042     updateBox : function(box){
38043         this.box = box;
38044         var el = this.activePanel.getEl();
38045         el.dom.style.left = box.x + "px";
38046         el.dom.style.top = box.y + "px";
38047         this.activePanel.setSize(box.width, box.height);
38048     },
38049     
38050     /**
38051      * Returns the container element for this region.
38052      * @return {Roo.Element}
38053      */
38054     getEl : function(){
38055         return this.activePanel;
38056     },
38057     
38058     /**
38059      * Returns true if this region is currently visible.
38060      * @return {Boolean}
38061      */
38062     isVisible : function(){
38063         return this.activePanel ? true : false;
38064     },
38065     
38066     setActivePanel : function(panel){
38067         panel = this.getPanel(panel);
38068         if(this.activePanel && this.activePanel != panel){
38069             this.activePanel.setActiveState(false);
38070             this.activePanel.getEl().setLeftTop(-10000,-10000);
38071         }
38072         this.activePanel = panel;
38073         panel.setActiveState(true);
38074         if(this.box){
38075             panel.setSize(this.box.width, this.box.height);
38076         }
38077         this.fireEvent("panelactivated", this, panel);
38078         this.fireEvent("invalidated");
38079     },
38080     
38081     /**
38082      * Show the specified panel.
38083      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38084      * @return {Roo.ContentPanel} The shown panel or null
38085      */
38086     showPanel : function(panel){
38087         panel = this.getPanel(panel);
38088         if(panel){
38089             this.setActivePanel(panel);
38090         }
38091         return panel;
38092     },
38093     
38094     /**
38095      * Get the active panel for this region.
38096      * @return {Roo.ContentPanel} The active panel or null
38097      */
38098     getActivePanel : function(){
38099         return this.activePanel;
38100     },
38101     
38102     /**
38103      * Add the passed ContentPanel(s)
38104      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38105      * @return {Roo.ContentPanel} The panel added (if only one was added)
38106      */
38107     add : function(panel){
38108         if(arguments.length > 1){
38109             for(var i = 0, len = arguments.length; i < len; i++) {
38110                 this.add(arguments[i]);
38111             }
38112             return null;
38113         }
38114         if(this.hasPanel(panel)){
38115             this.showPanel(panel);
38116             return panel;
38117         }
38118         var el = panel.getEl();
38119         if(el.dom.parentNode != this.mgr.el.dom){
38120             this.mgr.el.dom.appendChild(el.dom);
38121         }
38122         if(panel.setRegion){
38123             panel.setRegion(this);
38124         }
38125         this.panels.add(panel);
38126         el.setStyle("position", "absolute");
38127         if(!panel.background){
38128             this.setActivePanel(panel);
38129             if(this.config.initialSize && this.panels.getCount()==1){
38130                 this.resizeTo(this.config.initialSize);
38131             }
38132         }
38133         this.fireEvent("paneladded", this, panel);
38134         return panel;
38135     },
38136     
38137     /**
38138      * Returns true if the panel is in this region.
38139      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38140      * @return {Boolean}
38141      */
38142     hasPanel : function(panel){
38143         if(typeof panel == "object"){ // must be panel obj
38144             panel = panel.getId();
38145         }
38146         return this.getPanel(panel) ? true : false;
38147     },
38148     
38149     /**
38150      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38151      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38152      * @param {Boolean} preservePanel Overrides the config preservePanel option
38153      * @return {Roo.ContentPanel} The panel that was removed
38154      */
38155     remove : function(panel, preservePanel){
38156         panel = this.getPanel(panel);
38157         if(!panel){
38158             return null;
38159         }
38160         var e = {};
38161         this.fireEvent("beforeremove", this, panel, e);
38162         if(e.cancel === true){
38163             return null;
38164         }
38165         var panelId = panel.getId();
38166         this.panels.removeKey(panelId);
38167         return panel;
38168     },
38169     
38170     /**
38171      * Returns the panel specified or null if it's not in this region.
38172      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38173      * @return {Roo.ContentPanel}
38174      */
38175     getPanel : function(id){
38176         if(typeof id == "object"){ // must be panel obj
38177             return id;
38178         }
38179         return this.panels.get(id);
38180     },
38181     
38182     /**
38183      * Returns this regions position (north/south/east/west/center).
38184      * @return {String} 
38185      */
38186     getPosition: function(){
38187         return this.position;    
38188     }
38189 });/*
38190  * Based on:
38191  * Ext JS Library 1.1.1
38192  * Copyright(c) 2006-2007, Ext JS, LLC.
38193  *
38194  * Originally Released Under LGPL - original licence link has changed is not relivant.
38195  *
38196  * Fork - LGPL
38197  * <script type="text/javascript">
38198  */
38199  
38200 /**
38201  * @class Roo.bootstrap.layout.Region
38202  * @extends Roo.bootstrap.layout.Basic
38203  * This class represents a region in a layout manager.
38204  
38205  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38206  * @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})
38207  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38208  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38209  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38210  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38211  * @cfg {String}    title           The title for the region (overrides panel titles)
38212  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38213  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38214  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38215  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38216  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38217  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38218  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38219  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38220  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38221  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38222
38223  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38224  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38225  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38226  * @cfg {Number}    width           For East/West panels
38227  * @cfg {Number}    height          For North/South panels
38228  * @cfg {Boolean}   split           To show the splitter
38229  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38230  * 
38231  * @cfg {string}   cls             Extra CSS classes to add to region
38232  * 
38233  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38234  * @cfg {string}   region  the region that it inhabits..
38235  *
38236
38237  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38238  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38239
38240  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38241  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38242  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38243  */
38244 Roo.bootstrap.layout.Region = function(config)
38245 {
38246     this.applyConfig(config);
38247
38248     var mgr = config.mgr;
38249     var pos = config.region;
38250     config.skipConfig = true;
38251     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38252     
38253     if (mgr.el) {
38254         this.onRender(mgr.el);   
38255     }
38256      
38257     this.visible = true;
38258     this.collapsed = false;
38259     this.unrendered_panels = [];
38260 };
38261
38262 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38263
38264     position: '', // set by wrapper (eg. north/south etc..)
38265     unrendered_panels : null,  // unrendered panels.
38266     
38267     tabPosition : false,
38268     
38269     mgr: false, // points to 'Border'
38270     
38271     
38272     createBody : function(){
38273         /** This region's body element 
38274         * @type Roo.Element */
38275         this.bodyEl = this.el.createChild({
38276                 tag: "div",
38277                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38278         });
38279     },
38280
38281     onRender: function(ctr, pos)
38282     {
38283         var dh = Roo.DomHelper;
38284         /** This region's container element 
38285         * @type Roo.Element */
38286         this.el = dh.append(ctr.dom, {
38287                 tag: "div",
38288                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38289             }, true);
38290         /** This region's title element 
38291         * @type Roo.Element */
38292     
38293         this.titleEl = dh.append(this.el.dom,  {
38294                 tag: "div",
38295                 unselectable: "on",
38296                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38297                 children:[
38298                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38299                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38300                 ]
38301             }, true);
38302         
38303         this.titleEl.enableDisplayMode();
38304         /** This region's title text element 
38305         * @type HTMLElement */
38306         this.titleTextEl = this.titleEl.dom.firstChild;
38307         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38308         /*
38309         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38310         this.closeBtn.enableDisplayMode();
38311         this.closeBtn.on("click", this.closeClicked, this);
38312         this.closeBtn.hide();
38313     */
38314         this.createBody(this.config);
38315         if(this.config.hideWhenEmpty){
38316             this.hide();
38317             this.on("paneladded", this.validateVisibility, this);
38318             this.on("panelremoved", this.validateVisibility, this);
38319         }
38320         if(this.autoScroll){
38321             this.bodyEl.setStyle("overflow", "auto");
38322         }else{
38323             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38324         }
38325         //if(c.titlebar !== false){
38326             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38327                 this.titleEl.hide();
38328             }else{
38329                 this.titleEl.show();
38330                 if(this.config.title){
38331                     this.titleTextEl.innerHTML = this.config.title;
38332                 }
38333             }
38334         //}
38335         if(this.config.collapsed){
38336             this.collapse(true);
38337         }
38338         if(this.config.hidden){
38339             this.hide();
38340         }
38341         
38342         if (this.unrendered_panels && this.unrendered_panels.length) {
38343             for (var i =0;i< this.unrendered_panels.length; i++) {
38344                 this.add(this.unrendered_panels[i]);
38345             }
38346             this.unrendered_panels = null;
38347             
38348         }
38349         
38350     },
38351     
38352     applyConfig : function(c)
38353     {
38354         /*
38355          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38356             var dh = Roo.DomHelper;
38357             if(c.titlebar !== false){
38358                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38359                 this.collapseBtn.on("click", this.collapse, this);
38360                 this.collapseBtn.enableDisplayMode();
38361                 /*
38362                 if(c.showPin === true || this.showPin){
38363                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38364                     this.stickBtn.enableDisplayMode();
38365                     this.stickBtn.on("click", this.expand, this);
38366                     this.stickBtn.hide();
38367                 }
38368                 
38369             }
38370             */
38371             /** This region's collapsed element
38372             * @type Roo.Element */
38373             /*
38374              *
38375             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38376                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38377             ]}, true);
38378             
38379             if(c.floatable !== false){
38380                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38381                this.collapsedEl.on("click", this.collapseClick, this);
38382             }
38383
38384             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38385                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38386                    id: "message", unselectable: "on", style:{"float":"left"}});
38387                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38388              }
38389             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38390             this.expandBtn.on("click", this.expand, this);
38391             
38392         }
38393         
38394         if(this.collapseBtn){
38395             this.collapseBtn.setVisible(c.collapsible == true);
38396         }
38397         
38398         this.cmargins = c.cmargins || this.cmargins ||
38399                          (this.position == "west" || this.position == "east" ?
38400                              {top: 0, left: 2, right:2, bottom: 0} :
38401                              {top: 2, left: 0, right:0, bottom: 2});
38402         */
38403         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38404         
38405         
38406         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38407         
38408         this.autoScroll = c.autoScroll || false;
38409         
38410         
38411        
38412         
38413         this.duration = c.duration || .30;
38414         this.slideDuration = c.slideDuration || .45;
38415         this.config = c;
38416        
38417     },
38418     /**
38419      * Returns true if this region is currently visible.
38420      * @return {Boolean}
38421      */
38422     isVisible : function(){
38423         return this.visible;
38424     },
38425
38426     /**
38427      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38428      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38429      */
38430     //setCollapsedTitle : function(title){
38431     //    title = title || "&#160;";
38432      //   if(this.collapsedTitleTextEl){
38433       //      this.collapsedTitleTextEl.innerHTML = title;
38434        // }
38435     //},
38436
38437     getBox : function(){
38438         var b;
38439       //  if(!this.collapsed){
38440             b = this.el.getBox(false, true);
38441        // }else{
38442           //  b = this.collapsedEl.getBox(false, true);
38443         //}
38444         return b;
38445     },
38446
38447     getMargins : function(){
38448         return this.margins;
38449         //return this.collapsed ? this.cmargins : this.margins;
38450     },
38451 /*
38452     highlight : function(){
38453         this.el.addClass("x-layout-panel-dragover");
38454     },
38455
38456     unhighlight : function(){
38457         this.el.removeClass("x-layout-panel-dragover");
38458     },
38459 */
38460     updateBox : function(box)
38461     {
38462         if (!this.bodyEl) {
38463             return; // not rendered yet..
38464         }
38465         
38466         this.box = box;
38467         if(!this.collapsed){
38468             this.el.dom.style.left = box.x + "px";
38469             this.el.dom.style.top = box.y + "px";
38470             this.updateBody(box.width, box.height);
38471         }else{
38472             this.collapsedEl.dom.style.left = box.x + "px";
38473             this.collapsedEl.dom.style.top = box.y + "px";
38474             this.collapsedEl.setSize(box.width, box.height);
38475         }
38476         if(this.tabs){
38477             this.tabs.autoSizeTabs();
38478         }
38479     },
38480
38481     updateBody : function(w, h)
38482     {
38483         if(w !== null){
38484             this.el.setWidth(w);
38485             w -= this.el.getBorderWidth("rl");
38486             if(this.config.adjustments){
38487                 w += this.config.adjustments[0];
38488             }
38489         }
38490         if(h !== null && h > 0){
38491             this.el.setHeight(h);
38492             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38493             h -= this.el.getBorderWidth("tb");
38494             if(this.config.adjustments){
38495                 h += this.config.adjustments[1];
38496             }
38497             this.bodyEl.setHeight(h);
38498             if(this.tabs){
38499                 h = this.tabs.syncHeight(h);
38500             }
38501         }
38502         if(this.panelSize){
38503             w = w !== null ? w : this.panelSize.width;
38504             h = h !== null ? h : this.panelSize.height;
38505         }
38506         if(this.activePanel){
38507             var el = this.activePanel.getEl();
38508             w = w !== null ? w : el.getWidth();
38509             h = h !== null ? h : el.getHeight();
38510             this.panelSize = {width: w, height: h};
38511             this.activePanel.setSize(w, h);
38512         }
38513         if(Roo.isIE && this.tabs){
38514             this.tabs.el.repaint();
38515         }
38516     },
38517
38518     /**
38519      * Returns the container element for this region.
38520      * @return {Roo.Element}
38521      */
38522     getEl : function(){
38523         return this.el;
38524     },
38525
38526     /**
38527      * Hides this region.
38528      */
38529     hide : function(){
38530         //if(!this.collapsed){
38531             this.el.dom.style.left = "-2000px";
38532             this.el.hide();
38533         //}else{
38534          //   this.collapsedEl.dom.style.left = "-2000px";
38535          //   this.collapsedEl.hide();
38536        // }
38537         this.visible = false;
38538         this.fireEvent("visibilitychange", this, false);
38539     },
38540
38541     /**
38542      * Shows this region if it was previously hidden.
38543      */
38544     show : function(){
38545         //if(!this.collapsed){
38546             this.el.show();
38547         //}else{
38548         //    this.collapsedEl.show();
38549        // }
38550         this.visible = true;
38551         this.fireEvent("visibilitychange", this, true);
38552     },
38553 /*
38554     closeClicked : function(){
38555         if(this.activePanel){
38556             this.remove(this.activePanel);
38557         }
38558     },
38559
38560     collapseClick : function(e){
38561         if(this.isSlid){
38562            e.stopPropagation();
38563            this.slideIn();
38564         }else{
38565            e.stopPropagation();
38566            this.slideOut();
38567         }
38568     },
38569 */
38570     /**
38571      * Collapses this region.
38572      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38573      */
38574     /*
38575     collapse : function(skipAnim, skipCheck = false){
38576         if(this.collapsed) {
38577             return;
38578         }
38579         
38580         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38581             
38582             this.collapsed = true;
38583             if(this.split){
38584                 this.split.el.hide();
38585             }
38586             if(this.config.animate && skipAnim !== true){
38587                 this.fireEvent("invalidated", this);
38588                 this.animateCollapse();
38589             }else{
38590                 this.el.setLocation(-20000,-20000);
38591                 this.el.hide();
38592                 this.collapsedEl.show();
38593                 this.fireEvent("collapsed", this);
38594                 this.fireEvent("invalidated", this);
38595             }
38596         }
38597         
38598     },
38599 */
38600     animateCollapse : function(){
38601         // overridden
38602     },
38603
38604     /**
38605      * Expands this region if it was previously collapsed.
38606      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38607      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38608      */
38609     /*
38610     expand : function(e, skipAnim){
38611         if(e) {
38612             e.stopPropagation();
38613         }
38614         if(!this.collapsed || this.el.hasActiveFx()) {
38615             return;
38616         }
38617         if(this.isSlid){
38618             this.afterSlideIn();
38619             skipAnim = true;
38620         }
38621         this.collapsed = false;
38622         if(this.config.animate && skipAnim !== true){
38623             this.animateExpand();
38624         }else{
38625             this.el.show();
38626             if(this.split){
38627                 this.split.el.show();
38628             }
38629             this.collapsedEl.setLocation(-2000,-2000);
38630             this.collapsedEl.hide();
38631             this.fireEvent("invalidated", this);
38632             this.fireEvent("expanded", this);
38633         }
38634     },
38635 */
38636     animateExpand : function(){
38637         // overridden
38638     },
38639
38640     initTabs : function()
38641     {
38642         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38643         
38644         var ts = new Roo.bootstrap.panel.Tabs({
38645             el: this.bodyEl.dom,
38646             region : this,
38647             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38648             disableTooltips: this.config.disableTabTips,
38649             toolbar : this.config.toolbar
38650         });
38651         
38652         if(this.config.hideTabs){
38653             ts.stripWrap.setDisplayed(false);
38654         }
38655         this.tabs = ts;
38656         ts.resizeTabs = this.config.resizeTabs === true;
38657         ts.minTabWidth = this.config.minTabWidth || 40;
38658         ts.maxTabWidth = this.config.maxTabWidth || 250;
38659         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38660         ts.monitorResize = false;
38661         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38662         ts.bodyEl.addClass('roo-layout-tabs-body');
38663         this.panels.each(this.initPanelAsTab, this);
38664     },
38665
38666     initPanelAsTab : function(panel){
38667         var ti = this.tabs.addTab(
38668             panel.getEl().id,
38669             panel.getTitle(),
38670             null,
38671             this.config.closeOnTab && panel.isClosable(),
38672             panel.tpl
38673         );
38674         if(panel.tabTip !== undefined){
38675             ti.setTooltip(panel.tabTip);
38676         }
38677         ti.on("activate", function(){
38678               this.setActivePanel(panel);
38679         }, this);
38680         
38681         if(this.config.closeOnTab){
38682             ti.on("beforeclose", function(t, e){
38683                 e.cancel = true;
38684                 this.remove(panel);
38685             }, this);
38686         }
38687         
38688         panel.tabItem = ti;
38689         
38690         return ti;
38691     },
38692
38693     updatePanelTitle : function(panel, title)
38694     {
38695         if(this.activePanel == panel){
38696             this.updateTitle(title);
38697         }
38698         if(this.tabs){
38699             var ti = this.tabs.getTab(panel.getEl().id);
38700             ti.setText(title);
38701             if(panel.tabTip !== undefined){
38702                 ti.setTooltip(panel.tabTip);
38703             }
38704         }
38705     },
38706
38707     updateTitle : function(title){
38708         if(this.titleTextEl && !this.config.title){
38709             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38710         }
38711     },
38712
38713     setActivePanel : function(panel)
38714     {
38715         panel = this.getPanel(panel);
38716         if(this.activePanel && this.activePanel != panel){
38717             if(this.activePanel.setActiveState(false) === false){
38718                 return;
38719             }
38720         }
38721         this.activePanel = panel;
38722         panel.setActiveState(true);
38723         if(this.panelSize){
38724             panel.setSize(this.panelSize.width, this.panelSize.height);
38725         }
38726         if(this.closeBtn){
38727             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38728         }
38729         this.updateTitle(panel.getTitle());
38730         if(this.tabs){
38731             this.fireEvent("invalidated", this);
38732         }
38733         this.fireEvent("panelactivated", this, panel);
38734     },
38735
38736     /**
38737      * Shows the specified panel.
38738      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38739      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38740      */
38741     showPanel : function(panel)
38742     {
38743         panel = this.getPanel(panel);
38744         if(panel){
38745             if(this.tabs){
38746                 var tab = this.tabs.getTab(panel.getEl().id);
38747                 if(tab.isHidden()){
38748                     this.tabs.unhideTab(tab.id);
38749                 }
38750                 tab.activate();
38751             }else{
38752                 this.setActivePanel(panel);
38753             }
38754         }
38755         return panel;
38756     },
38757
38758     /**
38759      * Get the active panel for this region.
38760      * @return {Roo.ContentPanel} The active panel or null
38761      */
38762     getActivePanel : function(){
38763         return this.activePanel;
38764     },
38765
38766     validateVisibility : function(){
38767         if(this.panels.getCount() < 1){
38768             this.updateTitle("&#160;");
38769             this.closeBtn.hide();
38770             this.hide();
38771         }else{
38772             if(!this.isVisible()){
38773                 this.show();
38774             }
38775         }
38776     },
38777
38778     /**
38779      * Adds the passed ContentPanel(s) to this region.
38780      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38781      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38782      */
38783     add : function(panel)
38784     {
38785         if(arguments.length > 1){
38786             for(var i = 0, len = arguments.length; i < len; i++) {
38787                 this.add(arguments[i]);
38788             }
38789             return null;
38790         }
38791         
38792         // if we have not been rendered yet, then we can not really do much of this..
38793         if (!this.bodyEl) {
38794             this.unrendered_panels.push(panel);
38795             return panel;
38796         }
38797         
38798         
38799         
38800         
38801         if(this.hasPanel(panel)){
38802             this.showPanel(panel);
38803             return panel;
38804         }
38805         panel.setRegion(this);
38806         this.panels.add(panel);
38807        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38808             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38809             // and hide them... ???
38810             this.bodyEl.dom.appendChild(panel.getEl().dom);
38811             if(panel.background !== true){
38812                 this.setActivePanel(panel);
38813             }
38814             this.fireEvent("paneladded", this, panel);
38815             return panel;
38816         }
38817         */
38818         if(!this.tabs){
38819             this.initTabs();
38820         }else{
38821             this.initPanelAsTab(panel);
38822         }
38823         
38824         
38825         if(panel.background !== true){
38826             this.tabs.activate(panel.getEl().id);
38827         }
38828         this.fireEvent("paneladded", this, panel);
38829         return panel;
38830     },
38831
38832     /**
38833      * Hides the tab for the specified panel.
38834      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38835      */
38836     hidePanel : function(panel){
38837         if(this.tabs && (panel = this.getPanel(panel))){
38838             this.tabs.hideTab(panel.getEl().id);
38839         }
38840     },
38841
38842     /**
38843      * Unhides the tab for a previously hidden panel.
38844      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38845      */
38846     unhidePanel : function(panel){
38847         if(this.tabs && (panel = this.getPanel(panel))){
38848             this.tabs.unhideTab(panel.getEl().id);
38849         }
38850     },
38851
38852     clearPanels : function(){
38853         while(this.panels.getCount() > 0){
38854              this.remove(this.panels.first());
38855         }
38856     },
38857
38858     /**
38859      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38860      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38861      * @param {Boolean} preservePanel Overrides the config preservePanel option
38862      * @return {Roo.ContentPanel} The panel that was removed
38863      */
38864     remove : function(panel, preservePanel)
38865     {
38866         panel = this.getPanel(panel);
38867         if(!panel){
38868             return null;
38869         }
38870         var e = {};
38871         this.fireEvent("beforeremove", this, panel, e);
38872         if(e.cancel === true){
38873             return null;
38874         }
38875         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38876         var panelId = panel.getId();
38877         this.panels.removeKey(panelId);
38878         if(preservePanel){
38879             document.body.appendChild(panel.getEl().dom);
38880         }
38881         if(this.tabs){
38882             this.tabs.removeTab(panel.getEl().id);
38883         }else if (!preservePanel){
38884             this.bodyEl.dom.removeChild(panel.getEl().dom);
38885         }
38886         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38887             var p = this.panels.first();
38888             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38889             tempEl.appendChild(p.getEl().dom);
38890             this.bodyEl.update("");
38891             this.bodyEl.dom.appendChild(p.getEl().dom);
38892             tempEl = null;
38893             this.updateTitle(p.getTitle());
38894             this.tabs = null;
38895             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38896             this.setActivePanel(p);
38897         }
38898         panel.setRegion(null);
38899         if(this.activePanel == panel){
38900             this.activePanel = null;
38901         }
38902         if(this.config.autoDestroy !== false && preservePanel !== true){
38903             try{panel.destroy();}catch(e){}
38904         }
38905         this.fireEvent("panelremoved", this, panel);
38906         return panel;
38907     },
38908
38909     /**
38910      * Returns the TabPanel component used by this region
38911      * @return {Roo.TabPanel}
38912      */
38913     getTabs : function(){
38914         return this.tabs;
38915     },
38916
38917     createTool : function(parentEl, className){
38918         var btn = Roo.DomHelper.append(parentEl, {
38919             tag: "div",
38920             cls: "x-layout-tools-button",
38921             children: [ {
38922                 tag: "div",
38923                 cls: "roo-layout-tools-button-inner " + className,
38924                 html: "&#160;"
38925             }]
38926         }, true);
38927         btn.addClassOnOver("roo-layout-tools-button-over");
38928         return btn;
38929     }
38930 });/*
38931  * Based on:
38932  * Ext JS Library 1.1.1
38933  * Copyright(c) 2006-2007, Ext JS, LLC.
38934  *
38935  * Originally Released Under LGPL - original licence link has changed is not relivant.
38936  *
38937  * Fork - LGPL
38938  * <script type="text/javascript">
38939  */
38940  
38941
38942
38943 /**
38944  * @class Roo.SplitLayoutRegion
38945  * @extends Roo.LayoutRegion
38946  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38947  */
38948 Roo.bootstrap.layout.Split = function(config){
38949     this.cursor = config.cursor;
38950     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38951 };
38952
38953 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38954 {
38955     splitTip : "Drag to resize.",
38956     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38957     useSplitTips : false,
38958
38959     applyConfig : function(config){
38960         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38961     },
38962     
38963     onRender : function(ctr,pos) {
38964         
38965         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38966         if(!this.config.split){
38967             return;
38968         }
38969         if(!this.split){
38970             
38971             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38972                             tag: "div",
38973                             id: this.el.id + "-split",
38974                             cls: "roo-layout-split roo-layout-split-"+this.position,
38975                             html: "&#160;"
38976             });
38977             /** The SplitBar for this region 
38978             * @type Roo.SplitBar */
38979             // does not exist yet...
38980             Roo.log([this.position, this.orientation]);
38981             
38982             this.split = new Roo.bootstrap.SplitBar({
38983                 dragElement : splitEl,
38984                 resizingElement: this.el,
38985                 orientation : this.orientation
38986             });
38987             
38988             this.split.on("moved", this.onSplitMove, this);
38989             this.split.useShim = this.config.useShim === true;
38990             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38991             if(this.useSplitTips){
38992                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38993             }
38994             //if(config.collapsible){
38995             //    this.split.el.on("dblclick", this.collapse,  this);
38996             //}
38997         }
38998         if(typeof this.config.minSize != "undefined"){
38999             this.split.minSize = this.config.minSize;
39000         }
39001         if(typeof this.config.maxSize != "undefined"){
39002             this.split.maxSize = this.config.maxSize;
39003         }
39004         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39005             this.hideSplitter();
39006         }
39007         
39008     },
39009
39010     getHMaxSize : function(){
39011          var cmax = this.config.maxSize || 10000;
39012          var center = this.mgr.getRegion("center");
39013          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39014     },
39015
39016     getVMaxSize : function(){
39017          var cmax = this.config.maxSize || 10000;
39018          var center = this.mgr.getRegion("center");
39019          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39020     },
39021
39022     onSplitMove : function(split, newSize){
39023         this.fireEvent("resized", this, newSize);
39024     },
39025     
39026     /** 
39027      * Returns the {@link Roo.SplitBar} for this region.
39028      * @return {Roo.SplitBar}
39029      */
39030     getSplitBar : function(){
39031         return this.split;
39032     },
39033     
39034     hide : function(){
39035         this.hideSplitter();
39036         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39037     },
39038
39039     hideSplitter : function(){
39040         if(this.split){
39041             this.split.el.setLocation(-2000,-2000);
39042             this.split.el.hide();
39043         }
39044     },
39045
39046     show : function(){
39047         if(this.split){
39048             this.split.el.show();
39049         }
39050         Roo.bootstrap.layout.Split.superclass.show.call(this);
39051     },
39052     
39053     beforeSlide: function(){
39054         if(Roo.isGecko){// firefox overflow auto bug workaround
39055             this.bodyEl.clip();
39056             if(this.tabs) {
39057                 this.tabs.bodyEl.clip();
39058             }
39059             if(this.activePanel){
39060                 this.activePanel.getEl().clip();
39061                 
39062                 if(this.activePanel.beforeSlide){
39063                     this.activePanel.beforeSlide();
39064                 }
39065             }
39066         }
39067     },
39068     
39069     afterSlide : function(){
39070         if(Roo.isGecko){// firefox overflow auto bug workaround
39071             this.bodyEl.unclip();
39072             if(this.tabs) {
39073                 this.tabs.bodyEl.unclip();
39074             }
39075             if(this.activePanel){
39076                 this.activePanel.getEl().unclip();
39077                 if(this.activePanel.afterSlide){
39078                     this.activePanel.afterSlide();
39079                 }
39080             }
39081         }
39082     },
39083
39084     initAutoHide : function(){
39085         if(this.autoHide !== false){
39086             if(!this.autoHideHd){
39087                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39088                 this.autoHideHd = {
39089                     "mouseout": function(e){
39090                         if(!e.within(this.el, true)){
39091                             st.delay(500);
39092                         }
39093                     },
39094                     "mouseover" : function(e){
39095                         st.cancel();
39096                     },
39097                     scope : this
39098                 };
39099             }
39100             this.el.on(this.autoHideHd);
39101         }
39102     },
39103
39104     clearAutoHide : function(){
39105         if(this.autoHide !== false){
39106             this.el.un("mouseout", this.autoHideHd.mouseout);
39107             this.el.un("mouseover", this.autoHideHd.mouseover);
39108         }
39109     },
39110
39111     clearMonitor : function(){
39112         Roo.get(document).un("click", this.slideInIf, this);
39113     },
39114
39115     // these names are backwards but not changed for compat
39116     slideOut : function(){
39117         if(this.isSlid || this.el.hasActiveFx()){
39118             return;
39119         }
39120         this.isSlid = true;
39121         if(this.collapseBtn){
39122             this.collapseBtn.hide();
39123         }
39124         this.closeBtnState = this.closeBtn.getStyle('display');
39125         this.closeBtn.hide();
39126         if(this.stickBtn){
39127             this.stickBtn.show();
39128         }
39129         this.el.show();
39130         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39131         this.beforeSlide();
39132         this.el.setStyle("z-index", 10001);
39133         this.el.slideIn(this.getSlideAnchor(), {
39134             callback: function(){
39135                 this.afterSlide();
39136                 this.initAutoHide();
39137                 Roo.get(document).on("click", this.slideInIf, this);
39138                 this.fireEvent("slideshow", this);
39139             },
39140             scope: this,
39141             block: true
39142         });
39143     },
39144
39145     afterSlideIn : function(){
39146         this.clearAutoHide();
39147         this.isSlid = false;
39148         this.clearMonitor();
39149         this.el.setStyle("z-index", "");
39150         if(this.collapseBtn){
39151             this.collapseBtn.show();
39152         }
39153         this.closeBtn.setStyle('display', this.closeBtnState);
39154         if(this.stickBtn){
39155             this.stickBtn.hide();
39156         }
39157         this.fireEvent("slidehide", this);
39158     },
39159
39160     slideIn : function(cb){
39161         if(!this.isSlid || this.el.hasActiveFx()){
39162             Roo.callback(cb);
39163             return;
39164         }
39165         this.isSlid = false;
39166         this.beforeSlide();
39167         this.el.slideOut(this.getSlideAnchor(), {
39168             callback: function(){
39169                 this.el.setLeftTop(-10000, -10000);
39170                 this.afterSlide();
39171                 this.afterSlideIn();
39172                 Roo.callback(cb);
39173             },
39174             scope: this,
39175             block: true
39176         });
39177     },
39178     
39179     slideInIf : function(e){
39180         if(!e.within(this.el)){
39181             this.slideIn();
39182         }
39183     },
39184
39185     animateCollapse : function(){
39186         this.beforeSlide();
39187         this.el.setStyle("z-index", 20000);
39188         var anchor = this.getSlideAnchor();
39189         this.el.slideOut(anchor, {
39190             callback : function(){
39191                 this.el.setStyle("z-index", "");
39192                 this.collapsedEl.slideIn(anchor, {duration:.3});
39193                 this.afterSlide();
39194                 this.el.setLocation(-10000,-10000);
39195                 this.el.hide();
39196                 this.fireEvent("collapsed", this);
39197             },
39198             scope: this,
39199             block: true
39200         });
39201     },
39202
39203     animateExpand : function(){
39204         this.beforeSlide();
39205         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39206         this.el.setStyle("z-index", 20000);
39207         this.collapsedEl.hide({
39208             duration:.1
39209         });
39210         this.el.slideIn(this.getSlideAnchor(), {
39211             callback : function(){
39212                 this.el.setStyle("z-index", "");
39213                 this.afterSlide();
39214                 if(this.split){
39215                     this.split.el.show();
39216                 }
39217                 this.fireEvent("invalidated", this);
39218                 this.fireEvent("expanded", this);
39219             },
39220             scope: this,
39221             block: true
39222         });
39223     },
39224
39225     anchors : {
39226         "west" : "left",
39227         "east" : "right",
39228         "north" : "top",
39229         "south" : "bottom"
39230     },
39231
39232     sanchors : {
39233         "west" : "l",
39234         "east" : "r",
39235         "north" : "t",
39236         "south" : "b"
39237     },
39238
39239     canchors : {
39240         "west" : "tl-tr",
39241         "east" : "tr-tl",
39242         "north" : "tl-bl",
39243         "south" : "bl-tl"
39244     },
39245
39246     getAnchor : function(){
39247         return this.anchors[this.position];
39248     },
39249
39250     getCollapseAnchor : function(){
39251         return this.canchors[this.position];
39252     },
39253
39254     getSlideAnchor : function(){
39255         return this.sanchors[this.position];
39256     },
39257
39258     getAlignAdj : function(){
39259         var cm = this.cmargins;
39260         switch(this.position){
39261             case "west":
39262                 return [0, 0];
39263             break;
39264             case "east":
39265                 return [0, 0];
39266             break;
39267             case "north":
39268                 return [0, 0];
39269             break;
39270             case "south":
39271                 return [0, 0];
39272             break;
39273         }
39274     },
39275
39276     getExpandAdj : function(){
39277         var c = this.collapsedEl, cm = this.cmargins;
39278         switch(this.position){
39279             case "west":
39280                 return [-(cm.right+c.getWidth()+cm.left), 0];
39281             break;
39282             case "east":
39283                 return [cm.right+c.getWidth()+cm.left, 0];
39284             break;
39285             case "north":
39286                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39287             break;
39288             case "south":
39289                 return [0, cm.top+cm.bottom+c.getHeight()];
39290             break;
39291         }
39292     }
39293 });/*
39294  * Based on:
39295  * Ext JS Library 1.1.1
39296  * Copyright(c) 2006-2007, Ext JS, LLC.
39297  *
39298  * Originally Released Under LGPL - original licence link has changed is not relivant.
39299  *
39300  * Fork - LGPL
39301  * <script type="text/javascript">
39302  */
39303 /*
39304  * These classes are private internal classes
39305  */
39306 Roo.bootstrap.layout.Center = function(config){
39307     config.region = "center";
39308     Roo.bootstrap.layout.Region.call(this, config);
39309     this.visible = true;
39310     this.minWidth = config.minWidth || 20;
39311     this.minHeight = config.minHeight || 20;
39312 };
39313
39314 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39315     hide : function(){
39316         // center panel can't be hidden
39317     },
39318     
39319     show : function(){
39320         // center panel can't be hidden
39321     },
39322     
39323     getMinWidth: function(){
39324         return this.minWidth;
39325     },
39326     
39327     getMinHeight: function(){
39328         return this.minHeight;
39329     }
39330 });
39331
39332
39333
39334
39335  
39336
39337
39338
39339
39340
39341
39342 Roo.bootstrap.layout.North = function(config)
39343 {
39344     config.region = 'north';
39345     config.cursor = 'n-resize';
39346     
39347     Roo.bootstrap.layout.Split.call(this, config);
39348     
39349     
39350     if(this.split){
39351         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39352         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39353         this.split.el.addClass("roo-layout-split-v");
39354     }
39355     //var size = config.initialSize || config.height;
39356     //if(this.el && typeof size != "undefined"){
39357     //    this.el.setHeight(size);
39358     //}
39359 };
39360 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39361 {
39362     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39363      
39364      
39365     onRender : function(ctr, pos)
39366     {
39367         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39368         var size = this.config.initialSize || this.config.height;
39369         if(this.el && typeof size != "undefined"){
39370             this.el.setHeight(size);
39371         }
39372     
39373     },
39374     
39375     getBox : function(){
39376         if(this.collapsed){
39377             return this.collapsedEl.getBox();
39378         }
39379         var box = this.el.getBox();
39380         if(this.split){
39381             box.height += this.split.el.getHeight();
39382         }
39383         return box;
39384     },
39385     
39386     updateBox : function(box){
39387         if(this.split && !this.collapsed){
39388             box.height -= this.split.el.getHeight();
39389             this.split.el.setLeft(box.x);
39390             this.split.el.setTop(box.y+box.height);
39391             this.split.el.setWidth(box.width);
39392         }
39393         if(this.collapsed){
39394             this.updateBody(box.width, null);
39395         }
39396         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39397     }
39398 });
39399
39400
39401
39402
39403
39404 Roo.bootstrap.layout.South = function(config){
39405     config.region = 'south';
39406     config.cursor = 's-resize';
39407     Roo.bootstrap.layout.Split.call(this, config);
39408     if(this.split){
39409         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39410         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39411         this.split.el.addClass("roo-layout-split-v");
39412     }
39413     
39414 };
39415
39416 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39417     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39418     
39419     onRender : function(ctr, pos)
39420     {
39421         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39422         var size = this.config.initialSize || this.config.height;
39423         if(this.el && typeof size != "undefined"){
39424             this.el.setHeight(size);
39425         }
39426     
39427     },
39428     
39429     getBox : function(){
39430         if(this.collapsed){
39431             return this.collapsedEl.getBox();
39432         }
39433         var box = this.el.getBox();
39434         if(this.split){
39435             var sh = this.split.el.getHeight();
39436             box.height += sh;
39437             box.y -= sh;
39438         }
39439         return box;
39440     },
39441     
39442     updateBox : function(box){
39443         if(this.split && !this.collapsed){
39444             var sh = this.split.el.getHeight();
39445             box.height -= sh;
39446             box.y += sh;
39447             this.split.el.setLeft(box.x);
39448             this.split.el.setTop(box.y-sh);
39449             this.split.el.setWidth(box.width);
39450         }
39451         if(this.collapsed){
39452             this.updateBody(box.width, null);
39453         }
39454         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39455     }
39456 });
39457
39458 Roo.bootstrap.layout.East = function(config){
39459     config.region = "east";
39460     config.cursor = "e-resize";
39461     Roo.bootstrap.layout.Split.call(this, config);
39462     if(this.split){
39463         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39464         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39465         this.split.el.addClass("roo-layout-split-h");
39466     }
39467     
39468 };
39469 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39470     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39471     
39472     onRender : function(ctr, pos)
39473     {
39474         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39475         var size = this.config.initialSize || this.config.width;
39476         if(this.el && typeof size != "undefined"){
39477             this.el.setWidth(size);
39478         }
39479     
39480     },
39481     
39482     getBox : function(){
39483         if(this.collapsed){
39484             return this.collapsedEl.getBox();
39485         }
39486         var box = this.el.getBox();
39487         if(this.split){
39488             var sw = this.split.el.getWidth();
39489             box.width += sw;
39490             box.x -= sw;
39491         }
39492         return box;
39493     },
39494
39495     updateBox : function(box){
39496         if(this.split && !this.collapsed){
39497             var sw = this.split.el.getWidth();
39498             box.width -= sw;
39499             this.split.el.setLeft(box.x);
39500             this.split.el.setTop(box.y);
39501             this.split.el.setHeight(box.height);
39502             box.x += sw;
39503         }
39504         if(this.collapsed){
39505             this.updateBody(null, box.height);
39506         }
39507         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39508     }
39509 });
39510
39511 Roo.bootstrap.layout.West = function(config){
39512     config.region = "west";
39513     config.cursor = "w-resize";
39514     
39515     Roo.bootstrap.layout.Split.call(this, config);
39516     if(this.split){
39517         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39518         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39519         this.split.el.addClass("roo-layout-split-h");
39520     }
39521     
39522 };
39523 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39524     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39525     
39526     onRender: function(ctr, pos)
39527     {
39528         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39529         var size = this.config.initialSize || this.config.width;
39530         if(typeof size != "undefined"){
39531             this.el.setWidth(size);
39532         }
39533     },
39534     
39535     getBox : function(){
39536         if(this.collapsed){
39537             return this.collapsedEl.getBox();
39538         }
39539         var box = this.el.getBox();
39540         if (box.width == 0) {
39541             box.width = this.config.width; // kludge?
39542         }
39543         if(this.split){
39544             box.width += this.split.el.getWidth();
39545         }
39546         return box;
39547     },
39548     
39549     updateBox : function(box){
39550         if(this.split && !this.collapsed){
39551             var sw = this.split.el.getWidth();
39552             box.width -= sw;
39553             this.split.el.setLeft(box.x+box.width);
39554             this.split.el.setTop(box.y);
39555             this.split.el.setHeight(box.height);
39556         }
39557         if(this.collapsed){
39558             this.updateBody(null, box.height);
39559         }
39560         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39561     }
39562 });Roo.namespace("Roo.bootstrap.panel");/*
39563  * Based on:
39564  * Ext JS Library 1.1.1
39565  * Copyright(c) 2006-2007, Ext JS, LLC.
39566  *
39567  * Originally Released Under LGPL - original licence link has changed is not relivant.
39568  *
39569  * Fork - LGPL
39570  * <script type="text/javascript">
39571  */
39572 /**
39573  * @class Roo.ContentPanel
39574  * @extends Roo.util.Observable
39575  * A basic ContentPanel element.
39576  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39577  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39578  * @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
39579  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39580  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39581  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39582  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39583  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39584  * @cfg {String} title          The title for this panel
39585  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39586  * @cfg {String} url            Calls {@link #setUrl} with this value
39587  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39588  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39589  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39590  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39591  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39592  * @cfg {Boolean} badges render the badges
39593  * @cfg {String} cls  extra classes to use  
39594  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39595
39596  * @constructor
39597  * Create a new ContentPanel.
39598  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39599  * @param {String/Object} config A string to set only the title or a config object
39600  * @param {String} content (optional) Set the HTML content for this panel
39601  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39602  */
39603 Roo.bootstrap.panel.Content = function( config){
39604     
39605     this.tpl = config.tpl || false;
39606     
39607     var el = config.el;
39608     var content = config.content;
39609
39610     if(config.autoCreate){ // xtype is available if this is called from factory
39611         el = Roo.id();
39612     }
39613     this.el = Roo.get(el);
39614     if(!this.el && config && config.autoCreate){
39615         if(typeof config.autoCreate == "object"){
39616             if(!config.autoCreate.id){
39617                 config.autoCreate.id = config.id||el;
39618             }
39619             this.el = Roo.DomHelper.append(document.body,
39620                         config.autoCreate, true);
39621         }else{
39622             var elcfg =  {
39623                 tag: "div",
39624                 cls: (config.cls || '') +
39625                     (config.background ? ' bg-' + config.background : '') +
39626                     " roo-layout-inactive-content",
39627                 id: config.id||el
39628             };
39629             if (config.iframe) {
39630                 elcfg.cn = [
39631                     {
39632                         tag : 'iframe',
39633                         style : 'border: 0px',
39634                         src : 'about:blank'
39635                     }
39636                 ];
39637             }
39638               
39639             if (config.html) {
39640                 elcfg.html = config.html;
39641                 
39642             }
39643                         
39644             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39645             if (config.iframe) {
39646                 this.iframeEl = this.el.select('iframe',true).first();
39647             }
39648             
39649         }
39650     } 
39651     this.closable = false;
39652     this.loaded = false;
39653     this.active = false;
39654    
39655       
39656     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39657         
39658         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39659         
39660         this.wrapEl = this.el; //this.el.wrap();
39661         var ti = [];
39662         if (config.toolbar.items) {
39663             ti = config.toolbar.items ;
39664             delete config.toolbar.items ;
39665         }
39666         
39667         var nitems = [];
39668         this.toolbar.render(this.wrapEl, 'before');
39669         for(var i =0;i < ti.length;i++) {
39670           //  Roo.log(['add child', items[i]]);
39671             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39672         }
39673         this.toolbar.items = nitems;
39674         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39675         delete config.toolbar;
39676         
39677     }
39678     /*
39679     // xtype created footer. - not sure if will work as we normally have to render first..
39680     if (this.footer && !this.footer.el && this.footer.xtype) {
39681         if (!this.wrapEl) {
39682             this.wrapEl = this.el.wrap();
39683         }
39684     
39685         this.footer.container = this.wrapEl.createChild();
39686          
39687         this.footer = Roo.factory(this.footer, Roo);
39688         
39689     }
39690     */
39691     
39692      if(typeof config == "string"){
39693         this.title = config;
39694     }else{
39695         Roo.apply(this, config);
39696     }
39697     
39698     if(this.resizeEl){
39699         this.resizeEl = Roo.get(this.resizeEl, true);
39700     }else{
39701         this.resizeEl = this.el;
39702     }
39703     // handle view.xtype
39704     
39705  
39706     
39707     
39708     this.addEvents({
39709         /**
39710          * @event activate
39711          * Fires when this panel is activated. 
39712          * @param {Roo.ContentPanel} this
39713          */
39714         "activate" : true,
39715         /**
39716          * @event deactivate
39717          * Fires when this panel is activated. 
39718          * @param {Roo.ContentPanel} this
39719          */
39720         "deactivate" : true,
39721
39722         /**
39723          * @event resize
39724          * Fires when this panel is resized if fitToFrame is true.
39725          * @param {Roo.ContentPanel} this
39726          * @param {Number} width The width after any component adjustments
39727          * @param {Number} height The height after any component adjustments
39728          */
39729         "resize" : true,
39730         
39731          /**
39732          * @event render
39733          * Fires when this tab is created
39734          * @param {Roo.ContentPanel} this
39735          */
39736         "render" : true
39737         
39738         
39739         
39740     });
39741     
39742
39743     
39744     
39745     if(this.autoScroll && !this.iframe){
39746         this.resizeEl.setStyle("overflow", "auto");
39747     } else {
39748         // fix randome scrolling
39749         //this.el.on('scroll', function() {
39750         //    Roo.log('fix random scolling');
39751         //    this.scrollTo('top',0); 
39752         //});
39753     }
39754     content = content || this.content;
39755     if(content){
39756         this.setContent(content);
39757     }
39758     if(config && config.url){
39759         this.setUrl(this.url, this.params, this.loadOnce);
39760     }
39761     
39762     
39763     
39764     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39765     
39766     if (this.view && typeof(this.view.xtype) != 'undefined') {
39767         this.view.el = this.el.appendChild(document.createElement("div"));
39768         this.view = Roo.factory(this.view); 
39769         this.view.render  &&  this.view.render(false, '');  
39770     }
39771     
39772     
39773     this.fireEvent('render', this);
39774 };
39775
39776 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39777     
39778     cls : '',
39779     background : '',
39780     
39781     tabTip : '',
39782     
39783     iframe : false,
39784     iframeEl : false,
39785     
39786     setRegion : function(region){
39787         this.region = region;
39788         this.setActiveClass(region && !this.background);
39789     },
39790     
39791     
39792     setActiveClass: function(state)
39793     {
39794         if(state){
39795            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39796            this.el.setStyle('position','relative');
39797         }else{
39798            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39799            this.el.setStyle('position', 'absolute');
39800         } 
39801     },
39802     
39803     /**
39804      * Returns the toolbar for this Panel if one was configured. 
39805      * @return {Roo.Toolbar} 
39806      */
39807     getToolbar : function(){
39808         return this.toolbar;
39809     },
39810     
39811     setActiveState : function(active)
39812     {
39813         this.active = active;
39814         this.setActiveClass(active);
39815         if(!active){
39816             if(this.fireEvent("deactivate", this) === false){
39817                 return false;
39818             }
39819             return true;
39820         }
39821         this.fireEvent("activate", this);
39822         return true;
39823     },
39824     /**
39825      * Updates this panel's element (not for iframe)
39826      * @param {String} content The new content
39827      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39828     */
39829     setContent : function(content, loadScripts){
39830         if (this.iframe) {
39831             return;
39832         }
39833         
39834         this.el.update(content, loadScripts);
39835     },
39836
39837     ignoreResize : function(w, h){
39838         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39839             return true;
39840         }else{
39841             this.lastSize = {width: w, height: h};
39842             return false;
39843         }
39844     },
39845     /**
39846      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39847      * @return {Roo.UpdateManager} The UpdateManager
39848      */
39849     getUpdateManager : function(){
39850         if (this.iframe) {
39851             return false;
39852         }
39853         return this.el.getUpdateManager();
39854     },
39855      /**
39856      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39857      * Does not work with IFRAME contents
39858      * @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:
39859 <pre><code>
39860 panel.load({
39861     url: "your-url.php",
39862     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39863     callback: yourFunction,
39864     scope: yourObject, //(optional scope)
39865     discardUrl: false,
39866     nocache: false,
39867     text: "Loading...",
39868     timeout: 30,
39869     scripts: false
39870 });
39871 </code></pre>
39872      
39873      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39874      * 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.
39875      * @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}
39876      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39877      * @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.
39878      * @return {Roo.ContentPanel} this
39879      */
39880     load : function(){
39881         
39882         if (this.iframe) {
39883             return this;
39884         }
39885         
39886         var um = this.el.getUpdateManager();
39887         um.update.apply(um, arguments);
39888         return this;
39889     },
39890
39891
39892     /**
39893      * 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.
39894      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39895      * @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)
39896      * @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)
39897      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39898      */
39899     setUrl : function(url, params, loadOnce){
39900         if (this.iframe) {
39901             this.iframeEl.dom.src = url;
39902             return false;
39903         }
39904         
39905         if(this.refreshDelegate){
39906             this.removeListener("activate", this.refreshDelegate);
39907         }
39908         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39909         this.on("activate", this.refreshDelegate);
39910         return this.el.getUpdateManager();
39911     },
39912     
39913     _handleRefresh : function(url, params, loadOnce){
39914         if(!loadOnce || !this.loaded){
39915             var updater = this.el.getUpdateManager();
39916             updater.update(url, params, this._setLoaded.createDelegate(this));
39917         }
39918     },
39919     
39920     _setLoaded : function(){
39921         this.loaded = true;
39922     }, 
39923     
39924     /**
39925      * Returns this panel's id
39926      * @return {String} 
39927      */
39928     getId : function(){
39929         return this.el.id;
39930     },
39931     
39932     /** 
39933      * Returns this panel's element - used by regiosn to add.
39934      * @return {Roo.Element} 
39935      */
39936     getEl : function(){
39937         return this.wrapEl || this.el;
39938     },
39939     
39940    
39941     
39942     adjustForComponents : function(width, height)
39943     {
39944         //Roo.log('adjustForComponents ');
39945         if(this.resizeEl != this.el){
39946             width -= this.el.getFrameWidth('lr');
39947             height -= this.el.getFrameWidth('tb');
39948         }
39949         if(this.toolbar){
39950             var te = this.toolbar.getEl();
39951             te.setWidth(width);
39952             height -= te.getHeight();
39953         }
39954         if(this.footer){
39955             var te = this.footer.getEl();
39956             te.setWidth(width);
39957             height -= te.getHeight();
39958         }
39959         
39960         
39961         if(this.adjustments){
39962             width += this.adjustments[0];
39963             height += this.adjustments[1];
39964         }
39965         return {"width": width, "height": height};
39966     },
39967     
39968     setSize : function(width, height){
39969         if(this.fitToFrame && !this.ignoreResize(width, height)){
39970             if(this.fitContainer && this.resizeEl != this.el){
39971                 this.el.setSize(width, height);
39972             }
39973             var size = this.adjustForComponents(width, height);
39974             if (this.iframe) {
39975                 this.iframeEl.setSize(width,height);
39976             }
39977             
39978             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39979             this.fireEvent('resize', this, size.width, size.height);
39980             
39981             
39982         }
39983     },
39984     
39985     /**
39986      * Returns this panel's title
39987      * @return {String} 
39988      */
39989     getTitle : function(){
39990         
39991         if (typeof(this.title) != 'object') {
39992             return this.title;
39993         }
39994         
39995         var t = '';
39996         for (var k in this.title) {
39997             if (!this.title.hasOwnProperty(k)) {
39998                 continue;
39999             }
40000             
40001             if (k.indexOf('-') >= 0) {
40002                 var s = k.split('-');
40003                 for (var i = 0; i<s.length; i++) {
40004                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40005                 }
40006             } else {
40007                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40008             }
40009         }
40010         return t;
40011     },
40012     
40013     /**
40014      * Set this panel's title
40015      * @param {String} title
40016      */
40017     setTitle : function(title){
40018         this.title = title;
40019         if(this.region){
40020             this.region.updatePanelTitle(this, title);
40021         }
40022     },
40023     
40024     /**
40025      * Returns true is this panel was configured to be closable
40026      * @return {Boolean} 
40027      */
40028     isClosable : function(){
40029         return this.closable;
40030     },
40031     
40032     beforeSlide : function(){
40033         this.el.clip();
40034         this.resizeEl.clip();
40035     },
40036     
40037     afterSlide : function(){
40038         this.el.unclip();
40039         this.resizeEl.unclip();
40040     },
40041     
40042     /**
40043      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40044      *   Will fail silently if the {@link #setUrl} method has not been called.
40045      *   This does not activate the panel, just updates its content.
40046      */
40047     refresh : function(){
40048         if(this.refreshDelegate){
40049            this.loaded = false;
40050            this.refreshDelegate();
40051         }
40052     },
40053     
40054     /**
40055      * Destroys this panel
40056      */
40057     destroy : function(){
40058         this.el.removeAllListeners();
40059         var tempEl = document.createElement("span");
40060         tempEl.appendChild(this.el.dom);
40061         tempEl.innerHTML = "";
40062         this.el.remove();
40063         this.el = null;
40064     },
40065     
40066     /**
40067      * form - if the content panel contains a form - this is a reference to it.
40068      * @type {Roo.form.Form}
40069      */
40070     form : false,
40071     /**
40072      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40073      *    This contains a reference to it.
40074      * @type {Roo.View}
40075      */
40076     view : false,
40077     
40078       /**
40079      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40080      * <pre><code>
40081
40082 layout.addxtype({
40083        xtype : 'Form',
40084        items: [ .... ]
40085    }
40086 );
40087
40088 </code></pre>
40089      * @param {Object} cfg Xtype definition of item to add.
40090      */
40091     
40092     
40093     getChildContainer: function () {
40094         return this.getEl();
40095     }
40096     
40097     
40098     /*
40099         var  ret = new Roo.factory(cfg);
40100         return ret;
40101         
40102         
40103         // add form..
40104         if (cfg.xtype.match(/^Form$/)) {
40105             
40106             var el;
40107             //if (this.footer) {
40108             //    el = this.footer.container.insertSibling(false, 'before');
40109             //} else {
40110                 el = this.el.createChild();
40111             //}
40112
40113             this.form = new  Roo.form.Form(cfg);
40114             
40115             
40116             if ( this.form.allItems.length) {
40117                 this.form.render(el.dom);
40118             }
40119             return this.form;
40120         }
40121         // should only have one of theses..
40122         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40123             // views.. should not be just added - used named prop 'view''
40124             
40125             cfg.el = this.el.appendChild(document.createElement("div"));
40126             // factory?
40127             
40128             var ret = new Roo.factory(cfg);
40129              
40130              ret.render && ret.render(false, ''); // render blank..
40131             this.view = ret;
40132             return ret;
40133         }
40134         return false;
40135     }
40136     \*/
40137 });
40138  
40139 /**
40140  * @class Roo.bootstrap.panel.Grid
40141  * @extends Roo.bootstrap.panel.Content
40142  * @constructor
40143  * Create a new GridPanel.
40144  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40145  * @param {Object} config A the config object
40146   
40147  */
40148
40149
40150
40151 Roo.bootstrap.panel.Grid = function(config)
40152 {
40153     
40154       
40155     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40156         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40157
40158     config.el = this.wrapper;
40159     //this.el = this.wrapper;
40160     
40161       if (config.container) {
40162         // ctor'ed from a Border/panel.grid
40163         
40164         
40165         this.wrapper.setStyle("overflow", "hidden");
40166         this.wrapper.addClass('roo-grid-container');
40167
40168     }
40169     
40170     
40171     if(config.toolbar){
40172         var tool_el = this.wrapper.createChild();    
40173         this.toolbar = Roo.factory(config.toolbar);
40174         var ti = [];
40175         if (config.toolbar.items) {
40176             ti = config.toolbar.items ;
40177             delete config.toolbar.items ;
40178         }
40179         
40180         var nitems = [];
40181         this.toolbar.render(tool_el);
40182         for(var i =0;i < ti.length;i++) {
40183           //  Roo.log(['add child', items[i]]);
40184             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40185         }
40186         this.toolbar.items = nitems;
40187         
40188         delete config.toolbar;
40189     }
40190     
40191     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40192     config.grid.scrollBody = true;;
40193     config.grid.monitorWindowResize = false; // turn off autosizing
40194     config.grid.autoHeight = false;
40195     config.grid.autoWidth = false;
40196     
40197     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40198     
40199     if (config.background) {
40200         // render grid on panel activation (if panel background)
40201         this.on('activate', function(gp) {
40202             if (!gp.grid.rendered) {
40203                 gp.grid.render(this.wrapper);
40204                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40205             }
40206         });
40207             
40208     } else {
40209         this.grid.render(this.wrapper);
40210         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40211
40212     }
40213     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40214     // ??? needed ??? config.el = this.wrapper;
40215     
40216     
40217     
40218   
40219     // xtype created footer. - not sure if will work as we normally have to render first..
40220     if (this.footer && !this.footer.el && this.footer.xtype) {
40221         
40222         var ctr = this.grid.getView().getFooterPanel(true);
40223         this.footer.dataSource = this.grid.dataSource;
40224         this.footer = Roo.factory(this.footer, Roo);
40225         this.footer.render(ctr);
40226         
40227     }
40228     
40229     
40230     
40231     
40232      
40233 };
40234
40235 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40236     getId : function(){
40237         return this.grid.id;
40238     },
40239     
40240     /**
40241      * Returns the grid for this panel
40242      * @return {Roo.bootstrap.Table} 
40243      */
40244     getGrid : function(){
40245         return this.grid;    
40246     },
40247     
40248     setSize : function(width, height){
40249         if(!this.ignoreResize(width, height)){
40250             var grid = this.grid;
40251             var size = this.adjustForComponents(width, height);
40252             // tfoot is not a footer?
40253           
40254             
40255             var gridel = grid.getGridEl();
40256             gridel.setSize(size.width, size.height);
40257             
40258             var tbd = grid.getGridEl().select('tbody', true).first();
40259             var thd = grid.getGridEl().select('thead',true).first();
40260             var tbf= grid.getGridEl().select('tfoot', true).first();
40261
40262             if (tbf) {
40263                 size.height -= tbf.getHeight();
40264             }
40265             if (thd) {
40266                 size.height -= thd.getHeight();
40267             }
40268             
40269             tbd.setSize(size.width, size.height );
40270             // this is for the account management tab -seems to work there.
40271             var thd = grid.getGridEl().select('thead',true).first();
40272             //if (tbd) {
40273             //    tbd.setSize(size.width, size.height - thd.getHeight());
40274             //}
40275              
40276             grid.autoSize();
40277         }
40278     },
40279      
40280     
40281     
40282     beforeSlide : function(){
40283         this.grid.getView().scroller.clip();
40284     },
40285     
40286     afterSlide : function(){
40287         this.grid.getView().scroller.unclip();
40288     },
40289     
40290     destroy : function(){
40291         this.grid.destroy();
40292         delete this.grid;
40293         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40294     }
40295 });
40296
40297 /**
40298  * @class Roo.bootstrap.panel.Nest
40299  * @extends Roo.bootstrap.panel.Content
40300  * @constructor
40301  * Create a new Panel, that can contain a layout.Border.
40302  * 
40303  * 
40304  * @param {Roo.BorderLayout} layout The layout for this panel
40305  * @param {String/Object} config A string to set only the title or a config object
40306  */
40307 Roo.bootstrap.panel.Nest = function(config)
40308 {
40309     // construct with only one argument..
40310     /* FIXME - implement nicer consturctors
40311     if (layout.layout) {
40312         config = layout;
40313         layout = config.layout;
40314         delete config.layout;
40315     }
40316     if (layout.xtype && !layout.getEl) {
40317         // then layout needs constructing..
40318         layout = Roo.factory(layout, Roo);
40319     }
40320     */
40321     
40322     config.el =  config.layout.getEl();
40323     
40324     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40325     
40326     config.layout.monitorWindowResize = false; // turn off autosizing
40327     this.layout = config.layout;
40328     this.layout.getEl().addClass("roo-layout-nested-layout");
40329     this.layout.parent = this;
40330     
40331     
40332     
40333     
40334 };
40335
40336 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40337
40338     setSize : function(width, height){
40339         if(!this.ignoreResize(width, height)){
40340             var size = this.adjustForComponents(width, height);
40341             var el = this.layout.getEl();
40342             if (size.height < 1) {
40343                 el.setWidth(size.width);   
40344             } else {
40345                 el.setSize(size.width, size.height);
40346             }
40347             var touch = el.dom.offsetWidth;
40348             this.layout.layout();
40349             // ie requires a double layout on the first pass
40350             if(Roo.isIE && !this.initialized){
40351                 this.initialized = true;
40352                 this.layout.layout();
40353             }
40354         }
40355     },
40356     
40357     // activate all subpanels if not currently active..
40358     
40359     setActiveState : function(active){
40360         this.active = active;
40361         this.setActiveClass(active);
40362         
40363         if(!active){
40364             this.fireEvent("deactivate", this);
40365             return;
40366         }
40367         
40368         this.fireEvent("activate", this);
40369         // not sure if this should happen before or after..
40370         if (!this.layout) {
40371             return; // should not happen..
40372         }
40373         var reg = false;
40374         for (var r in this.layout.regions) {
40375             reg = this.layout.getRegion(r);
40376             if (reg.getActivePanel()) {
40377                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40378                 reg.setActivePanel(reg.getActivePanel());
40379                 continue;
40380             }
40381             if (!reg.panels.length) {
40382                 continue;
40383             }
40384             reg.showPanel(reg.getPanel(0));
40385         }
40386         
40387         
40388         
40389         
40390     },
40391     
40392     /**
40393      * Returns the nested BorderLayout for this panel
40394      * @return {Roo.BorderLayout} 
40395      */
40396     getLayout : function(){
40397         return this.layout;
40398     },
40399     
40400      /**
40401      * Adds a xtype elements to the layout of the nested panel
40402      * <pre><code>
40403
40404 panel.addxtype({
40405        xtype : 'ContentPanel',
40406        region: 'west',
40407        items: [ .... ]
40408    }
40409 );
40410
40411 panel.addxtype({
40412         xtype : 'NestedLayoutPanel',
40413         region: 'west',
40414         layout: {
40415            center: { },
40416            west: { }   
40417         },
40418         items : [ ... list of content panels or nested layout panels.. ]
40419    }
40420 );
40421 </code></pre>
40422      * @param {Object} cfg Xtype definition of item to add.
40423      */
40424     addxtype : function(cfg) {
40425         return this.layout.addxtype(cfg);
40426     
40427     }
40428 });/*
40429  * Based on:
40430  * Ext JS Library 1.1.1
40431  * Copyright(c) 2006-2007, Ext JS, LLC.
40432  *
40433  * Originally Released Under LGPL - original licence link has changed is not relivant.
40434  *
40435  * Fork - LGPL
40436  * <script type="text/javascript">
40437  */
40438 /**
40439  * @class Roo.TabPanel
40440  * @extends Roo.util.Observable
40441  * A lightweight tab container.
40442  * <br><br>
40443  * Usage:
40444  * <pre><code>
40445 // basic tabs 1, built from existing content
40446 var tabs = new Roo.TabPanel("tabs1");
40447 tabs.addTab("script", "View Script");
40448 tabs.addTab("markup", "View Markup");
40449 tabs.activate("script");
40450
40451 // more advanced tabs, built from javascript
40452 var jtabs = new Roo.TabPanel("jtabs");
40453 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40454
40455 // set up the UpdateManager
40456 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40457 var updater = tab2.getUpdateManager();
40458 updater.setDefaultUrl("ajax1.htm");
40459 tab2.on('activate', updater.refresh, updater, true);
40460
40461 // Use setUrl for Ajax loading
40462 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40463 tab3.setUrl("ajax2.htm", null, true);
40464
40465 // Disabled tab
40466 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40467 tab4.disable();
40468
40469 jtabs.activate("jtabs-1");
40470  * </code></pre>
40471  * @constructor
40472  * Create a new TabPanel.
40473  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40474  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40475  */
40476 Roo.bootstrap.panel.Tabs = function(config){
40477     /**
40478     * The container element for this TabPanel.
40479     * @type Roo.Element
40480     */
40481     this.el = Roo.get(config.el);
40482     delete config.el;
40483     if(config){
40484         if(typeof config == "boolean"){
40485             this.tabPosition = config ? "bottom" : "top";
40486         }else{
40487             Roo.apply(this, config);
40488         }
40489     }
40490     
40491     if(this.tabPosition == "bottom"){
40492         // if tabs are at the bottom = create the body first.
40493         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40494         this.el.addClass("roo-tabs-bottom");
40495     }
40496     // next create the tabs holders
40497     
40498     if (this.tabPosition == "west"){
40499         
40500         var reg = this.region; // fake it..
40501         while (reg) {
40502             if (!reg.mgr.parent) {
40503                 break;
40504             }
40505             reg = reg.mgr.parent.region;
40506         }
40507         Roo.log("got nest?");
40508         Roo.log(reg);
40509         if (reg.mgr.getRegion('west')) {
40510             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40511             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40512             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40513             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40514             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40515         
40516             
40517         }
40518         
40519         
40520     } else {
40521      
40522         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40523         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40524         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40525         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40526     }
40527     
40528     
40529     if(Roo.isIE){
40530         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40531     }
40532     
40533     // finally - if tabs are at the top, then create the body last..
40534     if(this.tabPosition != "bottom"){
40535         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40536          * @type Roo.Element
40537          */
40538         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40539         this.el.addClass("roo-tabs-top");
40540     }
40541     this.items = [];
40542
40543     this.bodyEl.setStyle("position", "relative");
40544
40545     this.active = null;
40546     this.activateDelegate = this.activate.createDelegate(this);
40547
40548     this.addEvents({
40549         /**
40550          * @event tabchange
40551          * Fires when the active tab changes
40552          * @param {Roo.TabPanel} this
40553          * @param {Roo.TabPanelItem} activePanel The new active tab
40554          */
40555         "tabchange": true,
40556         /**
40557          * @event beforetabchange
40558          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40559          * @param {Roo.TabPanel} this
40560          * @param {Object} e Set cancel to true on this object to cancel the tab change
40561          * @param {Roo.TabPanelItem} tab The tab being changed to
40562          */
40563         "beforetabchange" : true
40564     });
40565
40566     Roo.EventManager.onWindowResize(this.onResize, this);
40567     this.cpad = this.el.getPadding("lr");
40568     this.hiddenCount = 0;
40569
40570
40571     // toolbar on the tabbar support...
40572     if (this.toolbar) {
40573         alert("no toolbar support yet");
40574         this.toolbar  = false;
40575         /*
40576         var tcfg = this.toolbar;
40577         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40578         this.toolbar = new Roo.Toolbar(tcfg);
40579         if (Roo.isSafari) {
40580             var tbl = tcfg.container.child('table', true);
40581             tbl.setAttribute('width', '100%');
40582         }
40583         */
40584         
40585     }
40586    
40587
40588
40589     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40590 };
40591
40592 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40593     /*
40594      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40595      */
40596     tabPosition : "top",
40597     /*
40598      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40599      */
40600     currentTabWidth : 0,
40601     /*
40602      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40603      */
40604     minTabWidth : 40,
40605     /*
40606      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40607      */
40608     maxTabWidth : 250,
40609     /*
40610      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40611      */
40612     preferredTabWidth : 175,
40613     /*
40614      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40615      */
40616     resizeTabs : false,
40617     /*
40618      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40619      */
40620     monitorResize : true,
40621     /*
40622      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40623      */
40624     toolbar : false,  // set by caller..
40625     
40626     region : false, /// set by caller
40627     
40628     disableTooltips : true, // not used yet...
40629
40630     /**
40631      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40632      * @param {String} id The id of the div to use <b>or create</b>
40633      * @param {String} text The text for the tab
40634      * @param {String} content (optional) Content to put in the TabPanelItem body
40635      * @param {Boolean} closable (optional) True to create a close icon on the tab
40636      * @return {Roo.TabPanelItem} The created TabPanelItem
40637      */
40638     addTab : function(id, text, content, closable, tpl)
40639     {
40640         var item = new Roo.bootstrap.panel.TabItem({
40641             panel: this,
40642             id : id,
40643             text : text,
40644             closable : closable,
40645             tpl : tpl
40646         });
40647         this.addTabItem(item);
40648         if(content){
40649             item.setContent(content);
40650         }
40651         return item;
40652     },
40653
40654     /**
40655      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40656      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40657      * @return {Roo.TabPanelItem}
40658      */
40659     getTab : function(id){
40660         return this.items[id];
40661     },
40662
40663     /**
40664      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40665      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40666      */
40667     hideTab : function(id){
40668         var t = this.items[id];
40669         if(!t.isHidden()){
40670            t.setHidden(true);
40671            this.hiddenCount++;
40672            this.autoSizeTabs();
40673         }
40674     },
40675
40676     /**
40677      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40678      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40679      */
40680     unhideTab : function(id){
40681         var t = this.items[id];
40682         if(t.isHidden()){
40683            t.setHidden(false);
40684            this.hiddenCount--;
40685            this.autoSizeTabs();
40686         }
40687     },
40688
40689     /**
40690      * Adds an existing {@link Roo.TabPanelItem}.
40691      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40692      */
40693     addTabItem : function(item)
40694     {
40695         this.items[item.id] = item;
40696         this.items.push(item);
40697         this.autoSizeTabs();
40698       //  if(this.resizeTabs){
40699     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40700   //         this.autoSizeTabs();
40701 //        }else{
40702 //            item.autoSize();
40703        // }
40704     },
40705
40706     /**
40707      * Removes a {@link Roo.TabPanelItem}.
40708      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40709      */
40710     removeTab : function(id){
40711         var items = this.items;
40712         var tab = items[id];
40713         if(!tab) { return; }
40714         var index = items.indexOf(tab);
40715         if(this.active == tab && items.length > 1){
40716             var newTab = this.getNextAvailable(index);
40717             if(newTab) {
40718                 newTab.activate();
40719             }
40720         }
40721         this.stripEl.dom.removeChild(tab.pnode.dom);
40722         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40723             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40724         }
40725         items.splice(index, 1);
40726         delete this.items[tab.id];
40727         tab.fireEvent("close", tab);
40728         tab.purgeListeners();
40729         this.autoSizeTabs();
40730     },
40731
40732     getNextAvailable : function(start){
40733         var items = this.items;
40734         var index = start;
40735         // look for a next tab that will slide over to
40736         // replace the one being removed
40737         while(index < items.length){
40738             var item = items[++index];
40739             if(item && !item.isHidden()){
40740                 return item;
40741             }
40742         }
40743         // if one isn't found select the previous tab (on the left)
40744         index = start;
40745         while(index >= 0){
40746             var item = items[--index];
40747             if(item && !item.isHidden()){
40748                 return item;
40749             }
40750         }
40751         return null;
40752     },
40753
40754     /**
40755      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40756      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40757      */
40758     disableTab : function(id){
40759         var tab = this.items[id];
40760         if(tab && this.active != tab){
40761             tab.disable();
40762         }
40763     },
40764
40765     /**
40766      * Enables a {@link Roo.TabPanelItem} that is disabled.
40767      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40768      */
40769     enableTab : function(id){
40770         var tab = this.items[id];
40771         tab.enable();
40772     },
40773
40774     /**
40775      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40776      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40777      * @return {Roo.TabPanelItem} The TabPanelItem.
40778      */
40779     activate : function(id)
40780     {
40781         //Roo.log('activite:'  + id);
40782         
40783         var tab = this.items[id];
40784         if(!tab){
40785             return null;
40786         }
40787         if(tab == this.active || tab.disabled){
40788             return tab;
40789         }
40790         var e = {};
40791         this.fireEvent("beforetabchange", this, e, tab);
40792         if(e.cancel !== true && !tab.disabled){
40793             if(this.active){
40794                 this.active.hide();
40795             }
40796             this.active = this.items[id];
40797             this.active.show();
40798             this.fireEvent("tabchange", this, this.active);
40799         }
40800         return tab;
40801     },
40802
40803     /**
40804      * Gets the active {@link Roo.TabPanelItem}.
40805      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40806      */
40807     getActiveTab : function(){
40808         return this.active;
40809     },
40810
40811     /**
40812      * Updates the tab body element to fit the height of the container element
40813      * for overflow scrolling
40814      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40815      */
40816     syncHeight : function(targetHeight){
40817         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40818         var bm = this.bodyEl.getMargins();
40819         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40820         this.bodyEl.setHeight(newHeight);
40821         return newHeight;
40822     },
40823
40824     onResize : function(){
40825         if(this.monitorResize){
40826             this.autoSizeTabs();
40827         }
40828     },
40829
40830     /**
40831      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40832      */
40833     beginUpdate : function(){
40834         this.updating = true;
40835     },
40836
40837     /**
40838      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40839      */
40840     endUpdate : function(){
40841         this.updating = false;
40842         this.autoSizeTabs();
40843     },
40844
40845     /**
40846      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40847      */
40848     autoSizeTabs : function()
40849     {
40850         var count = this.items.length;
40851         var vcount = count - this.hiddenCount;
40852         
40853         if (vcount < 2) {
40854             this.stripEl.hide();
40855         } else {
40856             this.stripEl.show();
40857         }
40858         
40859         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40860             return;
40861         }
40862         
40863         
40864         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40865         var availWidth = Math.floor(w / vcount);
40866         var b = this.stripBody;
40867         if(b.getWidth() > w){
40868             var tabs = this.items;
40869             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40870             if(availWidth < this.minTabWidth){
40871                 /*if(!this.sleft){    // incomplete scrolling code
40872                     this.createScrollButtons();
40873                 }
40874                 this.showScroll();
40875                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40876             }
40877         }else{
40878             if(this.currentTabWidth < this.preferredTabWidth){
40879                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40880             }
40881         }
40882     },
40883
40884     /**
40885      * Returns the number of tabs in this TabPanel.
40886      * @return {Number}
40887      */
40888      getCount : function(){
40889          return this.items.length;
40890      },
40891
40892     /**
40893      * Resizes all the tabs to the passed width
40894      * @param {Number} The new width
40895      */
40896     setTabWidth : function(width){
40897         this.currentTabWidth = width;
40898         for(var i = 0, len = this.items.length; i < len; i++) {
40899                 if(!this.items[i].isHidden()) {
40900                 this.items[i].setWidth(width);
40901             }
40902         }
40903     },
40904
40905     /**
40906      * Destroys this TabPanel
40907      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40908      */
40909     destroy : function(removeEl){
40910         Roo.EventManager.removeResizeListener(this.onResize, this);
40911         for(var i = 0, len = this.items.length; i < len; i++){
40912             this.items[i].purgeListeners();
40913         }
40914         if(removeEl === true){
40915             this.el.update("");
40916             this.el.remove();
40917         }
40918     },
40919     
40920     createStrip : function(container)
40921     {
40922         var strip = document.createElement("nav");
40923         strip.className = Roo.bootstrap.version == 4 ?
40924             "navbar-light bg-light" : 
40925             "navbar navbar-default"; //"x-tabs-wrap";
40926         container.appendChild(strip);
40927         return strip;
40928     },
40929     
40930     createStripList : function(strip)
40931     {
40932         // div wrapper for retard IE
40933         // returns the "tr" element.
40934         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40935         //'<div class="x-tabs-strip-wrap">'+
40936           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40937           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40938         return strip.firstChild; //.firstChild.firstChild.firstChild;
40939     },
40940     createBody : function(container)
40941     {
40942         var body = document.createElement("div");
40943         Roo.id(body, "tab-body");
40944         //Roo.fly(body).addClass("x-tabs-body");
40945         Roo.fly(body).addClass("tab-content");
40946         container.appendChild(body);
40947         return body;
40948     },
40949     createItemBody :function(bodyEl, id){
40950         var body = Roo.getDom(id);
40951         if(!body){
40952             body = document.createElement("div");
40953             body.id = id;
40954         }
40955         //Roo.fly(body).addClass("x-tabs-item-body");
40956         Roo.fly(body).addClass("tab-pane");
40957          bodyEl.insertBefore(body, bodyEl.firstChild);
40958         return body;
40959     },
40960     /** @private */
40961     createStripElements :  function(stripEl, text, closable, tpl)
40962     {
40963         var td = document.createElement("li"); // was td..
40964         td.className = 'nav-item';
40965         
40966         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40967         
40968         
40969         stripEl.appendChild(td);
40970         /*if(closable){
40971             td.className = "x-tabs-closable";
40972             if(!this.closeTpl){
40973                 this.closeTpl = new Roo.Template(
40974                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40975                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40976                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40977                 );
40978             }
40979             var el = this.closeTpl.overwrite(td, {"text": text});
40980             var close = el.getElementsByTagName("div")[0];
40981             var inner = el.getElementsByTagName("em")[0];
40982             return {"el": el, "close": close, "inner": inner};
40983         } else {
40984         */
40985         // not sure what this is..
40986 //            if(!this.tabTpl){
40987                 //this.tabTpl = new Roo.Template(
40988                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40989                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40990                 //);
40991 //                this.tabTpl = new Roo.Template(
40992 //                   '<a href="#">' +
40993 //                   '<span unselectable="on"' +
40994 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40995 //                            ' >{text}</span></a>'
40996 //                );
40997 //                
40998 //            }
40999
41000
41001             var template = tpl || this.tabTpl || false;
41002             
41003             if(!template){
41004                 template =  new Roo.Template(
41005                         Roo.bootstrap.version == 4 ? 
41006                             (
41007                                 '<a class="nav-link" href="#" unselectable="on"' +
41008                                      (this.disableTooltips ? '' : ' title="{text}"') +
41009                                      ' >{text}</a>'
41010                             ) : (
41011                                 '<a class="nav-link" href="#">' +
41012                                 '<span unselectable="on"' +
41013                                          (this.disableTooltips ? '' : ' title="{text}"') +
41014                                     ' >{text}</span></a>'
41015                             )
41016                 );
41017             }
41018             
41019             switch (typeof(template)) {
41020                 case 'object' :
41021                     break;
41022                 case 'string' :
41023                     template = new Roo.Template(template);
41024                     break;
41025                 default :
41026                     break;
41027             }
41028             
41029             var el = template.overwrite(td, {"text": text});
41030             
41031             var inner = el.getElementsByTagName("span")[0];
41032             
41033             return {"el": el, "inner": inner};
41034             
41035     }
41036         
41037     
41038 });
41039
41040 /**
41041  * @class Roo.TabPanelItem
41042  * @extends Roo.util.Observable
41043  * Represents an individual item (tab plus body) in a TabPanel.
41044  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41045  * @param {String} id The id of this TabPanelItem
41046  * @param {String} text The text for the tab of this TabPanelItem
41047  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41048  */
41049 Roo.bootstrap.panel.TabItem = function(config){
41050     /**
41051      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41052      * @type Roo.TabPanel
41053      */
41054     this.tabPanel = config.panel;
41055     /**
41056      * The id for this TabPanelItem
41057      * @type String
41058      */
41059     this.id = config.id;
41060     /** @private */
41061     this.disabled = false;
41062     /** @private */
41063     this.text = config.text;
41064     /** @private */
41065     this.loaded = false;
41066     this.closable = config.closable;
41067
41068     /**
41069      * The body element for this TabPanelItem.
41070      * @type Roo.Element
41071      */
41072     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41073     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41074     this.bodyEl.setStyle("display", "block");
41075     this.bodyEl.setStyle("zoom", "1");
41076     //this.hideAction();
41077
41078     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41079     /** @private */
41080     this.el = Roo.get(els.el);
41081     this.inner = Roo.get(els.inner, true);
41082      this.textEl = Roo.bootstrap.version == 4 ?
41083         this.el : Roo.get(this.el.dom.firstChild, true);
41084
41085     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41086     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41087
41088     
41089 //    this.el.on("mousedown", this.onTabMouseDown, this);
41090     this.el.on("click", this.onTabClick, this);
41091     /** @private */
41092     if(config.closable){
41093         var c = Roo.get(els.close, true);
41094         c.dom.title = this.closeText;
41095         c.addClassOnOver("close-over");
41096         c.on("click", this.closeClick, this);
41097      }
41098
41099     this.addEvents({
41100          /**
41101          * @event activate
41102          * Fires when this tab becomes the active tab.
41103          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41104          * @param {Roo.TabPanelItem} this
41105          */
41106         "activate": true,
41107         /**
41108          * @event beforeclose
41109          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41110          * @param {Roo.TabPanelItem} this
41111          * @param {Object} e Set cancel to true on this object to cancel the close.
41112          */
41113         "beforeclose": true,
41114         /**
41115          * @event close
41116          * Fires when this tab is closed.
41117          * @param {Roo.TabPanelItem} this
41118          */
41119          "close": true,
41120         /**
41121          * @event deactivate
41122          * Fires when this tab is no longer the active tab.
41123          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41124          * @param {Roo.TabPanelItem} this
41125          */
41126          "deactivate" : true
41127     });
41128     this.hidden = false;
41129
41130     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41131 };
41132
41133 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41134            {
41135     purgeListeners : function(){
41136        Roo.util.Observable.prototype.purgeListeners.call(this);
41137        this.el.removeAllListeners();
41138     },
41139     /**
41140      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41141      */
41142     show : function(){
41143         this.status_node.addClass("active");
41144         this.showAction();
41145         if(Roo.isOpera){
41146             this.tabPanel.stripWrap.repaint();
41147         }
41148         this.fireEvent("activate", this.tabPanel, this);
41149     },
41150
41151     /**
41152      * Returns true if this tab is the active tab.
41153      * @return {Boolean}
41154      */
41155     isActive : function(){
41156         return this.tabPanel.getActiveTab() == this;
41157     },
41158
41159     /**
41160      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41161      */
41162     hide : function(){
41163         this.status_node.removeClass("active");
41164         this.hideAction();
41165         this.fireEvent("deactivate", this.tabPanel, this);
41166     },
41167
41168     hideAction : function(){
41169         this.bodyEl.hide();
41170         this.bodyEl.setStyle("position", "absolute");
41171         this.bodyEl.setLeft("-20000px");
41172         this.bodyEl.setTop("-20000px");
41173     },
41174
41175     showAction : function(){
41176         this.bodyEl.setStyle("position", "relative");
41177         this.bodyEl.setTop("");
41178         this.bodyEl.setLeft("");
41179         this.bodyEl.show();
41180     },
41181
41182     /**
41183      * Set the tooltip for the tab.
41184      * @param {String} tooltip The tab's tooltip
41185      */
41186     setTooltip : function(text){
41187         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41188             this.textEl.dom.qtip = text;
41189             this.textEl.dom.removeAttribute('title');
41190         }else{
41191             this.textEl.dom.title = text;
41192         }
41193     },
41194
41195     onTabClick : function(e){
41196         e.preventDefault();
41197         this.tabPanel.activate(this.id);
41198     },
41199
41200     onTabMouseDown : function(e){
41201         e.preventDefault();
41202         this.tabPanel.activate(this.id);
41203     },
41204 /*
41205     getWidth : function(){
41206         return this.inner.getWidth();
41207     },
41208
41209     setWidth : function(width){
41210         var iwidth = width - this.linode.getPadding("lr");
41211         this.inner.setWidth(iwidth);
41212         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41213         this.linode.setWidth(width);
41214     },
41215 */
41216     /**
41217      * Show or hide the tab
41218      * @param {Boolean} hidden True to hide or false to show.
41219      */
41220     setHidden : function(hidden){
41221         this.hidden = hidden;
41222         this.linode.setStyle("display", hidden ? "none" : "");
41223     },
41224
41225     /**
41226      * Returns true if this tab is "hidden"
41227      * @return {Boolean}
41228      */
41229     isHidden : function(){
41230         return this.hidden;
41231     },
41232
41233     /**
41234      * Returns the text for this tab
41235      * @return {String}
41236      */
41237     getText : function(){
41238         return this.text;
41239     },
41240     /*
41241     autoSize : function(){
41242         //this.el.beginMeasure();
41243         this.textEl.setWidth(1);
41244         /*
41245          *  #2804 [new] Tabs in Roojs
41246          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41247          */
41248         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41249         //this.el.endMeasure();
41250     //},
41251
41252     /**
41253      * Sets the text for the tab (Note: this also sets the tooltip text)
41254      * @param {String} text The tab's text and tooltip
41255      */
41256     setText : function(text){
41257         this.text = text;
41258         this.textEl.update(text);
41259         this.setTooltip(text);
41260         //if(!this.tabPanel.resizeTabs){
41261         //    this.autoSize();
41262         //}
41263     },
41264     /**
41265      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41266      */
41267     activate : function(){
41268         this.tabPanel.activate(this.id);
41269     },
41270
41271     /**
41272      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41273      */
41274     disable : function(){
41275         if(this.tabPanel.active != this){
41276             this.disabled = true;
41277             this.status_node.addClass("disabled");
41278         }
41279     },
41280
41281     /**
41282      * Enables this TabPanelItem if it was previously disabled.
41283      */
41284     enable : function(){
41285         this.disabled = false;
41286         this.status_node.removeClass("disabled");
41287     },
41288
41289     /**
41290      * Sets the content for this TabPanelItem.
41291      * @param {String} content The content
41292      * @param {Boolean} loadScripts true to look for and load scripts
41293      */
41294     setContent : function(content, loadScripts){
41295         this.bodyEl.update(content, loadScripts);
41296     },
41297
41298     /**
41299      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41300      * @return {Roo.UpdateManager} The UpdateManager
41301      */
41302     getUpdateManager : function(){
41303         return this.bodyEl.getUpdateManager();
41304     },
41305
41306     /**
41307      * Set a URL to be used to load the content for this TabPanelItem.
41308      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41309      * @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)
41310      * @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)
41311      * @return {Roo.UpdateManager} The UpdateManager
41312      */
41313     setUrl : function(url, params, loadOnce){
41314         if(this.refreshDelegate){
41315             this.un('activate', this.refreshDelegate);
41316         }
41317         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41318         this.on("activate", this.refreshDelegate);
41319         return this.bodyEl.getUpdateManager();
41320     },
41321
41322     /** @private */
41323     _handleRefresh : function(url, params, loadOnce){
41324         if(!loadOnce || !this.loaded){
41325             var updater = this.bodyEl.getUpdateManager();
41326             updater.update(url, params, this._setLoaded.createDelegate(this));
41327         }
41328     },
41329
41330     /**
41331      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41332      *   Will fail silently if the setUrl method has not been called.
41333      *   This does not activate the panel, just updates its content.
41334      */
41335     refresh : function(){
41336         if(this.refreshDelegate){
41337            this.loaded = false;
41338            this.refreshDelegate();
41339         }
41340     },
41341
41342     /** @private */
41343     _setLoaded : function(){
41344         this.loaded = true;
41345     },
41346
41347     /** @private */
41348     closeClick : function(e){
41349         var o = {};
41350         e.stopEvent();
41351         this.fireEvent("beforeclose", this, o);
41352         if(o.cancel !== true){
41353             this.tabPanel.removeTab(this.id);
41354         }
41355     },
41356     /**
41357      * The text displayed in the tooltip for the close icon.
41358      * @type String
41359      */
41360     closeText : "Close this tab"
41361 });
41362 /**
41363 *    This script refer to:
41364 *    Title: International Telephone Input
41365 *    Author: Jack O'Connor
41366 *    Code version:  v12.1.12
41367 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41368 **/
41369
41370 Roo.bootstrap.PhoneInputData = function() {
41371     var d = [
41372       [
41373         "Afghanistan (‫افغانستان‬‎)",
41374         "af",
41375         "93"
41376       ],
41377       [
41378         "Albania (Shqipëri)",
41379         "al",
41380         "355"
41381       ],
41382       [
41383         "Algeria (‫الجزائر‬‎)",
41384         "dz",
41385         "213"
41386       ],
41387       [
41388         "American Samoa",
41389         "as",
41390         "1684"
41391       ],
41392       [
41393         "Andorra",
41394         "ad",
41395         "376"
41396       ],
41397       [
41398         "Angola",
41399         "ao",
41400         "244"
41401       ],
41402       [
41403         "Anguilla",
41404         "ai",
41405         "1264"
41406       ],
41407       [
41408         "Antigua and Barbuda",
41409         "ag",
41410         "1268"
41411       ],
41412       [
41413         "Argentina",
41414         "ar",
41415         "54"
41416       ],
41417       [
41418         "Armenia (Հայաստան)",
41419         "am",
41420         "374"
41421       ],
41422       [
41423         "Aruba",
41424         "aw",
41425         "297"
41426       ],
41427       [
41428         "Australia",
41429         "au",
41430         "61",
41431         0
41432       ],
41433       [
41434         "Austria (Österreich)",
41435         "at",
41436         "43"
41437       ],
41438       [
41439         "Azerbaijan (Azərbaycan)",
41440         "az",
41441         "994"
41442       ],
41443       [
41444         "Bahamas",
41445         "bs",
41446         "1242"
41447       ],
41448       [
41449         "Bahrain (‫البحرين‬‎)",
41450         "bh",
41451         "973"
41452       ],
41453       [
41454         "Bangladesh (বাংলাদেশ)",
41455         "bd",
41456         "880"
41457       ],
41458       [
41459         "Barbados",
41460         "bb",
41461         "1246"
41462       ],
41463       [
41464         "Belarus (Беларусь)",
41465         "by",
41466         "375"
41467       ],
41468       [
41469         "Belgium (België)",
41470         "be",
41471         "32"
41472       ],
41473       [
41474         "Belize",
41475         "bz",
41476         "501"
41477       ],
41478       [
41479         "Benin (Bénin)",
41480         "bj",
41481         "229"
41482       ],
41483       [
41484         "Bermuda",
41485         "bm",
41486         "1441"
41487       ],
41488       [
41489         "Bhutan (འབྲུག)",
41490         "bt",
41491         "975"
41492       ],
41493       [
41494         "Bolivia",
41495         "bo",
41496         "591"
41497       ],
41498       [
41499         "Bosnia and Herzegovina (Босна и Херцеговина)",
41500         "ba",
41501         "387"
41502       ],
41503       [
41504         "Botswana",
41505         "bw",
41506         "267"
41507       ],
41508       [
41509         "Brazil (Brasil)",
41510         "br",
41511         "55"
41512       ],
41513       [
41514         "British Indian Ocean Territory",
41515         "io",
41516         "246"
41517       ],
41518       [
41519         "British Virgin Islands",
41520         "vg",
41521         "1284"
41522       ],
41523       [
41524         "Brunei",
41525         "bn",
41526         "673"
41527       ],
41528       [
41529         "Bulgaria (България)",
41530         "bg",
41531         "359"
41532       ],
41533       [
41534         "Burkina Faso",
41535         "bf",
41536         "226"
41537       ],
41538       [
41539         "Burundi (Uburundi)",
41540         "bi",
41541         "257"
41542       ],
41543       [
41544         "Cambodia (កម្ពុជា)",
41545         "kh",
41546         "855"
41547       ],
41548       [
41549         "Cameroon (Cameroun)",
41550         "cm",
41551         "237"
41552       ],
41553       [
41554         "Canada",
41555         "ca",
41556         "1",
41557         1,
41558         ["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"]
41559       ],
41560       [
41561         "Cape Verde (Kabu Verdi)",
41562         "cv",
41563         "238"
41564       ],
41565       [
41566         "Caribbean Netherlands",
41567         "bq",
41568         "599",
41569         1
41570       ],
41571       [
41572         "Cayman Islands",
41573         "ky",
41574         "1345"
41575       ],
41576       [
41577         "Central African Republic (République centrafricaine)",
41578         "cf",
41579         "236"
41580       ],
41581       [
41582         "Chad (Tchad)",
41583         "td",
41584         "235"
41585       ],
41586       [
41587         "Chile",
41588         "cl",
41589         "56"
41590       ],
41591       [
41592         "China (中国)",
41593         "cn",
41594         "86"
41595       ],
41596       [
41597         "Christmas Island",
41598         "cx",
41599         "61",
41600         2
41601       ],
41602       [
41603         "Cocos (Keeling) Islands",
41604         "cc",
41605         "61",
41606         1
41607       ],
41608       [
41609         "Colombia",
41610         "co",
41611         "57"
41612       ],
41613       [
41614         "Comoros (‫جزر القمر‬‎)",
41615         "km",
41616         "269"
41617       ],
41618       [
41619         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41620         "cd",
41621         "243"
41622       ],
41623       [
41624         "Congo (Republic) (Congo-Brazzaville)",
41625         "cg",
41626         "242"
41627       ],
41628       [
41629         "Cook Islands",
41630         "ck",
41631         "682"
41632       ],
41633       [
41634         "Costa Rica",
41635         "cr",
41636         "506"
41637       ],
41638       [
41639         "Côte d’Ivoire",
41640         "ci",
41641         "225"
41642       ],
41643       [
41644         "Croatia (Hrvatska)",
41645         "hr",
41646         "385"
41647       ],
41648       [
41649         "Cuba",
41650         "cu",
41651         "53"
41652       ],
41653       [
41654         "Curaçao",
41655         "cw",
41656         "599",
41657         0
41658       ],
41659       [
41660         "Cyprus (Κύπρος)",
41661         "cy",
41662         "357"
41663       ],
41664       [
41665         "Czech Republic (Česká republika)",
41666         "cz",
41667         "420"
41668       ],
41669       [
41670         "Denmark (Danmark)",
41671         "dk",
41672         "45"
41673       ],
41674       [
41675         "Djibouti",
41676         "dj",
41677         "253"
41678       ],
41679       [
41680         "Dominica",
41681         "dm",
41682         "1767"
41683       ],
41684       [
41685         "Dominican Republic (República Dominicana)",
41686         "do",
41687         "1",
41688         2,
41689         ["809", "829", "849"]
41690       ],
41691       [
41692         "Ecuador",
41693         "ec",
41694         "593"
41695       ],
41696       [
41697         "Egypt (‫مصر‬‎)",
41698         "eg",
41699         "20"
41700       ],
41701       [
41702         "El Salvador",
41703         "sv",
41704         "503"
41705       ],
41706       [
41707         "Equatorial Guinea (Guinea Ecuatorial)",
41708         "gq",
41709         "240"
41710       ],
41711       [
41712         "Eritrea",
41713         "er",
41714         "291"
41715       ],
41716       [
41717         "Estonia (Eesti)",
41718         "ee",
41719         "372"
41720       ],
41721       [
41722         "Ethiopia",
41723         "et",
41724         "251"
41725       ],
41726       [
41727         "Falkland Islands (Islas Malvinas)",
41728         "fk",
41729         "500"
41730       ],
41731       [
41732         "Faroe Islands (Føroyar)",
41733         "fo",
41734         "298"
41735       ],
41736       [
41737         "Fiji",
41738         "fj",
41739         "679"
41740       ],
41741       [
41742         "Finland (Suomi)",
41743         "fi",
41744         "358",
41745         0
41746       ],
41747       [
41748         "France",
41749         "fr",
41750         "33"
41751       ],
41752       [
41753         "French Guiana (Guyane française)",
41754         "gf",
41755         "594"
41756       ],
41757       [
41758         "French Polynesia (Polynésie française)",
41759         "pf",
41760         "689"
41761       ],
41762       [
41763         "Gabon",
41764         "ga",
41765         "241"
41766       ],
41767       [
41768         "Gambia",
41769         "gm",
41770         "220"
41771       ],
41772       [
41773         "Georgia (საქართველო)",
41774         "ge",
41775         "995"
41776       ],
41777       [
41778         "Germany (Deutschland)",
41779         "de",
41780         "49"
41781       ],
41782       [
41783         "Ghana (Gaana)",
41784         "gh",
41785         "233"
41786       ],
41787       [
41788         "Gibraltar",
41789         "gi",
41790         "350"
41791       ],
41792       [
41793         "Greece (Ελλάδα)",
41794         "gr",
41795         "30"
41796       ],
41797       [
41798         "Greenland (Kalaallit Nunaat)",
41799         "gl",
41800         "299"
41801       ],
41802       [
41803         "Grenada",
41804         "gd",
41805         "1473"
41806       ],
41807       [
41808         "Guadeloupe",
41809         "gp",
41810         "590",
41811         0
41812       ],
41813       [
41814         "Guam",
41815         "gu",
41816         "1671"
41817       ],
41818       [
41819         "Guatemala",
41820         "gt",
41821         "502"
41822       ],
41823       [
41824         "Guernsey",
41825         "gg",
41826         "44",
41827         1
41828       ],
41829       [
41830         "Guinea (Guinée)",
41831         "gn",
41832         "224"
41833       ],
41834       [
41835         "Guinea-Bissau (Guiné Bissau)",
41836         "gw",
41837         "245"
41838       ],
41839       [
41840         "Guyana",
41841         "gy",
41842         "592"
41843       ],
41844       [
41845         "Haiti",
41846         "ht",
41847         "509"
41848       ],
41849       [
41850         "Honduras",
41851         "hn",
41852         "504"
41853       ],
41854       [
41855         "Hong Kong (香港)",
41856         "hk",
41857         "852"
41858       ],
41859       [
41860         "Hungary (Magyarország)",
41861         "hu",
41862         "36"
41863       ],
41864       [
41865         "Iceland (Ísland)",
41866         "is",
41867         "354"
41868       ],
41869       [
41870         "India (भारत)",
41871         "in",
41872         "91"
41873       ],
41874       [
41875         "Indonesia",
41876         "id",
41877         "62"
41878       ],
41879       [
41880         "Iran (‫ایران‬‎)",
41881         "ir",
41882         "98"
41883       ],
41884       [
41885         "Iraq (‫العراق‬‎)",
41886         "iq",
41887         "964"
41888       ],
41889       [
41890         "Ireland",
41891         "ie",
41892         "353"
41893       ],
41894       [
41895         "Isle of Man",
41896         "im",
41897         "44",
41898         2
41899       ],
41900       [
41901         "Israel (‫ישראל‬‎)",
41902         "il",
41903         "972"
41904       ],
41905       [
41906         "Italy (Italia)",
41907         "it",
41908         "39",
41909         0
41910       ],
41911       [
41912         "Jamaica",
41913         "jm",
41914         "1876"
41915       ],
41916       [
41917         "Japan (日本)",
41918         "jp",
41919         "81"
41920       ],
41921       [
41922         "Jersey",
41923         "je",
41924         "44",
41925         3
41926       ],
41927       [
41928         "Jordan (‫الأردن‬‎)",
41929         "jo",
41930         "962"
41931       ],
41932       [
41933         "Kazakhstan (Казахстан)",
41934         "kz",
41935         "7",
41936         1
41937       ],
41938       [
41939         "Kenya",
41940         "ke",
41941         "254"
41942       ],
41943       [
41944         "Kiribati",
41945         "ki",
41946         "686"
41947       ],
41948       [
41949         "Kosovo",
41950         "xk",
41951         "383"
41952       ],
41953       [
41954         "Kuwait (‫الكويت‬‎)",
41955         "kw",
41956         "965"
41957       ],
41958       [
41959         "Kyrgyzstan (Кыргызстан)",
41960         "kg",
41961         "996"
41962       ],
41963       [
41964         "Laos (ລາວ)",
41965         "la",
41966         "856"
41967       ],
41968       [
41969         "Latvia (Latvija)",
41970         "lv",
41971         "371"
41972       ],
41973       [
41974         "Lebanon (‫لبنان‬‎)",
41975         "lb",
41976         "961"
41977       ],
41978       [
41979         "Lesotho",
41980         "ls",
41981         "266"
41982       ],
41983       [
41984         "Liberia",
41985         "lr",
41986         "231"
41987       ],
41988       [
41989         "Libya (‫ليبيا‬‎)",
41990         "ly",
41991         "218"
41992       ],
41993       [
41994         "Liechtenstein",
41995         "li",
41996         "423"
41997       ],
41998       [
41999         "Lithuania (Lietuva)",
42000         "lt",
42001         "370"
42002       ],
42003       [
42004         "Luxembourg",
42005         "lu",
42006         "352"
42007       ],
42008       [
42009         "Macau (澳門)",
42010         "mo",
42011         "853"
42012       ],
42013       [
42014         "Macedonia (FYROM) (Македонија)",
42015         "mk",
42016         "389"
42017       ],
42018       [
42019         "Madagascar (Madagasikara)",
42020         "mg",
42021         "261"
42022       ],
42023       [
42024         "Malawi",
42025         "mw",
42026         "265"
42027       ],
42028       [
42029         "Malaysia",
42030         "my",
42031         "60"
42032       ],
42033       [
42034         "Maldives",
42035         "mv",
42036         "960"
42037       ],
42038       [
42039         "Mali",
42040         "ml",
42041         "223"
42042       ],
42043       [
42044         "Malta",
42045         "mt",
42046         "356"
42047       ],
42048       [
42049         "Marshall Islands",
42050         "mh",
42051         "692"
42052       ],
42053       [
42054         "Martinique",
42055         "mq",
42056         "596"
42057       ],
42058       [
42059         "Mauritania (‫موريتانيا‬‎)",
42060         "mr",
42061         "222"
42062       ],
42063       [
42064         "Mauritius (Moris)",
42065         "mu",
42066         "230"
42067       ],
42068       [
42069         "Mayotte",
42070         "yt",
42071         "262",
42072         1
42073       ],
42074       [
42075         "Mexico (México)",
42076         "mx",
42077         "52"
42078       ],
42079       [
42080         "Micronesia",
42081         "fm",
42082         "691"
42083       ],
42084       [
42085         "Moldova (Republica Moldova)",
42086         "md",
42087         "373"
42088       ],
42089       [
42090         "Monaco",
42091         "mc",
42092         "377"
42093       ],
42094       [
42095         "Mongolia (Монгол)",
42096         "mn",
42097         "976"
42098       ],
42099       [
42100         "Montenegro (Crna Gora)",
42101         "me",
42102         "382"
42103       ],
42104       [
42105         "Montserrat",
42106         "ms",
42107         "1664"
42108       ],
42109       [
42110         "Morocco (‫المغرب‬‎)",
42111         "ma",
42112         "212",
42113         0
42114       ],
42115       [
42116         "Mozambique (Moçambique)",
42117         "mz",
42118         "258"
42119       ],
42120       [
42121         "Myanmar (Burma) (မြန်မာ)",
42122         "mm",
42123         "95"
42124       ],
42125       [
42126         "Namibia (Namibië)",
42127         "na",
42128         "264"
42129       ],
42130       [
42131         "Nauru",
42132         "nr",
42133         "674"
42134       ],
42135       [
42136         "Nepal (नेपाल)",
42137         "np",
42138         "977"
42139       ],
42140       [
42141         "Netherlands (Nederland)",
42142         "nl",
42143         "31"
42144       ],
42145       [
42146         "New Caledonia (Nouvelle-Calédonie)",
42147         "nc",
42148         "687"
42149       ],
42150       [
42151         "New Zealand",
42152         "nz",
42153         "64"
42154       ],
42155       [
42156         "Nicaragua",
42157         "ni",
42158         "505"
42159       ],
42160       [
42161         "Niger (Nijar)",
42162         "ne",
42163         "227"
42164       ],
42165       [
42166         "Nigeria",
42167         "ng",
42168         "234"
42169       ],
42170       [
42171         "Niue",
42172         "nu",
42173         "683"
42174       ],
42175       [
42176         "Norfolk Island",
42177         "nf",
42178         "672"
42179       ],
42180       [
42181         "North Korea (조선 민주주의 인민 공화국)",
42182         "kp",
42183         "850"
42184       ],
42185       [
42186         "Northern Mariana Islands",
42187         "mp",
42188         "1670"
42189       ],
42190       [
42191         "Norway (Norge)",
42192         "no",
42193         "47",
42194         0
42195       ],
42196       [
42197         "Oman (‫عُمان‬‎)",
42198         "om",
42199         "968"
42200       ],
42201       [
42202         "Pakistan (‫پاکستان‬‎)",
42203         "pk",
42204         "92"
42205       ],
42206       [
42207         "Palau",
42208         "pw",
42209         "680"
42210       ],
42211       [
42212         "Palestine (‫فلسطين‬‎)",
42213         "ps",
42214         "970"
42215       ],
42216       [
42217         "Panama (Panamá)",
42218         "pa",
42219         "507"
42220       ],
42221       [
42222         "Papua New Guinea",
42223         "pg",
42224         "675"
42225       ],
42226       [
42227         "Paraguay",
42228         "py",
42229         "595"
42230       ],
42231       [
42232         "Peru (Perú)",
42233         "pe",
42234         "51"
42235       ],
42236       [
42237         "Philippines",
42238         "ph",
42239         "63"
42240       ],
42241       [
42242         "Poland (Polska)",
42243         "pl",
42244         "48"
42245       ],
42246       [
42247         "Portugal",
42248         "pt",
42249         "351"
42250       ],
42251       [
42252         "Puerto Rico",
42253         "pr",
42254         "1",
42255         3,
42256         ["787", "939"]
42257       ],
42258       [
42259         "Qatar (‫قطر‬‎)",
42260         "qa",
42261         "974"
42262       ],
42263       [
42264         "Réunion (La Réunion)",
42265         "re",
42266         "262",
42267         0
42268       ],
42269       [
42270         "Romania (România)",
42271         "ro",
42272         "40"
42273       ],
42274       [
42275         "Russia (Россия)",
42276         "ru",
42277         "7",
42278         0
42279       ],
42280       [
42281         "Rwanda",
42282         "rw",
42283         "250"
42284       ],
42285       [
42286         "Saint Barthélemy",
42287         "bl",
42288         "590",
42289         1
42290       ],
42291       [
42292         "Saint Helena",
42293         "sh",
42294         "290"
42295       ],
42296       [
42297         "Saint Kitts and Nevis",
42298         "kn",
42299         "1869"
42300       ],
42301       [
42302         "Saint Lucia",
42303         "lc",
42304         "1758"
42305       ],
42306       [
42307         "Saint Martin (Saint-Martin (partie française))",
42308         "mf",
42309         "590",
42310         2
42311       ],
42312       [
42313         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42314         "pm",
42315         "508"
42316       ],
42317       [
42318         "Saint Vincent and the Grenadines",
42319         "vc",
42320         "1784"
42321       ],
42322       [
42323         "Samoa",
42324         "ws",
42325         "685"
42326       ],
42327       [
42328         "San Marino",
42329         "sm",
42330         "378"
42331       ],
42332       [
42333         "São Tomé and Príncipe (São Tomé e Príncipe)",
42334         "st",
42335         "239"
42336       ],
42337       [
42338         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42339         "sa",
42340         "966"
42341       ],
42342       [
42343         "Senegal (Sénégal)",
42344         "sn",
42345         "221"
42346       ],
42347       [
42348         "Serbia (Србија)",
42349         "rs",
42350         "381"
42351       ],
42352       [
42353         "Seychelles",
42354         "sc",
42355         "248"
42356       ],
42357       [
42358         "Sierra Leone",
42359         "sl",
42360         "232"
42361       ],
42362       [
42363         "Singapore",
42364         "sg",
42365         "65"
42366       ],
42367       [
42368         "Sint Maarten",
42369         "sx",
42370         "1721"
42371       ],
42372       [
42373         "Slovakia (Slovensko)",
42374         "sk",
42375         "421"
42376       ],
42377       [
42378         "Slovenia (Slovenija)",
42379         "si",
42380         "386"
42381       ],
42382       [
42383         "Solomon Islands",
42384         "sb",
42385         "677"
42386       ],
42387       [
42388         "Somalia (Soomaaliya)",
42389         "so",
42390         "252"
42391       ],
42392       [
42393         "South Africa",
42394         "za",
42395         "27"
42396       ],
42397       [
42398         "South Korea (대한민국)",
42399         "kr",
42400         "82"
42401       ],
42402       [
42403         "South Sudan (‫جنوب السودان‬‎)",
42404         "ss",
42405         "211"
42406       ],
42407       [
42408         "Spain (España)",
42409         "es",
42410         "34"
42411       ],
42412       [
42413         "Sri Lanka (ශ්‍රී ලංකාව)",
42414         "lk",
42415         "94"
42416       ],
42417       [
42418         "Sudan (‫السودان‬‎)",
42419         "sd",
42420         "249"
42421       ],
42422       [
42423         "Suriname",
42424         "sr",
42425         "597"
42426       ],
42427       [
42428         "Svalbard and Jan Mayen",
42429         "sj",
42430         "47",
42431         1
42432       ],
42433       [
42434         "Swaziland",
42435         "sz",
42436         "268"
42437       ],
42438       [
42439         "Sweden (Sverige)",
42440         "se",
42441         "46"
42442       ],
42443       [
42444         "Switzerland (Schweiz)",
42445         "ch",
42446         "41"
42447       ],
42448       [
42449         "Syria (‫سوريا‬‎)",
42450         "sy",
42451         "963"
42452       ],
42453       [
42454         "Taiwan (台灣)",
42455         "tw",
42456         "886"
42457       ],
42458       [
42459         "Tajikistan",
42460         "tj",
42461         "992"
42462       ],
42463       [
42464         "Tanzania",
42465         "tz",
42466         "255"
42467       ],
42468       [
42469         "Thailand (ไทย)",
42470         "th",
42471         "66"
42472       ],
42473       [
42474         "Timor-Leste",
42475         "tl",
42476         "670"
42477       ],
42478       [
42479         "Togo",
42480         "tg",
42481         "228"
42482       ],
42483       [
42484         "Tokelau",
42485         "tk",
42486         "690"
42487       ],
42488       [
42489         "Tonga",
42490         "to",
42491         "676"
42492       ],
42493       [
42494         "Trinidad and Tobago",
42495         "tt",
42496         "1868"
42497       ],
42498       [
42499         "Tunisia (‫تونس‬‎)",
42500         "tn",
42501         "216"
42502       ],
42503       [
42504         "Turkey (Türkiye)",
42505         "tr",
42506         "90"
42507       ],
42508       [
42509         "Turkmenistan",
42510         "tm",
42511         "993"
42512       ],
42513       [
42514         "Turks and Caicos Islands",
42515         "tc",
42516         "1649"
42517       ],
42518       [
42519         "Tuvalu",
42520         "tv",
42521         "688"
42522       ],
42523       [
42524         "U.S. Virgin Islands",
42525         "vi",
42526         "1340"
42527       ],
42528       [
42529         "Uganda",
42530         "ug",
42531         "256"
42532       ],
42533       [
42534         "Ukraine (Україна)",
42535         "ua",
42536         "380"
42537       ],
42538       [
42539         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42540         "ae",
42541         "971"
42542       ],
42543       [
42544         "United Kingdom",
42545         "gb",
42546         "44",
42547         0
42548       ],
42549       [
42550         "United States",
42551         "us",
42552         "1",
42553         0
42554       ],
42555       [
42556         "Uruguay",
42557         "uy",
42558         "598"
42559       ],
42560       [
42561         "Uzbekistan (Oʻzbekiston)",
42562         "uz",
42563         "998"
42564       ],
42565       [
42566         "Vanuatu",
42567         "vu",
42568         "678"
42569       ],
42570       [
42571         "Vatican City (Città del Vaticano)",
42572         "va",
42573         "39",
42574         1
42575       ],
42576       [
42577         "Venezuela",
42578         "ve",
42579         "58"
42580       ],
42581       [
42582         "Vietnam (Việt Nam)",
42583         "vn",
42584         "84"
42585       ],
42586       [
42587         "Wallis and Futuna (Wallis-et-Futuna)",
42588         "wf",
42589         "681"
42590       ],
42591       [
42592         "Western Sahara (‫الصحراء الغربية‬‎)",
42593         "eh",
42594         "212",
42595         1
42596       ],
42597       [
42598         "Yemen (‫اليمن‬‎)",
42599         "ye",
42600         "967"
42601       ],
42602       [
42603         "Zambia",
42604         "zm",
42605         "260"
42606       ],
42607       [
42608         "Zimbabwe",
42609         "zw",
42610         "263"
42611       ],
42612       [
42613         "Åland Islands",
42614         "ax",
42615         "358",
42616         1
42617       ]
42618   ];
42619   
42620   return d;
42621 }/**
42622 *    This script refer to:
42623 *    Title: International Telephone Input
42624 *    Author: Jack O'Connor
42625 *    Code version:  v12.1.12
42626 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42627 **/
42628
42629 /**
42630  * @class Roo.bootstrap.PhoneInput
42631  * @extends Roo.bootstrap.TriggerField
42632  * An input with International dial-code selection
42633  
42634  * @cfg {String} defaultDialCode default '+852'
42635  * @cfg {Array} preferedCountries default []
42636   
42637  * @constructor
42638  * Create a new PhoneInput.
42639  * @param {Object} config Configuration options
42640  */
42641
42642 Roo.bootstrap.PhoneInput = function(config) {
42643     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42644 };
42645
42646 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42647         
42648         listWidth: undefined,
42649         
42650         selectedClass: 'active',
42651         
42652         invalidClass : "has-warning",
42653         
42654         validClass: 'has-success',
42655         
42656         allowed: '0123456789',
42657         
42658         max_length: 15,
42659         
42660         /**
42661          * @cfg {String} defaultDialCode The default dial code when initializing the input
42662          */
42663         defaultDialCode: '+852',
42664         
42665         /**
42666          * @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
42667          */
42668         preferedCountries: false,
42669         
42670         getAutoCreate : function()
42671         {
42672             var data = Roo.bootstrap.PhoneInputData();
42673             var align = this.labelAlign || this.parentLabelAlign();
42674             var id = Roo.id();
42675             
42676             this.allCountries = [];
42677             this.dialCodeMapping = [];
42678             
42679             for (var i = 0; i < data.length; i++) {
42680               var c = data[i];
42681               this.allCountries[i] = {
42682                 name: c[0],
42683                 iso2: c[1],
42684                 dialCode: c[2],
42685                 priority: c[3] || 0,
42686                 areaCodes: c[4] || null
42687               };
42688               this.dialCodeMapping[c[2]] = {
42689                   name: c[0],
42690                   iso2: c[1],
42691                   priority: c[3] || 0,
42692                   areaCodes: c[4] || null
42693               };
42694             }
42695             
42696             var cfg = {
42697                 cls: 'form-group',
42698                 cn: []
42699             };
42700             
42701             var input =  {
42702                 tag: 'input',
42703                 id : id,
42704                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42705                 maxlength: this.max_length,
42706                 cls : 'form-control tel-input',
42707                 autocomplete: 'new-password'
42708             };
42709             
42710             var hiddenInput = {
42711                 tag: 'input',
42712                 type: 'hidden',
42713                 cls: 'hidden-tel-input'
42714             };
42715             
42716             if (this.name) {
42717                 hiddenInput.name = this.name;
42718             }
42719             
42720             if (this.disabled) {
42721                 input.disabled = true;
42722             }
42723             
42724             var flag_container = {
42725                 tag: 'div',
42726                 cls: 'flag-box',
42727                 cn: [
42728                     {
42729                         tag: 'div',
42730                         cls: 'flag'
42731                     },
42732                     {
42733                         tag: 'div',
42734                         cls: 'caret'
42735                     }
42736                 ]
42737             };
42738             
42739             var box = {
42740                 tag: 'div',
42741                 cls: this.hasFeedback ? 'has-feedback' : '',
42742                 cn: [
42743                     hiddenInput,
42744                     input,
42745                     {
42746                         tag: 'input',
42747                         cls: 'dial-code-holder',
42748                         disabled: true
42749                     }
42750                 ]
42751             };
42752             
42753             var container = {
42754                 cls: 'roo-select2-container input-group',
42755                 cn: [
42756                     flag_container,
42757                     box
42758                 ]
42759             };
42760             
42761             if (this.fieldLabel.length) {
42762                 var indicator = {
42763                     tag: 'i',
42764                     tooltip: 'This field is required'
42765                 };
42766                 
42767                 var label = {
42768                     tag: 'label',
42769                     'for':  id,
42770                     cls: 'control-label',
42771                     cn: []
42772                 };
42773                 
42774                 var label_text = {
42775                     tag: 'span',
42776                     html: this.fieldLabel
42777                 };
42778                 
42779                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42780                 label.cn = [
42781                     indicator,
42782                     label_text
42783                 ];
42784                 
42785                 if(this.indicatorpos == 'right') {
42786                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42787                     label.cn = [
42788                         label_text,
42789                         indicator
42790                     ];
42791                 }
42792                 
42793                 if(align == 'left') {
42794                     container = {
42795                         tag: 'div',
42796                         cn: [
42797                             container
42798                         ]
42799                     };
42800                     
42801                     if(this.labelWidth > 12){
42802                         label.style = "width: " + this.labelWidth + 'px';
42803                     }
42804                     if(this.labelWidth < 13 && this.labelmd == 0){
42805                         this.labelmd = this.labelWidth;
42806                     }
42807                     if(this.labellg > 0){
42808                         label.cls += ' col-lg-' + this.labellg;
42809                         input.cls += ' col-lg-' + (12 - this.labellg);
42810                     }
42811                     if(this.labelmd > 0){
42812                         label.cls += ' col-md-' + this.labelmd;
42813                         container.cls += ' col-md-' + (12 - this.labelmd);
42814                     }
42815                     if(this.labelsm > 0){
42816                         label.cls += ' col-sm-' + this.labelsm;
42817                         container.cls += ' col-sm-' + (12 - this.labelsm);
42818                     }
42819                     if(this.labelxs > 0){
42820                         label.cls += ' col-xs-' + this.labelxs;
42821                         container.cls += ' col-xs-' + (12 - this.labelxs);
42822                     }
42823                 }
42824             }
42825             
42826             cfg.cn = [
42827                 label,
42828                 container
42829             ];
42830             
42831             var settings = this;
42832             
42833             ['xs','sm','md','lg'].map(function(size){
42834                 if (settings[size]) {
42835                     cfg.cls += ' col-' + size + '-' + settings[size];
42836                 }
42837             });
42838             
42839             this.store = new Roo.data.Store({
42840                 proxy : new Roo.data.MemoryProxy({}),
42841                 reader : new Roo.data.JsonReader({
42842                     fields : [
42843                         {
42844                             'name' : 'name',
42845                             'type' : 'string'
42846                         },
42847                         {
42848                             'name' : 'iso2',
42849                             'type' : 'string'
42850                         },
42851                         {
42852                             'name' : 'dialCode',
42853                             'type' : 'string'
42854                         },
42855                         {
42856                             'name' : 'priority',
42857                             'type' : 'string'
42858                         },
42859                         {
42860                             'name' : 'areaCodes',
42861                             'type' : 'string'
42862                         }
42863                     ]
42864                 })
42865             });
42866             
42867             if(!this.preferedCountries) {
42868                 this.preferedCountries = [
42869                     'hk',
42870                     'gb',
42871                     'us'
42872                 ];
42873             }
42874             
42875             var p = this.preferedCountries.reverse();
42876             
42877             if(p) {
42878                 for (var i = 0; i < p.length; i++) {
42879                     for (var j = 0; j < this.allCountries.length; j++) {
42880                         if(this.allCountries[j].iso2 == p[i]) {
42881                             var t = this.allCountries[j];
42882                             this.allCountries.splice(j,1);
42883                             this.allCountries.unshift(t);
42884                         }
42885                     } 
42886                 }
42887             }
42888             
42889             this.store.proxy.data = {
42890                 success: true,
42891                 data: this.allCountries
42892             };
42893             
42894             return cfg;
42895         },
42896         
42897         initEvents : function()
42898         {
42899             this.createList();
42900             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42901             
42902             this.indicator = this.indicatorEl();
42903             this.flag = this.flagEl();
42904             this.dialCodeHolder = this.dialCodeHolderEl();
42905             
42906             this.trigger = this.el.select('div.flag-box',true).first();
42907             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42908             
42909             var _this = this;
42910             
42911             (function(){
42912                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42913                 _this.list.setWidth(lw);
42914             }).defer(100);
42915             
42916             this.list.on('mouseover', this.onViewOver, this);
42917             this.list.on('mousemove', this.onViewMove, this);
42918             this.inputEl().on("keyup", this.onKeyUp, this);
42919             this.inputEl().on("keypress", this.onKeyPress, this);
42920             
42921             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42922
42923             this.view = new Roo.View(this.list, this.tpl, {
42924                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42925             });
42926             
42927             this.view.on('click', this.onViewClick, this);
42928             this.setValue(this.defaultDialCode);
42929         },
42930         
42931         onTriggerClick : function(e)
42932         {
42933             Roo.log('trigger click');
42934             if(this.disabled){
42935                 return;
42936             }
42937             
42938             if(this.isExpanded()){
42939                 this.collapse();
42940                 this.hasFocus = false;
42941             }else {
42942                 this.store.load({});
42943                 this.hasFocus = true;
42944                 this.expand();
42945             }
42946         },
42947         
42948         isExpanded : function()
42949         {
42950             return this.list.isVisible();
42951         },
42952         
42953         collapse : function()
42954         {
42955             if(!this.isExpanded()){
42956                 return;
42957             }
42958             this.list.hide();
42959             Roo.get(document).un('mousedown', this.collapseIf, this);
42960             Roo.get(document).un('mousewheel', this.collapseIf, this);
42961             this.fireEvent('collapse', this);
42962             this.validate();
42963         },
42964         
42965         expand : function()
42966         {
42967             Roo.log('expand');
42968
42969             if(this.isExpanded() || !this.hasFocus){
42970                 return;
42971             }
42972             
42973             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42974             this.list.setWidth(lw);
42975             
42976             this.list.show();
42977             this.restrictHeight();
42978             
42979             Roo.get(document).on('mousedown', this.collapseIf, this);
42980             Roo.get(document).on('mousewheel', this.collapseIf, this);
42981             
42982             this.fireEvent('expand', this);
42983         },
42984         
42985         restrictHeight : function()
42986         {
42987             this.list.alignTo(this.inputEl(), this.listAlign);
42988             this.list.alignTo(this.inputEl(), this.listAlign);
42989         },
42990         
42991         onViewOver : function(e, t)
42992         {
42993             if(this.inKeyMode){
42994                 return;
42995             }
42996             var item = this.view.findItemFromChild(t);
42997             
42998             if(item){
42999                 var index = this.view.indexOf(item);
43000                 this.select(index, false);
43001             }
43002         },
43003
43004         // private
43005         onViewClick : function(view, doFocus, el, e)
43006         {
43007             var index = this.view.getSelectedIndexes()[0];
43008             
43009             var r = this.store.getAt(index);
43010             
43011             if(r){
43012                 this.onSelect(r, index);
43013             }
43014             if(doFocus !== false && !this.blockFocus){
43015                 this.inputEl().focus();
43016             }
43017         },
43018         
43019         onViewMove : function(e, t)
43020         {
43021             this.inKeyMode = false;
43022         },
43023         
43024         select : function(index, scrollIntoView)
43025         {
43026             this.selectedIndex = index;
43027             this.view.select(index);
43028             if(scrollIntoView !== false){
43029                 var el = this.view.getNode(index);
43030                 if(el){
43031                     this.list.scrollChildIntoView(el, false);
43032                 }
43033             }
43034         },
43035         
43036         createList : function()
43037         {
43038             this.list = Roo.get(document.body).createChild({
43039                 tag: 'ul',
43040                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43041                 style: 'display:none'
43042             });
43043             
43044             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43045         },
43046         
43047         collapseIf : function(e)
43048         {
43049             var in_combo  = e.within(this.el);
43050             var in_list =  e.within(this.list);
43051             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43052             
43053             if (in_combo || in_list || is_list) {
43054                 return;
43055             }
43056             this.collapse();
43057         },
43058         
43059         onSelect : function(record, index)
43060         {
43061             if(this.fireEvent('beforeselect', this, record, index) !== false){
43062                 
43063                 this.setFlagClass(record.data.iso2);
43064                 this.setDialCode(record.data.dialCode);
43065                 this.hasFocus = false;
43066                 this.collapse();
43067                 this.fireEvent('select', this, record, index);
43068             }
43069         },
43070         
43071         flagEl : function()
43072         {
43073             var flag = this.el.select('div.flag',true).first();
43074             if(!flag){
43075                 return false;
43076             }
43077             return flag;
43078         },
43079         
43080         dialCodeHolderEl : function()
43081         {
43082             var d = this.el.select('input.dial-code-holder',true).first();
43083             if(!d){
43084                 return false;
43085             }
43086             return d;
43087         },
43088         
43089         setDialCode : function(v)
43090         {
43091             this.dialCodeHolder.dom.value = '+'+v;
43092         },
43093         
43094         setFlagClass : function(n)
43095         {
43096             this.flag.dom.className = 'flag '+n;
43097         },
43098         
43099         getValue : function()
43100         {
43101             var v = this.inputEl().getValue();
43102             if(this.dialCodeHolder) {
43103                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43104             }
43105             return v;
43106         },
43107         
43108         setValue : function(v)
43109         {
43110             var d = this.getDialCode(v);
43111             
43112             //invalid dial code
43113             if(v.length == 0 || !d || d.length == 0) {
43114                 if(this.rendered){
43115                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43116                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43117                 }
43118                 return;
43119             }
43120             
43121             //valid dial code
43122             this.setFlagClass(this.dialCodeMapping[d].iso2);
43123             this.setDialCode(d);
43124             this.inputEl().dom.value = v.replace('+'+d,'');
43125             this.hiddenEl().dom.value = this.getValue();
43126             
43127             this.validate();
43128         },
43129         
43130         getDialCode : function(v)
43131         {
43132             v = v ||  '';
43133             
43134             if (v.length == 0) {
43135                 return this.dialCodeHolder.dom.value;
43136             }
43137             
43138             var dialCode = "";
43139             if (v.charAt(0) != "+") {
43140                 return false;
43141             }
43142             var numericChars = "";
43143             for (var i = 1; i < v.length; i++) {
43144               var c = v.charAt(i);
43145               if (!isNaN(c)) {
43146                 numericChars += c;
43147                 if (this.dialCodeMapping[numericChars]) {
43148                   dialCode = v.substr(1, i);
43149                 }
43150                 if (numericChars.length == 4) {
43151                   break;
43152                 }
43153               }
43154             }
43155             return dialCode;
43156         },
43157         
43158         reset : function()
43159         {
43160             this.setValue(this.defaultDialCode);
43161             this.validate();
43162         },
43163         
43164         hiddenEl : function()
43165         {
43166             return this.el.select('input.hidden-tel-input',true).first();
43167         },
43168         
43169         // after setting val
43170         onKeyUp : function(e){
43171             this.setValue(this.getValue());
43172         },
43173         
43174         onKeyPress : function(e){
43175             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43176                 e.stopEvent();
43177             }
43178         }
43179         
43180 });
43181 /**
43182  * @class Roo.bootstrap.MoneyField
43183  * @extends Roo.bootstrap.ComboBox
43184  * Bootstrap MoneyField class
43185  * 
43186  * @constructor
43187  * Create a new MoneyField.
43188  * @param {Object} config Configuration options
43189  */
43190
43191 Roo.bootstrap.MoneyField = function(config) {
43192     
43193     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43194     
43195 };
43196
43197 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43198     
43199     /**
43200      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43201      */
43202     allowDecimals : true,
43203     /**
43204      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43205      */
43206     decimalSeparator : ".",
43207     /**
43208      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43209      */
43210     decimalPrecision : 0,
43211     /**
43212      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43213      */
43214     allowNegative : true,
43215     /**
43216      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43217      */
43218     allowZero: true,
43219     /**
43220      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43221      */
43222     minValue : Number.NEGATIVE_INFINITY,
43223     /**
43224      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43225      */
43226     maxValue : Number.MAX_VALUE,
43227     /**
43228      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43229      */
43230     minText : "The minimum value for this field is {0}",
43231     /**
43232      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43233      */
43234     maxText : "The maximum value for this field is {0}",
43235     /**
43236      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43237      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43238      */
43239     nanText : "{0} is not a valid number",
43240     /**
43241      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43242      */
43243     castInt : true,
43244     /**
43245      * @cfg {String} defaults currency of the MoneyField
43246      * value should be in lkey
43247      */
43248     defaultCurrency : false,
43249     /**
43250      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43251      */
43252     thousandsDelimiter : false,
43253     /**
43254      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43255      */
43256     max_length: false,
43257     
43258     inputlg : 9,
43259     inputmd : 9,
43260     inputsm : 9,
43261     inputxs : 6,
43262     
43263     store : false,
43264     
43265     getAutoCreate : function()
43266     {
43267         var align = this.labelAlign || this.parentLabelAlign();
43268         
43269         var id = Roo.id();
43270
43271         var cfg = {
43272             cls: 'form-group',
43273             cn: []
43274         };
43275
43276         var input =  {
43277             tag: 'input',
43278             id : id,
43279             cls : 'form-control roo-money-amount-input',
43280             autocomplete: 'new-password'
43281         };
43282         
43283         var hiddenInput = {
43284             tag: 'input',
43285             type: 'hidden',
43286             id: Roo.id(),
43287             cls: 'hidden-number-input'
43288         };
43289         
43290         if(this.max_length) {
43291             input.maxlength = this.max_length; 
43292         }
43293         
43294         if (this.name) {
43295             hiddenInput.name = this.name;
43296         }
43297
43298         if (this.disabled) {
43299             input.disabled = true;
43300         }
43301
43302         var clg = 12 - this.inputlg;
43303         var cmd = 12 - this.inputmd;
43304         var csm = 12 - this.inputsm;
43305         var cxs = 12 - this.inputxs;
43306         
43307         var container = {
43308             tag : 'div',
43309             cls : 'row roo-money-field',
43310             cn : [
43311                 {
43312                     tag : 'div',
43313                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43314                     cn : [
43315                         {
43316                             tag : 'div',
43317                             cls: 'roo-select2-container input-group',
43318                             cn: [
43319                                 {
43320                                     tag : 'input',
43321                                     cls : 'form-control roo-money-currency-input',
43322                                     autocomplete: 'new-password',
43323                                     readOnly : 1,
43324                                     name : this.currencyName
43325                                 },
43326                                 {
43327                                     tag :'span',
43328                                     cls : 'input-group-addon',
43329                                     cn : [
43330                                         {
43331                                             tag: 'span',
43332                                             cls: 'caret'
43333                                         }
43334                                     ]
43335                                 }
43336                             ]
43337                         }
43338                     ]
43339                 },
43340                 {
43341                     tag : 'div',
43342                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43343                     cn : [
43344                         {
43345                             tag: 'div',
43346                             cls: this.hasFeedback ? 'has-feedback' : '',
43347                             cn: [
43348                                 input
43349                             ]
43350                         }
43351                     ]
43352                 }
43353             ]
43354             
43355         };
43356         
43357         if (this.fieldLabel.length) {
43358             var indicator = {
43359                 tag: 'i',
43360                 tooltip: 'This field is required'
43361             };
43362
43363             var label = {
43364                 tag: 'label',
43365                 'for':  id,
43366                 cls: 'control-label',
43367                 cn: []
43368             };
43369
43370             var label_text = {
43371                 tag: 'span',
43372                 html: this.fieldLabel
43373             };
43374
43375             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43376             label.cn = [
43377                 indicator,
43378                 label_text
43379             ];
43380
43381             if(this.indicatorpos == 'right') {
43382                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43383                 label.cn = [
43384                     label_text,
43385                     indicator
43386                 ];
43387             }
43388
43389             if(align == 'left') {
43390                 container = {
43391                     tag: 'div',
43392                     cn: [
43393                         container
43394                     ]
43395                 };
43396
43397                 if(this.labelWidth > 12){
43398                     label.style = "width: " + this.labelWidth + 'px';
43399                 }
43400                 if(this.labelWidth < 13 && this.labelmd == 0){
43401                     this.labelmd = this.labelWidth;
43402                 }
43403                 if(this.labellg > 0){
43404                     label.cls += ' col-lg-' + this.labellg;
43405                     input.cls += ' col-lg-' + (12 - this.labellg);
43406                 }
43407                 if(this.labelmd > 0){
43408                     label.cls += ' col-md-' + this.labelmd;
43409                     container.cls += ' col-md-' + (12 - this.labelmd);
43410                 }
43411                 if(this.labelsm > 0){
43412                     label.cls += ' col-sm-' + this.labelsm;
43413                     container.cls += ' col-sm-' + (12 - this.labelsm);
43414                 }
43415                 if(this.labelxs > 0){
43416                     label.cls += ' col-xs-' + this.labelxs;
43417                     container.cls += ' col-xs-' + (12 - this.labelxs);
43418                 }
43419             }
43420         }
43421
43422         cfg.cn = [
43423             label,
43424             container,
43425             hiddenInput
43426         ];
43427         
43428         var settings = this;
43429
43430         ['xs','sm','md','lg'].map(function(size){
43431             if (settings[size]) {
43432                 cfg.cls += ' col-' + size + '-' + settings[size];
43433             }
43434         });
43435         
43436         return cfg;
43437     },
43438     
43439     initEvents : function()
43440     {
43441         this.indicator = this.indicatorEl();
43442         
43443         this.initCurrencyEvent();
43444         
43445         this.initNumberEvent();
43446     },
43447     
43448     initCurrencyEvent : function()
43449     {
43450         if (!this.store) {
43451             throw "can not find store for combo";
43452         }
43453         
43454         this.store = Roo.factory(this.store, Roo.data);
43455         this.store.parent = this;
43456         
43457         this.createList();
43458         
43459         this.triggerEl = this.el.select('.input-group-addon', true).first();
43460         
43461         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43462         
43463         var _this = this;
43464         
43465         (function(){
43466             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43467             _this.list.setWidth(lw);
43468         }).defer(100);
43469         
43470         this.list.on('mouseover', this.onViewOver, this);
43471         this.list.on('mousemove', this.onViewMove, this);
43472         this.list.on('scroll', this.onViewScroll, this);
43473         
43474         if(!this.tpl){
43475             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43476         }
43477         
43478         this.view = new Roo.View(this.list, this.tpl, {
43479             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43480         });
43481         
43482         this.view.on('click', this.onViewClick, this);
43483         
43484         this.store.on('beforeload', this.onBeforeLoad, this);
43485         this.store.on('load', this.onLoad, this);
43486         this.store.on('loadexception', this.onLoadException, this);
43487         
43488         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43489             "up" : function(e){
43490                 this.inKeyMode = true;
43491                 this.selectPrev();
43492             },
43493
43494             "down" : function(e){
43495                 if(!this.isExpanded()){
43496                     this.onTriggerClick();
43497                 }else{
43498                     this.inKeyMode = true;
43499                     this.selectNext();
43500                 }
43501             },
43502
43503             "enter" : function(e){
43504                 this.collapse();
43505                 
43506                 if(this.fireEvent("specialkey", this, e)){
43507                     this.onViewClick(false);
43508                 }
43509                 
43510                 return true;
43511             },
43512
43513             "esc" : function(e){
43514                 this.collapse();
43515             },
43516
43517             "tab" : function(e){
43518                 this.collapse();
43519                 
43520                 if(this.fireEvent("specialkey", this, e)){
43521                     this.onViewClick(false);
43522                 }
43523                 
43524                 return true;
43525             },
43526
43527             scope : this,
43528
43529             doRelay : function(foo, bar, hname){
43530                 if(hname == 'down' || this.scope.isExpanded()){
43531                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43532                 }
43533                 return true;
43534             },
43535
43536             forceKeyDown: true
43537         });
43538         
43539         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43540         
43541     },
43542     
43543     initNumberEvent : function(e)
43544     {
43545         this.inputEl().on("keydown" , this.fireKey,  this);
43546         this.inputEl().on("focus", this.onFocus,  this);
43547         this.inputEl().on("blur", this.onBlur,  this);
43548         
43549         this.inputEl().relayEvent('keyup', this);
43550         
43551         if(this.indicator){
43552             this.indicator.addClass('invisible');
43553         }
43554  
43555         this.originalValue = this.getValue();
43556         
43557         if(this.validationEvent == 'keyup'){
43558             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43559             this.inputEl().on('keyup', this.filterValidation, this);
43560         }
43561         else if(this.validationEvent !== false){
43562             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43563         }
43564         
43565         if(this.selectOnFocus){
43566             this.on("focus", this.preFocus, this);
43567             
43568         }
43569         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43570             this.inputEl().on("keypress", this.filterKeys, this);
43571         } else {
43572             this.inputEl().relayEvent('keypress', this);
43573         }
43574         
43575         var allowed = "0123456789";
43576         
43577         if(this.allowDecimals){
43578             allowed += this.decimalSeparator;
43579         }
43580         
43581         if(this.allowNegative){
43582             allowed += "-";
43583         }
43584         
43585         if(this.thousandsDelimiter) {
43586             allowed += ",";
43587         }
43588         
43589         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43590         
43591         var keyPress = function(e){
43592             
43593             var k = e.getKey();
43594             
43595             var c = e.getCharCode();
43596             
43597             if(
43598                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43599                     allowed.indexOf(String.fromCharCode(c)) === -1
43600             ){
43601                 e.stopEvent();
43602                 return;
43603             }
43604             
43605             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43606                 return;
43607             }
43608             
43609             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43610                 e.stopEvent();
43611             }
43612         };
43613         
43614         this.inputEl().on("keypress", keyPress, this);
43615         
43616     },
43617     
43618     onTriggerClick : function(e)
43619     {   
43620         if(this.disabled){
43621             return;
43622         }
43623         
43624         this.page = 0;
43625         this.loadNext = false;
43626         
43627         if(this.isExpanded()){
43628             this.collapse();
43629             return;
43630         }
43631         
43632         this.hasFocus = true;
43633         
43634         if(this.triggerAction == 'all') {
43635             this.doQuery(this.allQuery, true);
43636             return;
43637         }
43638         
43639         this.doQuery(this.getRawValue());
43640     },
43641     
43642     getCurrency : function()
43643     {   
43644         var v = this.currencyEl().getValue();
43645         
43646         return v;
43647     },
43648     
43649     restrictHeight : function()
43650     {
43651         this.list.alignTo(this.currencyEl(), this.listAlign);
43652         this.list.alignTo(this.currencyEl(), this.listAlign);
43653     },
43654     
43655     onViewClick : function(view, doFocus, el, e)
43656     {
43657         var index = this.view.getSelectedIndexes()[0];
43658         
43659         var r = this.store.getAt(index);
43660         
43661         if(r){
43662             this.onSelect(r, index);
43663         }
43664     },
43665     
43666     onSelect : function(record, index){
43667         
43668         if(this.fireEvent('beforeselect', this, record, index) !== false){
43669         
43670             this.setFromCurrencyData(index > -1 ? record.data : false);
43671             
43672             this.collapse();
43673             
43674             this.fireEvent('select', this, record, index);
43675         }
43676     },
43677     
43678     setFromCurrencyData : function(o)
43679     {
43680         var currency = '';
43681         
43682         this.lastCurrency = o;
43683         
43684         if (this.currencyField) {
43685             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43686         } else {
43687             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43688         }
43689         
43690         this.lastSelectionText = currency;
43691         
43692         //setting default currency
43693         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43694             this.setCurrency(this.defaultCurrency);
43695             return;
43696         }
43697         
43698         this.setCurrency(currency);
43699     },
43700     
43701     setFromData : function(o)
43702     {
43703         var c = {};
43704         
43705         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43706         
43707         this.setFromCurrencyData(c);
43708         
43709         var value = '';
43710         
43711         if (this.name) {
43712             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43713         } else {
43714             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43715         }
43716         
43717         this.setValue(value);
43718         
43719     },
43720     
43721     setCurrency : function(v)
43722     {   
43723         this.currencyValue = v;
43724         
43725         if(this.rendered){
43726             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43727             this.validate();
43728         }
43729     },
43730     
43731     setValue : function(v)
43732     {
43733         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43734         
43735         this.value = v;
43736         
43737         if(this.rendered){
43738             
43739             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43740             
43741             this.inputEl().dom.value = (v == '') ? '' :
43742                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43743             
43744             if(!this.allowZero && v === '0') {
43745                 this.hiddenEl().dom.value = '';
43746                 this.inputEl().dom.value = '';
43747             }
43748             
43749             this.validate();
43750         }
43751     },
43752     
43753     getRawValue : function()
43754     {
43755         var v = this.inputEl().getValue();
43756         
43757         return v;
43758     },
43759     
43760     getValue : function()
43761     {
43762         return this.fixPrecision(this.parseValue(this.getRawValue()));
43763     },
43764     
43765     parseValue : function(value)
43766     {
43767         if(this.thousandsDelimiter) {
43768             value += "";
43769             r = new RegExp(",", "g");
43770             value = value.replace(r, "");
43771         }
43772         
43773         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43774         return isNaN(value) ? '' : value;
43775         
43776     },
43777     
43778     fixPrecision : function(value)
43779     {
43780         if(this.thousandsDelimiter) {
43781             value += "";
43782             r = new RegExp(",", "g");
43783             value = value.replace(r, "");
43784         }
43785         
43786         var nan = isNaN(value);
43787         
43788         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43789             return nan ? '' : value;
43790         }
43791         return parseFloat(value).toFixed(this.decimalPrecision);
43792     },
43793     
43794     decimalPrecisionFcn : function(v)
43795     {
43796         return Math.floor(v);
43797     },
43798     
43799     validateValue : function(value)
43800     {
43801         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43802             return false;
43803         }
43804         
43805         var num = this.parseValue(value);
43806         
43807         if(isNaN(num)){
43808             this.markInvalid(String.format(this.nanText, value));
43809             return false;
43810         }
43811         
43812         if(num < this.minValue){
43813             this.markInvalid(String.format(this.minText, this.minValue));
43814             return false;
43815         }
43816         
43817         if(num > this.maxValue){
43818             this.markInvalid(String.format(this.maxText, this.maxValue));
43819             return false;
43820         }
43821         
43822         return true;
43823     },
43824     
43825     validate : function()
43826     {
43827         if(this.disabled || this.allowBlank){
43828             this.markValid();
43829             return true;
43830         }
43831         
43832         var currency = this.getCurrency();
43833         
43834         if(this.validateValue(this.getRawValue()) && currency.length){
43835             this.markValid();
43836             return true;
43837         }
43838         
43839         this.markInvalid();
43840         return false;
43841     },
43842     
43843     getName: function()
43844     {
43845         return this.name;
43846     },
43847     
43848     beforeBlur : function()
43849     {
43850         if(!this.castInt){
43851             return;
43852         }
43853         
43854         var v = this.parseValue(this.getRawValue());
43855         
43856         if(v || v == 0){
43857             this.setValue(v);
43858         }
43859     },
43860     
43861     onBlur : function()
43862     {
43863         this.beforeBlur();
43864         
43865         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43866             //this.el.removeClass(this.focusClass);
43867         }
43868         
43869         this.hasFocus = false;
43870         
43871         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43872             this.validate();
43873         }
43874         
43875         var v = this.getValue();
43876         
43877         if(String(v) !== String(this.startValue)){
43878             this.fireEvent('change', this, v, this.startValue);
43879         }
43880         
43881         this.fireEvent("blur", this);
43882     },
43883     
43884     inputEl : function()
43885     {
43886         return this.el.select('.roo-money-amount-input', true).first();
43887     },
43888     
43889     currencyEl : function()
43890     {
43891         return this.el.select('.roo-money-currency-input', true).first();
43892     },
43893     
43894     hiddenEl : function()
43895     {
43896         return this.el.select('input.hidden-number-input',true).first();
43897     }
43898     
43899 });/**
43900  * @class Roo.bootstrap.BezierSignature
43901  * @extends Roo.bootstrap.Component
43902  * Bootstrap BezierSignature class
43903  * This script refer to:
43904  *    Title: Signature Pad
43905  *    Author: szimek
43906  *    Availability: https://github.com/szimek/signature_pad
43907  *
43908  * @constructor
43909  * Create a new BezierSignature
43910  * @param {Object} config The config object
43911  */
43912
43913 Roo.bootstrap.BezierSignature = function(config){
43914     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43915     this.addEvents({
43916         "resize" : true
43917     });
43918 };
43919
43920 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43921 {
43922      
43923     curve_data: [],
43924     
43925     is_empty: true,
43926     
43927     mouse_btn_down: true,
43928     
43929     /**
43930      * @cfg {int} canvas height
43931      */
43932     canvas_height: '200px',
43933     
43934     /**
43935      * @cfg {float|function} Radius of a single dot.
43936      */ 
43937     dot_size: false,
43938     
43939     /**
43940      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43941      */
43942     min_width: 0.5,
43943     
43944     /**
43945      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43946      */
43947     max_width: 2.5,
43948     
43949     /**
43950      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43951      */
43952     throttle: 16,
43953     
43954     /**
43955      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43956      */
43957     min_distance: 5,
43958     
43959     /**
43960      * @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.
43961      */
43962     bg_color: 'rgba(0, 0, 0, 0)',
43963     
43964     /**
43965      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43966      */
43967     dot_color: 'black',
43968     
43969     /**
43970      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43971      */ 
43972     velocity_filter_weight: 0.7,
43973     
43974     /**
43975      * @cfg {function} Callback when stroke begin. 
43976      */
43977     onBegin: false,
43978     
43979     /**
43980      * @cfg {function} Callback when stroke end.
43981      */
43982     onEnd: false,
43983     
43984     getAutoCreate : function()
43985     {
43986         var cls = 'roo-signature column';
43987         
43988         if(this.cls){
43989             cls += ' ' + this.cls;
43990         }
43991         
43992         var col_sizes = [
43993             'lg',
43994             'md',
43995             'sm',
43996             'xs'
43997         ];
43998         
43999         for(var i = 0; i < col_sizes.length; i++) {
44000             if(this[col_sizes[i]]) {
44001                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44002             }
44003         }
44004         
44005         var cfg = {
44006             tag: 'div',
44007             cls: cls,
44008             cn: [
44009                 {
44010                     tag: 'div',
44011                     cls: 'roo-signature-body',
44012                     cn: [
44013                         {
44014                             tag: 'canvas',
44015                             cls: 'roo-signature-body-canvas',
44016                             height: this.canvas_height,
44017                             width: this.canvas_width
44018                         }
44019                     ]
44020                 },
44021                 {
44022                     tag: 'input',
44023                     type: 'file',
44024                     style: 'display: none'
44025                 }
44026             ]
44027         };
44028         
44029         return cfg;
44030     },
44031     
44032     initEvents: function() 
44033     {
44034         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44035         
44036         var canvas = this.canvasEl();
44037         
44038         // mouse && touch event swapping...
44039         canvas.dom.style.touchAction = 'none';
44040         canvas.dom.style.msTouchAction = 'none';
44041         
44042         this.mouse_btn_down = false;
44043         canvas.on('mousedown', this._handleMouseDown, this);
44044         canvas.on('mousemove', this._handleMouseMove, this);
44045         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44046         
44047         if (window.PointerEvent) {
44048             canvas.on('pointerdown', this._handleMouseDown, this);
44049             canvas.on('pointermove', this._handleMouseMove, this);
44050             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44051         }
44052         
44053         if ('ontouchstart' in window) {
44054             canvas.on('touchstart', this._handleTouchStart, this);
44055             canvas.on('touchmove', this._handleTouchMove, this);
44056             canvas.on('touchend', this._handleTouchEnd, this);
44057         }
44058         
44059         Roo.EventManager.onWindowResize(this.resize, this, true);
44060         
44061         // file input event
44062         this.fileEl().on('change', this.uploadImage, this);
44063         
44064         this.clear();
44065         
44066         this.resize();
44067     },
44068     
44069     resize: function(){
44070         
44071         var canvas = this.canvasEl().dom;
44072         var ctx = this.canvasElCtx();
44073         var img_data = false;
44074         
44075         if(canvas.width > 0) {
44076             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44077         }
44078         // setting canvas width will clean img data
44079         canvas.width = 0;
44080         
44081         var style = window.getComputedStyle ? 
44082             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44083             
44084         var padding_left = parseInt(style.paddingLeft) || 0;
44085         var padding_right = parseInt(style.paddingRight) || 0;
44086         
44087         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44088         
44089         if(img_data) {
44090             ctx.putImageData(img_data, 0, 0);
44091         }
44092     },
44093     
44094     _handleMouseDown: function(e)
44095     {
44096         if (e.browserEvent.which === 1) {
44097             this.mouse_btn_down = true;
44098             this.strokeBegin(e);
44099         }
44100     },
44101     
44102     _handleMouseMove: function (e)
44103     {
44104         if (this.mouse_btn_down) {
44105             this.strokeMoveUpdate(e);
44106         }
44107     },
44108     
44109     _handleMouseUp: function (e)
44110     {
44111         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44112             this.mouse_btn_down = false;
44113             this.strokeEnd(e);
44114         }
44115     },
44116     
44117     _handleTouchStart: function (e) {
44118         
44119         e.preventDefault();
44120         if (e.browserEvent.targetTouches.length === 1) {
44121             // var touch = e.browserEvent.changedTouches[0];
44122             // this.strokeBegin(touch);
44123             
44124              this.strokeBegin(e); // assume e catching the correct xy...
44125         }
44126     },
44127     
44128     _handleTouchMove: function (e) {
44129         e.preventDefault();
44130         // var touch = event.targetTouches[0];
44131         // _this._strokeMoveUpdate(touch);
44132         this.strokeMoveUpdate(e);
44133     },
44134     
44135     _handleTouchEnd: function (e) {
44136         var wasCanvasTouched = e.target === this.canvasEl().dom;
44137         if (wasCanvasTouched) {
44138             e.preventDefault();
44139             // var touch = event.changedTouches[0];
44140             // _this._strokeEnd(touch);
44141             this.strokeEnd(e);
44142         }
44143     },
44144     
44145     reset: function () {
44146         this._lastPoints = [];
44147         this._lastVelocity = 0;
44148         this._lastWidth = (this.min_width + this.max_width) / 2;
44149         this.canvasElCtx().fillStyle = this.dot_color;
44150     },
44151     
44152     strokeMoveUpdate: function(e)
44153     {
44154         this.strokeUpdate(e);
44155         
44156         if (this.throttle) {
44157             this.throttleStroke(this.strokeUpdate, this.throttle);
44158         }
44159         else {
44160             this.strokeUpdate(e);
44161         }
44162     },
44163     
44164     strokeBegin: function(e)
44165     {
44166         var newPointGroup = {
44167             color: this.dot_color,
44168             points: []
44169         };
44170         
44171         if (typeof this.onBegin === 'function') {
44172             this.onBegin(e);
44173         }
44174         
44175         this.curve_data.push(newPointGroup);
44176         this.reset();
44177         this.strokeUpdate(e);
44178     },
44179     
44180     strokeUpdate: function(e)
44181     {
44182         var rect = this.canvasEl().dom.getBoundingClientRect();
44183         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44184         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44185         var lastPoints = lastPointGroup.points;
44186         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44187         var isLastPointTooClose = lastPoint
44188             ? point.distanceTo(lastPoint) <= this.min_distance
44189             : false;
44190         var color = lastPointGroup.color;
44191         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44192             var curve = this.addPoint(point);
44193             if (!lastPoint) {
44194                 this.drawDot({color: color, point: point});
44195             }
44196             else if (curve) {
44197                 this.drawCurve({color: color, curve: curve});
44198             }
44199             lastPoints.push({
44200                 time: point.time,
44201                 x: point.x,
44202                 y: point.y
44203             });
44204         }
44205     },
44206     
44207     strokeEnd: function(e)
44208     {
44209         this.strokeUpdate(e);
44210         if (typeof this.onEnd === 'function') {
44211             this.onEnd(e);
44212         }
44213     },
44214     
44215     addPoint:  function (point) {
44216         var _lastPoints = this._lastPoints;
44217         _lastPoints.push(point);
44218         if (_lastPoints.length > 2) {
44219             if (_lastPoints.length === 3) {
44220                 _lastPoints.unshift(_lastPoints[0]);
44221             }
44222             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44223             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44224             _lastPoints.shift();
44225             return curve;
44226         }
44227         return null;
44228     },
44229     
44230     calculateCurveWidths: function (startPoint, endPoint) {
44231         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44232             (1 - this.velocity_filter_weight) * this._lastVelocity;
44233
44234         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44235         var widths = {
44236             end: newWidth,
44237             start: this._lastWidth
44238         };
44239         
44240         this._lastVelocity = velocity;
44241         this._lastWidth = newWidth;
44242         return widths;
44243     },
44244     
44245     drawDot: function (_a) {
44246         var color = _a.color, point = _a.point;
44247         var ctx = this.canvasElCtx();
44248         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44249         ctx.beginPath();
44250         this.drawCurveSegment(point.x, point.y, width);
44251         ctx.closePath();
44252         ctx.fillStyle = color;
44253         ctx.fill();
44254     },
44255     
44256     drawCurve: function (_a) {
44257         var color = _a.color, curve = _a.curve;
44258         var ctx = this.canvasElCtx();
44259         var widthDelta = curve.endWidth - curve.startWidth;
44260         var drawSteps = Math.floor(curve.length()) * 2;
44261         ctx.beginPath();
44262         ctx.fillStyle = color;
44263         for (var i = 0; i < drawSteps; i += 1) {
44264         var t = i / drawSteps;
44265         var tt = t * t;
44266         var ttt = tt * t;
44267         var u = 1 - t;
44268         var uu = u * u;
44269         var uuu = uu * u;
44270         var x = uuu * curve.startPoint.x;
44271         x += 3 * uu * t * curve.control1.x;
44272         x += 3 * u * tt * curve.control2.x;
44273         x += ttt * curve.endPoint.x;
44274         var y = uuu * curve.startPoint.y;
44275         y += 3 * uu * t * curve.control1.y;
44276         y += 3 * u * tt * curve.control2.y;
44277         y += ttt * curve.endPoint.y;
44278         var width = curve.startWidth + ttt * widthDelta;
44279         this.drawCurveSegment(x, y, width);
44280         }
44281         ctx.closePath();
44282         ctx.fill();
44283     },
44284     
44285     drawCurveSegment: function (x, y, width) {
44286         var ctx = this.canvasElCtx();
44287         ctx.moveTo(x, y);
44288         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44289         this.is_empty = false;
44290     },
44291     
44292     clear: function()
44293     {
44294         var ctx = this.canvasElCtx();
44295         var canvas = this.canvasEl().dom;
44296         ctx.fillStyle = this.bg_color;
44297         ctx.clearRect(0, 0, canvas.width, canvas.height);
44298         ctx.fillRect(0, 0, canvas.width, canvas.height);
44299         this.curve_data = [];
44300         this.reset();
44301         this.is_empty = true;
44302     },
44303     
44304     fileEl: function()
44305     {
44306         return  this.el.select('input',true).first();
44307     },
44308     
44309     canvasEl: function()
44310     {
44311         return this.el.select('canvas',true).first();
44312     },
44313     
44314     canvasElCtx: function()
44315     {
44316         return this.el.select('canvas',true).first().dom.getContext('2d');
44317     },
44318     
44319     getImage: function(type)
44320     {
44321         if(this.is_empty) {
44322             return false;
44323         }
44324         
44325         // encryption ?
44326         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44327     },
44328     
44329     drawFromImage: function(img_src)
44330     {
44331         var img = new Image();
44332         
44333         img.onload = function(){
44334             this.canvasElCtx().drawImage(img, 0, 0);
44335         }.bind(this);
44336         
44337         img.src = img_src;
44338         
44339         this.is_empty = false;
44340     },
44341     
44342     selectImage: function()
44343     {
44344         this.fileEl().dom.click();
44345     },
44346     
44347     uploadImage: function(e)
44348     {
44349         var reader = new FileReader();
44350         
44351         reader.onload = function(e){
44352             var img = new Image();
44353             img.onload = function(){
44354                 this.reset();
44355                 this.canvasElCtx().drawImage(img, 0, 0);
44356             }.bind(this);
44357             img.src = e.target.result;
44358         }.bind(this);
44359         
44360         reader.readAsDataURL(e.target.files[0]);
44361     },
44362     
44363     // Bezier Point Constructor
44364     Point: (function () {
44365         function Point(x, y, time) {
44366             this.x = x;
44367             this.y = y;
44368             this.time = time || Date.now();
44369         }
44370         Point.prototype.distanceTo = function (start) {
44371             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44372         };
44373         Point.prototype.equals = function (other) {
44374             return this.x === other.x && this.y === other.y && this.time === other.time;
44375         };
44376         Point.prototype.velocityFrom = function (start) {
44377             return this.time !== start.time
44378             ? this.distanceTo(start) / (this.time - start.time)
44379             : 0;
44380         };
44381         return Point;
44382     }()),
44383     
44384     
44385     // Bezier Constructor
44386     Bezier: (function () {
44387         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44388             this.startPoint = startPoint;
44389             this.control2 = control2;
44390             this.control1 = control1;
44391             this.endPoint = endPoint;
44392             this.startWidth = startWidth;
44393             this.endWidth = endWidth;
44394         }
44395         Bezier.fromPoints = function (points, widths, scope) {
44396             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44397             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44398             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44399         };
44400         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44401             var dx1 = s1.x - s2.x;
44402             var dy1 = s1.y - s2.y;
44403             var dx2 = s2.x - s3.x;
44404             var dy2 = s2.y - s3.y;
44405             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44406             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44407             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44408             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44409             var dxm = m1.x - m2.x;
44410             var dym = m1.y - m2.y;
44411             var k = l2 / (l1 + l2);
44412             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44413             var tx = s2.x - cm.x;
44414             var ty = s2.y - cm.y;
44415             return {
44416                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44417                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44418             };
44419         };
44420         Bezier.prototype.length = function () {
44421             var steps = 10;
44422             var length = 0;
44423             var px;
44424             var py;
44425             for (var i = 0; i <= steps; i += 1) {
44426                 var t = i / steps;
44427                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44428                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44429                 if (i > 0) {
44430                     var xdiff = cx - px;
44431                     var ydiff = cy - py;
44432                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44433                 }
44434                 px = cx;
44435                 py = cy;
44436             }
44437             return length;
44438         };
44439         Bezier.prototype.point = function (t, start, c1, c2, end) {
44440             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44441             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44442             + (3.0 * c2 * (1.0 - t) * t * t)
44443             + (end * t * t * t);
44444         };
44445         return Bezier;
44446     }()),
44447     
44448     throttleStroke: function(fn, wait) {
44449       if (wait === void 0) { wait = 250; }
44450       var previous = 0;
44451       var timeout = null;
44452       var result;
44453       var storedContext;
44454       var storedArgs;
44455       var later = function () {
44456           previous = Date.now();
44457           timeout = null;
44458           result = fn.apply(storedContext, storedArgs);
44459           if (!timeout) {
44460               storedContext = null;
44461               storedArgs = [];
44462           }
44463       };
44464       return function wrapper() {
44465           var args = [];
44466           for (var _i = 0; _i < arguments.length; _i++) {
44467               args[_i] = arguments[_i];
44468           }
44469           var now = Date.now();
44470           var remaining = wait - (now - previous);
44471           storedContext = this;
44472           storedArgs = args;
44473           if (remaining <= 0 || remaining > wait) {
44474               if (timeout) {
44475                   clearTimeout(timeout);
44476                   timeout = null;
44477               }
44478               previous = now;
44479               result = fn.apply(storedContext, storedArgs);
44480               if (!timeout) {
44481                   storedContext = null;
44482                   storedArgs = [];
44483               }
44484           }
44485           else if (!timeout) {
44486               timeout = window.setTimeout(later, remaining);
44487           }
44488           return result;
44489       };
44490   }
44491   
44492 });
44493
44494  
44495
44496