Changed Roo/bootstrap/CardUploader.jsdocs/src/Roo_bootstrap_Card.js.htmlroojs-bootstr...
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.header = html;
2683         if (this.headerContainerEl) {
2684             this.headerContainerEl.dom.innerHTML = html;
2685         }
2686     }
2687
2688     
2689 });
2690
2691 /*
2692  * - LGPL
2693  *
2694  * Card header - holder for the card header elements.
2695  * 
2696  */
2697
2698 /**
2699  * @class Roo.bootstrap.CardHeader
2700  * @extends Roo.bootstrap.Element
2701  * Bootstrap CardHeader class
2702  * @constructor
2703  * Create a new Card Header - that you can embed children into
2704  * @param {Object} config The config object
2705  */
2706
2707 Roo.bootstrap.CardHeader = function(config){
2708     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2709 };
2710
2711 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2712     
2713     
2714     container_method : 'getCardHeader' 
2715     
2716      
2717     
2718     
2719    
2720 });
2721
2722  
2723
2724  /*
2725  * - LGPL
2726  *
2727  * Card footer - holder for the card footer elements.
2728  * 
2729  */
2730
2731 /**
2732  * @class Roo.bootstrap.CardFooter
2733  * @extends Roo.bootstrap.Element
2734  * Bootstrap CardFooter class
2735  * @constructor
2736  * Create a new Card Footer - that you can embed children into
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.CardFooter = function(config){
2741     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2745     
2746     
2747     container_method : 'getCardFooter' 
2748     
2749      
2750     
2751     
2752    
2753 });
2754
2755  
2756
2757  /*
2758  * - LGPL
2759  *
2760  * Card header - holder for the card header elements.
2761  * 
2762  */
2763
2764 /**
2765  * @class Roo.bootstrap.CardImageTop
2766  * @extends Roo.bootstrap.Element
2767  * Bootstrap CardImageTop class
2768  * @constructor
2769  * Create a new Card Image Top container
2770  * @param {Object} config The config object
2771  */
2772
2773 Roo.bootstrap.CardImageTop = function(config){
2774     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2775 };
2776
2777 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2778     
2779    
2780     container_method : 'getCardImageTop' 
2781     
2782      
2783     
2784    
2785 });
2786
2787  
2788
2789  /*
2790  * - LGPL
2791  *
2792  * image
2793  * 
2794  */
2795
2796
2797 /**
2798  * @class Roo.bootstrap.Img
2799  * @extends Roo.bootstrap.Component
2800  * Bootstrap Img class
2801  * @cfg {Boolean} imgResponsive false | true
2802  * @cfg {String} border rounded | circle | thumbnail
2803  * @cfg {String} src image source
2804  * @cfg {String} alt image alternative text
2805  * @cfg {String} href a tag href
2806  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2807  * @cfg {String} xsUrl xs image source
2808  * @cfg {String} smUrl sm image source
2809  * @cfg {String} mdUrl md image source
2810  * @cfg {String} lgUrl lg image source
2811  * 
2812  * @constructor
2813  * Create a new Input
2814  * @param {Object} config The config object
2815  */
2816
2817 Roo.bootstrap.Img = function(config){
2818     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2819     
2820     this.addEvents({
2821         // img events
2822         /**
2823          * @event click
2824          * The img click event for the img.
2825          * @param {Roo.EventObject} e
2826          */
2827         "click" : true
2828     });
2829 };
2830
2831 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2832     
2833     imgResponsive: true,
2834     border: '',
2835     src: 'about:blank',
2836     href: false,
2837     target: false,
2838     xsUrl: '',
2839     smUrl: '',
2840     mdUrl: '',
2841     lgUrl: '',
2842
2843     getAutoCreate : function()
2844     {   
2845         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2846             return this.createSingleImg();
2847         }
2848         
2849         var cfg = {
2850             tag: 'div',
2851             cls: 'roo-image-responsive-group',
2852             cn: []
2853         };
2854         var _this = this;
2855         
2856         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2857             
2858             if(!_this[size + 'Url']){
2859                 return;
2860             }
2861             
2862             var img = {
2863                 tag: 'img',
2864                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2865                 html: _this.html || cfg.html,
2866                 src: _this[size + 'Url']
2867             };
2868             
2869             img.cls += ' roo-image-responsive-' + size;
2870             
2871             var s = ['xs', 'sm', 'md', 'lg'];
2872             
2873             s.splice(s.indexOf(size), 1);
2874             
2875             Roo.each(s, function(ss){
2876                 img.cls += ' hidden-' + ss;
2877             });
2878             
2879             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2880                 cfg.cls += ' img-' + _this.border;
2881             }
2882             
2883             if(_this.alt){
2884                 cfg.alt = _this.alt;
2885             }
2886             
2887             if(_this.href){
2888                 var a = {
2889                     tag: 'a',
2890                     href: _this.href,
2891                     cn: [
2892                         img
2893                     ]
2894                 };
2895
2896                 if(this.target){
2897                     a.target = _this.target;
2898                 }
2899             }
2900             
2901             cfg.cn.push((_this.href) ? a : img);
2902             
2903         });
2904         
2905         return cfg;
2906     },
2907     
2908     createSingleImg : function()
2909     {
2910         var cfg = {
2911             tag: 'img',
2912             cls: (this.imgResponsive) ? 'img-responsive' : '',
2913             html : null,
2914             src : 'about:blank'  // just incase src get's set to undefined?!?
2915         };
2916         
2917         cfg.html = this.html || cfg.html;
2918         
2919         cfg.src = this.src || cfg.src;
2920         
2921         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2922             cfg.cls += ' img-' + this.border;
2923         }
2924         
2925         if(this.alt){
2926             cfg.alt = this.alt;
2927         }
2928         
2929         if(this.href){
2930             var a = {
2931                 tag: 'a',
2932                 href: this.href,
2933                 cn: [
2934                     cfg
2935                 ]
2936             };
2937             
2938             if(this.target){
2939                 a.target = this.target;
2940             }
2941             
2942         }
2943         
2944         return (this.href) ? a : cfg;
2945     },
2946     
2947     initEvents: function() 
2948     {
2949         if(!this.href){
2950             this.el.on('click', this.onClick, this);
2951         }
2952         
2953     },
2954     
2955     onClick : function(e)
2956     {
2957         Roo.log('img onclick');
2958         this.fireEvent('click', this, e);
2959     },
2960     /**
2961      * Sets the url of the image - used to update it
2962      * @param {String} url the url of the image
2963      */
2964     
2965     setSrc : function(url)
2966     {
2967         this.src =  url;
2968         
2969         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2970             this.el.dom.src =  url;
2971             return;
2972         }
2973         
2974         this.el.select('img', true).first().dom.src =  url;
2975     }
2976     
2977     
2978    
2979 });
2980
2981  /*
2982  * - LGPL
2983  *
2984  * image
2985  * 
2986  */
2987
2988
2989 /**
2990  * @class Roo.bootstrap.Link
2991  * @extends Roo.bootstrap.Component
2992  * Bootstrap Link Class
2993  * @cfg {String} alt image alternative text
2994  * @cfg {String} href a tag href
2995  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2996  * @cfg {String} html the content of the link.
2997  * @cfg {String} anchor name for the anchor link
2998  * @cfg {String} fa - favicon
2999
3000  * @cfg {Boolean} preventDefault (true | false) default false
3001
3002  * 
3003  * @constructor
3004  * Create a new Input
3005  * @param {Object} config The config object
3006  */
3007
3008 Roo.bootstrap.Link = function(config){
3009     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3010     
3011     this.addEvents({
3012         // img events
3013         /**
3014          * @event click
3015          * The img click event for the img.
3016          * @param {Roo.EventObject} e
3017          */
3018         "click" : true
3019     });
3020 };
3021
3022 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3023     
3024     href: false,
3025     target: false,
3026     preventDefault: false,
3027     anchor : false,
3028     alt : false,
3029     fa: false,
3030
3031
3032     getAutoCreate : function()
3033     {
3034         var html = this.html || '';
3035         
3036         if (this.fa !== false) {
3037             html = '<i class="fa fa-' + this.fa + '"></i>';
3038         }
3039         var cfg = {
3040             tag: 'a'
3041         };
3042         // anchor's do not require html/href...
3043         if (this.anchor === false) {
3044             cfg.html = html;
3045             cfg.href = this.href || '#';
3046         } else {
3047             cfg.name = this.anchor;
3048             if (this.html !== false || this.fa !== false) {
3049                 cfg.html = html;
3050             }
3051             if (this.href !== false) {
3052                 cfg.href = this.href;
3053             }
3054         }
3055         
3056         if(this.alt !== false){
3057             cfg.alt = this.alt;
3058         }
3059         
3060         
3061         if(this.target !== false) {
3062             cfg.target = this.target;
3063         }
3064         
3065         return cfg;
3066     },
3067     
3068     initEvents: function() {
3069         
3070         if(!this.href || this.preventDefault){
3071             this.el.on('click', this.onClick, this);
3072         }
3073     },
3074     
3075     onClick : function(e)
3076     {
3077         if(this.preventDefault){
3078             e.preventDefault();
3079         }
3080         //Roo.log('img onclick');
3081         this.fireEvent('click', this, e);
3082     }
3083    
3084 });
3085
3086  /*
3087  * - LGPL
3088  *
3089  * header
3090  * 
3091  */
3092
3093 /**
3094  * @class Roo.bootstrap.Header
3095  * @extends Roo.bootstrap.Component
3096  * Bootstrap Header class
3097  * @cfg {String} html content of header
3098  * @cfg {Number} level (1|2|3|4|5|6) default 1
3099  * 
3100  * @constructor
3101  * Create a new Header
3102  * @param {Object} config The config object
3103  */
3104
3105
3106 Roo.bootstrap.Header  = function(config){
3107     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3108 };
3109
3110 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3111     
3112     //href : false,
3113     html : false,
3114     level : 1,
3115     
3116     
3117     
3118     getAutoCreate : function(){
3119         
3120         
3121         
3122         var cfg = {
3123             tag: 'h' + (1 *this.level),
3124             html: this.html || ''
3125         } ;
3126         
3127         return cfg;
3128     }
3129    
3130 });
3131
3132  
3133
3134  /*
3135  * Based on:
3136  * Ext JS Library 1.1.1
3137  * Copyright(c) 2006-2007, Ext JS, LLC.
3138  *
3139  * Originally Released Under LGPL - original licence link has changed is not relivant.
3140  *
3141  * Fork - LGPL
3142  * <script type="text/javascript">
3143  */
3144  
3145 /**
3146  * @class Roo.bootstrap.MenuMgr
3147  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3148  * @singleton
3149  */
3150 Roo.bootstrap.MenuMgr = function(){
3151    var menus, active, groups = {}, attached = false, lastShow = new Date();
3152
3153    // private - called when first menu is created
3154    function init(){
3155        menus = {};
3156        active = new Roo.util.MixedCollection();
3157        Roo.get(document).addKeyListener(27, function(){
3158            if(active.length > 0){
3159                hideAll();
3160            }
3161        });
3162    }
3163
3164    // private
3165    function hideAll(){
3166        if(active && active.length > 0){
3167            var c = active.clone();
3168            c.each(function(m){
3169                m.hide();
3170            });
3171        }
3172    }
3173
3174    // private
3175    function onHide(m){
3176        active.remove(m);
3177        if(active.length < 1){
3178            Roo.get(document).un("mouseup", onMouseDown);
3179             
3180            attached = false;
3181        }
3182    }
3183
3184    // private
3185    function onShow(m){
3186        var last = active.last();
3187        lastShow = new Date();
3188        active.add(m);
3189        if(!attached){
3190           Roo.get(document).on("mouseup", onMouseDown);
3191            
3192            attached = true;
3193        }
3194        if(m.parentMenu){
3195           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3196           m.parentMenu.activeChild = m;
3197        }else if(last && last.isVisible()){
3198           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3199        }
3200    }
3201
3202    // private
3203    function onBeforeHide(m){
3204        if(m.activeChild){
3205            m.activeChild.hide();
3206        }
3207        if(m.autoHideTimer){
3208            clearTimeout(m.autoHideTimer);
3209            delete m.autoHideTimer;
3210        }
3211    }
3212
3213    // private
3214    function onBeforeShow(m){
3215        var pm = m.parentMenu;
3216        if(!pm && !m.allowOtherMenus){
3217            hideAll();
3218        }else if(pm && pm.activeChild && active != m){
3219            pm.activeChild.hide();
3220        }
3221    }
3222
3223    // private this should really trigger on mouseup..
3224    function onMouseDown(e){
3225         Roo.log("on Mouse Up");
3226         
3227         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3228             Roo.log("MenuManager hideAll");
3229             hideAll();
3230             e.stopEvent();
3231         }
3232         
3233         
3234    }
3235
3236    // private
3237    function onBeforeCheck(mi, state){
3238        if(state){
3239            var g = groups[mi.group];
3240            for(var i = 0, l = g.length; i < l; i++){
3241                if(g[i] != mi){
3242                    g[i].setChecked(false);
3243                }
3244            }
3245        }
3246    }
3247
3248    return {
3249
3250        /**
3251         * Hides all menus that are currently visible
3252         */
3253        hideAll : function(){
3254             hideAll();  
3255        },
3256
3257        // private
3258        register : function(menu){
3259            if(!menus){
3260                init();
3261            }
3262            menus[menu.id] = menu;
3263            menu.on("beforehide", onBeforeHide);
3264            menu.on("hide", onHide);
3265            menu.on("beforeshow", onBeforeShow);
3266            menu.on("show", onShow);
3267            var g = menu.group;
3268            if(g && menu.events["checkchange"]){
3269                if(!groups[g]){
3270                    groups[g] = [];
3271                }
3272                groups[g].push(menu);
3273                menu.on("checkchange", onCheck);
3274            }
3275        },
3276
3277         /**
3278          * Returns a {@link Roo.menu.Menu} object
3279          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3280          * be used to generate and return a new Menu instance.
3281          */
3282        get : function(menu){
3283            if(typeof menu == "string"){ // menu id
3284                return menus[menu];
3285            }else if(menu.events){  // menu instance
3286                return menu;
3287            }
3288            /*else if(typeof menu.length == 'number'){ // array of menu items?
3289                return new Roo.bootstrap.Menu({items:menu});
3290            }else{ // otherwise, must be a config
3291                return new Roo.bootstrap.Menu(menu);
3292            }
3293            */
3294            return false;
3295        },
3296
3297        // private
3298        unregister : function(menu){
3299            delete menus[menu.id];
3300            menu.un("beforehide", onBeforeHide);
3301            menu.un("hide", onHide);
3302            menu.un("beforeshow", onBeforeShow);
3303            menu.un("show", onShow);
3304            var g = menu.group;
3305            if(g && menu.events["checkchange"]){
3306                groups[g].remove(menu);
3307                menu.un("checkchange", onCheck);
3308            }
3309        },
3310
3311        // private
3312        registerCheckable : function(menuItem){
3313            var g = menuItem.group;
3314            if(g){
3315                if(!groups[g]){
3316                    groups[g] = [];
3317                }
3318                groups[g].push(menuItem);
3319                menuItem.on("beforecheckchange", onBeforeCheck);
3320            }
3321        },
3322
3323        // private
3324        unregisterCheckable : function(menuItem){
3325            var g = menuItem.group;
3326            if(g){
3327                groups[g].remove(menuItem);
3328                menuItem.un("beforecheckchange", onBeforeCheck);
3329            }
3330        }
3331    };
3332 }();/*
3333  * - LGPL
3334  *
3335  * menu
3336  * 
3337  */
3338
3339 /**
3340  * @class Roo.bootstrap.Menu
3341  * @extends Roo.bootstrap.Component
3342  * Bootstrap Menu class - container for MenuItems
3343  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3344  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3345  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3346  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3347  * 
3348  * @constructor
3349  * Create a new Menu
3350  * @param {Object} config The config object
3351  */
3352
3353
3354 Roo.bootstrap.Menu = function(config){
3355     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3356     if (this.registerMenu && this.type != 'treeview')  {
3357         Roo.bootstrap.MenuMgr.register(this);
3358     }
3359     
3360     
3361     this.addEvents({
3362         /**
3363          * @event beforeshow
3364          * Fires before this menu is displayed (return false to block)
3365          * @param {Roo.menu.Menu} this
3366          */
3367         beforeshow : true,
3368         /**
3369          * @event beforehide
3370          * Fires before this menu is hidden (return false to block)
3371          * @param {Roo.menu.Menu} this
3372          */
3373         beforehide : true,
3374         /**
3375          * @event show
3376          * Fires after this menu is displayed
3377          * @param {Roo.menu.Menu} this
3378          */
3379         show : true,
3380         /**
3381          * @event hide
3382          * Fires after this menu is hidden
3383          * @param {Roo.menu.Menu} this
3384          */
3385         hide : true,
3386         /**
3387          * @event click
3388          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3389          * @param {Roo.menu.Menu} this
3390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391          * @param {Roo.EventObject} e
3392          */
3393         click : true,
3394         /**
3395          * @event mouseover
3396          * Fires when the mouse is hovering over this menu
3397          * @param {Roo.menu.Menu} this
3398          * @param {Roo.EventObject} e
3399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3400          */
3401         mouseover : true,
3402         /**
3403          * @event mouseout
3404          * Fires when the mouse exits this menu
3405          * @param {Roo.menu.Menu} this
3406          * @param {Roo.EventObject} e
3407          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3408          */
3409         mouseout : true,
3410         /**
3411          * @event itemclick
3412          * Fires when a menu item contained in this menu is clicked
3413          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3414          * @param {Roo.EventObject} e
3415          */
3416         itemclick: true
3417     });
3418     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3419 };
3420
3421 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3422     
3423    /// html : false,
3424     //align : '',
3425     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3426     type: false,
3427     /**
3428      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3429      */
3430     registerMenu : true,
3431     
3432     menuItems :false, // stores the menu items..
3433     
3434     hidden:true,
3435         
3436     parentMenu : false,
3437     
3438     stopEvent : true,
3439     
3440     isLink : false,
3441     
3442     getChildContainer : function() {
3443         return this.el;  
3444     },
3445     
3446     getAutoCreate : function(){
3447          
3448         //if (['right'].indexOf(this.align)!==-1) {
3449         //    cfg.cn[1].cls += ' pull-right'
3450         //}
3451         
3452         
3453         var cfg = {
3454             tag : 'ul',
3455             cls : 'dropdown-menu' ,
3456             style : 'z-index:1000'
3457             
3458         };
3459         
3460         if (this.type === 'submenu') {
3461             cfg.cls = 'submenu active';
3462         }
3463         if (this.type === 'treeview') {
3464             cfg.cls = 'treeview-menu';
3465         }
3466         
3467         return cfg;
3468     },
3469     initEvents : function() {
3470         
3471        // Roo.log("ADD event");
3472        // Roo.log(this.triggerEl.dom);
3473         
3474         this.triggerEl.on('click', this.onTriggerClick, this);
3475         
3476         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3477         
3478         
3479         if (this.triggerEl.hasClass('nav-item')) {
3480             // dropdown toggle on the 'a' in BS4?
3481             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3482         } else {
3483             this.triggerEl.addClass('dropdown-toggle');
3484         }
3485         if (Roo.isTouch) {
3486             this.el.on('touchstart'  , this.onTouch, this);
3487         }
3488         this.el.on('click' , this.onClick, this);
3489
3490         this.el.on("mouseover", this.onMouseOver, this);
3491         this.el.on("mouseout", this.onMouseOut, this);
3492         
3493     },
3494     
3495     findTargetItem : function(e)
3496     {
3497         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3498         if(!t){
3499             return false;
3500         }
3501         //Roo.log(t);         Roo.log(t.id);
3502         if(t && t.id){
3503             //Roo.log(this.menuitems);
3504             return this.menuitems.get(t.id);
3505             
3506             //return this.items.get(t.menuItemId);
3507         }
3508         
3509         return false;
3510     },
3511     
3512     onTouch : function(e) 
3513     {
3514         Roo.log("menu.onTouch");
3515         //e.stopEvent(); this make the user popdown broken
3516         this.onClick(e);
3517     },
3518     
3519     onClick : function(e)
3520     {
3521         Roo.log("menu.onClick");
3522         
3523         var t = this.findTargetItem(e);
3524         if(!t || t.isContainer){
3525             return;
3526         }
3527         Roo.log(e);
3528         /*
3529         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3530             if(t == this.activeItem && t.shouldDeactivate(e)){
3531                 this.activeItem.deactivate();
3532                 delete this.activeItem;
3533                 return;
3534             }
3535             if(t.canActivate){
3536                 this.setActiveItem(t, true);
3537             }
3538             return;
3539             
3540             
3541         }
3542         */
3543        
3544         Roo.log('pass click event');
3545         
3546         t.onClick(e);
3547         
3548         this.fireEvent("click", this, t, e);
3549         
3550         var _this = this;
3551         
3552         if(!t.href.length || t.href == '#'){
3553             (function() { _this.hide(); }).defer(100);
3554         }
3555         
3556     },
3557     
3558     onMouseOver : function(e){
3559         var t  = this.findTargetItem(e);
3560         //Roo.log(t);
3561         //if(t){
3562         //    if(t.canActivate && !t.disabled){
3563         //        this.setActiveItem(t, true);
3564         //    }
3565         //}
3566         
3567         this.fireEvent("mouseover", this, e, t);
3568     },
3569     isVisible : function(){
3570         return !this.hidden;
3571     },
3572     onMouseOut : function(e){
3573         var t  = this.findTargetItem(e);
3574         
3575         //if(t ){
3576         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3577         //        this.activeItem.deactivate();
3578         //        delete this.activeItem;
3579         //    }
3580         //}
3581         this.fireEvent("mouseout", this, e, t);
3582     },
3583     
3584     
3585     /**
3586      * Displays this menu relative to another element
3587      * @param {String/HTMLElement/Roo.Element} element The element to align to
3588      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3589      * the element (defaults to this.defaultAlign)
3590      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3591      */
3592     show : function(el, pos, parentMenu)
3593     {
3594         if (false === this.fireEvent("beforeshow", this)) {
3595             Roo.log("show canceled");
3596             return;
3597         }
3598         this.parentMenu = parentMenu;
3599         if(!this.el){
3600             this.render();
3601         }
3602         
3603         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3604     },
3605      /**
3606      * Displays this menu at a specific xy position
3607      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3608      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3609      */
3610     showAt : function(xy, parentMenu, /* private: */_e){
3611         this.parentMenu = parentMenu;
3612         if(!this.el){
3613             this.render();
3614         }
3615         if(_e !== false){
3616             this.fireEvent("beforeshow", this);
3617             //xy = this.el.adjustForConstraints(xy);
3618         }
3619         
3620         //this.el.show();
3621         this.hideMenuItems();
3622         this.hidden = false;
3623         this.triggerEl.addClass('open');
3624         this.el.addClass('show');
3625         
3626         // reassign x when hitting right
3627         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3628             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3629         }
3630         
3631         // reassign y when hitting bottom
3632         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3633             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3634         }
3635         
3636         // but the list may align on trigger left or trigger top... should it be a properity?
3637         
3638         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3639             this.el.setXY(xy);
3640         }
3641         
3642         this.focus();
3643         this.fireEvent("show", this);
3644     },
3645     
3646     focus : function(){
3647         return;
3648         if(!this.hidden){
3649             this.doFocus.defer(50, this);
3650         }
3651     },
3652
3653     doFocus : function(){
3654         if(!this.hidden){
3655             this.focusEl.focus();
3656         }
3657     },
3658
3659     /**
3660      * Hides this menu and optionally all parent menus
3661      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3662      */
3663     hide : function(deep)
3664     {
3665         if (false === this.fireEvent("beforehide", this)) {
3666             Roo.log("hide canceled");
3667             return;
3668         }
3669         this.hideMenuItems();
3670         if(this.el && this.isVisible()){
3671            
3672             if(this.activeItem){
3673                 this.activeItem.deactivate();
3674                 this.activeItem = null;
3675             }
3676             this.triggerEl.removeClass('open');;
3677             this.el.removeClass('show');
3678             this.hidden = true;
3679             this.fireEvent("hide", this);
3680         }
3681         if(deep === true && this.parentMenu){
3682             this.parentMenu.hide(true);
3683         }
3684     },
3685     
3686     onTriggerClick : function(e)
3687     {
3688         Roo.log('trigger click');
3689         
3690         var target = e.getTarget();
3691         
3692         Roo.log(target.nodeName.toLowerCase());
3693         
3694         if(target.nodeName.toLowerCase() === 'i'){
3695             e.preventDefault();
3696         }
3697         
3698     },
3699     
3700     onTriggerPress  : function(e)
3701     {
3702         Roo.log('trigger press');
3703         //Roo.log(e.getTarget());
3704        // Roo.log(this.triggerEl.dom);
3705        
3706         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3707         var pel = Roo.get(e.getTarget());
3708         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3709             Roo.log('is treeview or dropdown?');
3710             return;
3711         }
3712         
3713         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714             return;
3715         }
3716         
3717         if (this.isVisible()) {
3718             Roo.log('hide');
3719             this.hide();
3720         } else {
3721             Roo.log('show');
3722             this.show(this.triggerEl, '?', false);
3723         }
3724         
3725         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3726             e.stopEvent();
3727         }
3728         
3729     },
3730        
3731     
3732     hideMenuItems : function()
3733     {
3734         Roo.log("hide Menu Items");
3735         if (!this.el) { 
3736             return;
3737         }
3738         
3739         this.el.select('.open',true).each(function(aa) {
3740             
3741             aa.removeClass('open');
3742          
3743         });
3744     },
3745     addxtypeChild : function (tree, cntr) {
3746         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3747           
3748         this.menuitems.add(comp);
3749         return comp;
3750
3751     },
3752     getEl : function()
3753     {
3754         Roo.log(this.el);
3755         return this.el;
3756     },
3757     
3758     clear : function()
3759     {
3760         this.getEl().dom.innerHTML = '';
3761         this.menuitems.clear();
3762     }
3763 });
3764
3765  
3766  /*
3767  * - LGPL
3768  *
3769  * menu item
3770  * 
3771  */
3772
3773
3774 /**
3775  * @class Roo.bootstrap.MenuItem
3776  * @extends Roo.bootstrap.Component
3777  * Bootstrap MenuItem class
3778  * @cfg {String} html the menu label
3779  * @cfg {String} href the link
3780  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3781  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3782  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3783  * @cfg {String} fa favicon to show on left of menu item.
3784  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785  * 
3786  * 
3787  * @constructor
3788  * Create a new MenuItem
3789  * @param {Object} config The config object
3790  */
3791
3792
3793 Roo.bootstrap.MenuItem = function(config){
3794     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3795     this.addEvents({
3796         // raw events
3797         /**
3798          * @event click
3799          * The raw click event for the entire grid.
3800          * @param {Roo.bootstrap.MenuItem} this
3801          * @param {Roo.EventObject} e
3802          */
3803         "click" : true
3804     });
3805 };
3806
3807 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3808     
3809     href : false,
3810     html : false,
3811     preventDefault: false,
3812     isContainer : false,
3813     active : false,
3814     fa: false,
3815     
3816     getAutoCreate : function(){
3817         
3818         if(this.isContainer){
3819             return {
3820                 tag: 'li',
3821                 cls: 'dropdown-menu-item '
3822             };
3823         }
3824         var ctag = {
3825             tag: 'span',
3826             html: 'Link'
3827         };
3828         
3829         var anc = {
3830             tag : 'a',
3831             cls : 'dropdown-item',
3832             href : '#',
3833             cn : [  ]
3834         };
3835         
3836         if (this.fa !== false) {
3837             anc.cn.push({
3838                 tag : 'i',
3839                 cls : 'fa fa-' + this.fa
3840             });
3841         }
3842         
3843         anc.cn.push(ctag);
3844         
3845         
3846         var cfg= {
3847             tag: 'li',
3848             cls: 'dropdown-menu-item',
3849             cn: [ anc ]
3850         };
3851         if (this.parent().type == 'treeview') {
3852             cfg.cls = 'treeview-menu';
3853         }
3854         if (this.active) {
3855             cfg.cls += ' active';
3856         }
3857         
3858         
3859         
3860         anc.href = this.href || cfg.cn[0].href ;
3861         ctag.html = this.html || cfg.cn[0].html ;
3862         return cfg;
3863     },
3864     
3865     initEvents: function()
3866     {
3867         if (this.parent().type == 'treeview') {
3868             this.el.select('a').on('click', this.onClick, this);
3869         }
3870         
3871         if (this.menu) {
3872             this.menu.parentType = this.xtype;
3873             this.menu.triggerEl = this.el;
3874             this.menu = this.addxtype(Roo.apply({}, this.menu));
3875         }
3876         
3877     },
3878     onClick : function(e)
3879     {
3880         Roo.log('item on click ');
3881         
3882         if(this.preventDefault){
3883             e.preventDefault();
3884         }
3885         //this.parent().hideMenuItems();
3886         
3887         this.fireEvent('click', this, e);
3888     },
3889     getEl : function()
3890     {
3891         return this.el;
3892     } 
3893 });
3894
3895  
3896
3897  /*
3898  * - LGPL
3899  *
3900  * menu separator
3901  * 
3902  */
3903
3904
3905 /**
3906  * @class Roo.bootstrap.MenuSeparator
3907  * @extends Roo.bootstrap.Component
3908  * Bootstrap MenuSeparator class
3909  * 
3910  * @constructor
3911  * Create a new MenuItem
3912  * @param {Object} config The config object
3913  */
3914
3915
3916 Roo.bootstrap.MenuSeparator = function(config){
3917     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3918 };
3919
3920 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3921     
3922     getAutoCreate : function(){
3923         var cfg = {
3924             cls: 'divider',
3925             tag : 'li'
3926         };
3927         
3928         return cfg;
3929     }
3930    
3931 });
3932
3933  
3934
3935  
3936 /*
3937 * Licence: LGPL
3938 */
3939
3940 /**
3941  * @class Roo.bootstrap.Modal
3942  * @extends Roo.bootstrap.Component
3943  * Bootstrap Modal class
3944  * @cfg {String} title Title of dialog
3945  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3946  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3947  * @cfg {Boolean} specificTitle default false
3948  * @cfg {Array} buttons Array of buttons or standard button set..
3949  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3950  * @cfg {Boolean} animate default true
3951  * @cfg {Boolean} allow_close default true
3952  * @cfg {Boolean} fitwindow default false
3953  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3954  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3955  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3956  * @cfg {String} size (sm|lg|xl) default empty
3957  * @cfg {Number} max_width set the max width of modal
3958  * @cfg {Boolean} editableTitle can the title be edited
3959
3960  *
3961  *
3962  * @constructor
3963  * Create a new Modal Dialog
3964  * @param {Object} config The config object
3965  */
3966
3967 Roo.bootstrap.Modal = function(config){
3968     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3969     this.addEvents({
3970         // raw events
3971         /**
3972          * @event btnclick
3973          * The raw btnclick event for the button
3974          * @param {Roo.EventObject} e
3975          */
3976         "btnclick" : true,
3977         /**
3978          * @event resize
3979          * Fire when dialog resize
3980          * @param {Roo.bootstrap.Modal} this
3981          * @param {Roo.EventObject} e
3982          */
3983         "resize" : true,
3984         /**
3985          * @event titlechanged
3986          * Fire when the editable title has been changed
3987          * @param {Roo.bootstrap.Modal} this
3988          * @param {Roo.EventObject} value
3989          */
3990         "titlechanged" : true 
3991         
3992     });
3993     this.buttons = this.buttons || [];
3994
3995     if (this.tmpl) {
3996         this.tmpl = Roo.factory(this.tmpl);
3997     }
3998
3999 };
4000
4001 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4002
4003     title : 'test dialog',
4004
4005     buttons : false,
4006
4007     // set on load...
4008
4009     html: false,
4010
4011     tmp: false,
4012
4013     specificTitle: false,
4014
4015     buttonPosition: 'right',
4016
4017     allow_close : true,
4018
4019     animate : true,
4020
4021     fitwindow: false,
4022     
4023      // private
4024     dialogEl: false,
4025     bodyEl:  false,
4026     footerEl:  false,
4027     titleEl:  false,
4028     closeEl:  false,
4029
4030     size: '',
4031     
4032     max_width: 0,
4033     
4034     max_height: 0,
4035     
4036     fit_content: false,
4037     editableTitle  : false,
4038
4039     onRender : function(ct, position)
4040     {
4041         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4042
4043         if(!this.el){
4044             var cfg = Roo.apply({},  this.getAutoCreate());
4045             cfg.id = Roo.id();
4046             //if(!cfg.name){
4047             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4048             //}
4049             //if (!cfg.name.length) {
4050             //    delete cfg.name;
4051            // }
4052             if (this.cls) {
4053                 cfg.cls += ' ' + this.cls;
4054             }
4055             if (this.style) {
4056                 cfg.style = this.style;
4057             }
4058             this.el = Roo.get(document.body).createChild(cfg, position);
4059         }
4060         //var type = this.el.dom.type;
4061
4062
4063         if(this.tabIndex !== undefined){
4064             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4065         }
4066
4067         this.dialogEl = this.el.select('.modal-dialog',true).first();
4068         this.bodyEl = this.el.select('.modal-body',true).first();
4069         this.closeEl = this.el.select('.modal-header .close', true).first();
4070         this.headerEl = this.el.select('.modal-header',true).first();
4071         this.titleEl = this.el.select('.modal-title',true).first();
4072         this.footerEl = this.el.select('.modal-footer',true).first();
4073
4074         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4075         
4076         //this.el.addClass("x-dlg-modal");
4077
4078         if (this.buttons.length) {
4079             Roo.each(this.buttons, function(bb) {
4080                 var b = Roo.apply({}, bb);
4081                 b.xns = b.xns || Roo.bootstrap;
4082                 b.xtype = b.xtype || 'Button';
4083                 if (typeof(b.listeners) == 'undefined') {
4084                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4085                 }
4086
4087                 var btn = Roo.factory(b);
4088
4089                 btn.render(this.getButtonContainer());
4090
4091             },this);
4092         }
4093         // render the children.
4094         var nitems = [];
4095
4096         if(typeof(this.items) != 'undefined'){
4097             var items = this.items;
4098             delete this.items;
4099
4100             for(var i =0;i < items.length;i++) {
4101                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4102             }
4103         }
4104
4105         this.items = nitems;
4106
4107         // where are these used - they used to be body/close/footer
4108
4109
4110         this.initEvents();
4111         //this.el.addClass([this.fieldClass, this.cls]);
4112
4113     },
4114
4115     getAutoCreate : function()
4116     {
4117         // we will default to modal-body-overflow - might need to remove or make optional later.
4118         var bdy = {
4119                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4120                 html : this.html || ''
4121         };
4122
4123         var title = {
4124             tag: 'h5',
4125             cls : 'modal-title',
4126             html : this.title
4127         };
4128
4129         if(this.specificTitle){ // WTF is this?
4130             title = this.title;
4131         }
4132
4133         var header = [];
4134         if (this.allow_close && Roo.bootstrap.version == 3) {
4135             header.push({
4136                 tag: 'button',
4137                 cls : 'close',
4138                 html : '&times'
4139             });
4140         }
4141
4142         header.push(title);
4143
4144         if (this.editableTitle) {
4145             header.push({
4146                 cls: 'form-control roo-editable-title d-none',
4147                 tag: 'input',
4148                 type: 'text'
4149             });
4150         }
4151         
4152         if (this.allow_close && Roo.bootstrap.version == 4) {
4153             header.push({
4154                 tag: 'button',
4155                 cls : 'close',
4156                 html : '&times'
4157             });
4158         }
4159         
4160         var size = '';
4161
4162         if(this.size.length){
4163             size = 'modal-' + this.size;
4164         }
4165         
4166         var footer = Roo.bootstrap.version == 3 ?
4167             {
4168                 cls : 'modal-footer',
4169                 cn : [
4170                     {
4171                         tag: 'div',
4172                         cls: 'btn-' + this.buttonPosition
4173                     }
4174                 ]
4175
4176             } :
4177             {  // BS4 uses mr-auto on left buttons....
4178                 cls : 'modal-footer'
4179             };
4180
4181             
4182
4183         
4184         
4185         var modal = {
4186             cls: "modal",
4187              cn : [
4188                 {
4189                     cls: "modal-dialog " + size,
4190                     cn : [
4191                         {
4192                             cls : "modal-content",
4193                             cn : [
4194                                 {
4195                                     cls : 'modal-header',
4196                                     cn : header
4197                                 },
4198                                 bdy,
4199                                 footer
4200                             ]
4201
4202                         }
4203                     ]
4204
4205                 }
4206             ]
4207         };
4208
4209         if(this.animate){
4210             modal.cls += ' fade';
4211         }
4212
4213         return modal;
4214
4215     },
4216     getChildContainer : function() {
4217
4218          return this.bodyEl;
4219
4220     },
4221     getButtonContainer : function() {
4222         
4223          return Roo.bootstrap.version == 4 ?
4224             this.el.select('.modal-footer',true).first()
4225             : this.el.select('.modal-footer div',true).first();
4226
4227     },
4228     initEvents : function()
4229     {
4230         if (this.allow_close) {
4231             this.closeEl.on('click', this.hide, this);
4232         }
4233         Roo.EventManager.onWindowResize(this.resize, this, true);
4234         if (this.editableTitle) {
4235             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4236             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4237             this.headerEditEl.on('keyup', function(e) {
4238                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4239                         this.toggleHeaderInput(false)
4240                     }
4241                 }, this);
4242             this.headerEditEl.on('blur', function(e) {
4243                 this.toggleHeaderInput(false)
4244             },this);
4245         }
4246
4247     },
4248   
4249
4250     resize : function()
4251     {
4252         this.maskEl.setSize(
4253             Roo.lib.Dom.getViewWidth(true),
4254             Roo.lib.Dom.getViewHeight(true)
4255         );
4256         
4257         if (this.fitwindow) {
4258             
4259            this.dialogEl.setStyle( { 'max-width' : '100%' });
4260             this.setSize(
4261                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4262                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4263             );
4264             return;
4265         }
4266         
4267         if(this.max_width !== 0) {
4268             
4269             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4270             
4271             if(this.height) {
4272                 this.setSize(w, this.height);
4273                 return;
4274             }
4275             
4276             if(this.max_height) {
4277                 this.setSize(w,Math.min(
4278                     this.max_height,
4279                     Roo.lib.Dom.getViewportHeight(true) - 60
4280                 ));
4281                 
4282                 return;
4283             }
4284             
4285             if(!this.fit_content) {
4286                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4287                 return;
4288             }
4289             
4290             this.setSize(w, Math.min(
4291                 60 +
4292                 this.headerEl.getHeight() + 
4293                 this.footerEl.getHeight() + 
4294                 this.getChildHeight(this.bodyEl.dom.childNodes),
4295                 Roo.lib.Dom.getViewportHeight(true) - 60)
4296             );
4297         }
4298         
4299     },
4300
4301     setSize : function(w,h)
4302     {
4303         if (!w && !h) {
4304             return;
4305         }
4306         
4307         this.resizeTo(w,h);
4308     },
4309
4310     show : function() {
4311
4312         if (!this.rendered) {
4313             this.render();
4314         }
4315         this.toggleHeaderInput(false);
4316         //this.el.setStyle('display', 'block');
4317         this.el.removeClass('hideing');
4318         this.el.dom.style.display='block';
4319         
4320         Roo.get(document.body).addClass('modal-open');
4321  
4322         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4323             
4324             (function(){
4325                 this.el.addClass('show');
4326                 this.el.addClass('in');
4327             }).defer(50, this);
4328         }else{
4329             this.el.addClass('show');
4330             this.el.addClass('in');
4331         }
4332
4333         // not sure how we can show data in here..
4334         //if (this.tmpl) {
4335         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4336         //}
4337
4338         Roo.get(document.body).addClass("x-body-masked");
4339         
4340         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4341         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342         this.maskEl.dom.style.display = 'block';
4343         this.maskEl.addClass('show');
4344         
4345         
4346         this.resize();
4347         
4348         this.fireEvent('show', this);
4349
4350         // set zindex here - otherwise it appears to be ignored...
4351         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4352
4353         (function () {
4354             this.items.forEach( function(e) {
4355                 e.layout ? e.layout() : false;
4356
4357             });
4358         }).defer(100,this);
4359
4360     },
4361     hide : function()
4362     {
4363         if(this.fireEvent("beforehide", this) !== false){
4364             
4365             this.maskEl.removeClass('show');
4366             
4367             this.maskEl.dom.style.display = '';
4368             Roo.get(document.body).removeClass("x-body-masked");
4369             this.el.removeClass('in');
4370             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4371
4372             if(this.animate){ // why
4373                 this.el.addClass('hideing');
4374                 this.el.removeClass('show');
4375                 (function(){
4376                     if (!this.el.hasClass('hideing')) {
4377                         return; // it's been shown again...
4378                     }
4379                     
4380                     this.el.dom.style.display='';
4381
4382                     Roo.get(document.body).removeClass('modal-open');
4383                     this.el.removeClass('hideing');
4384                 }).defer(150,this);
4385                 
4386             }else{
4387                 this.el.removeClass('show');
4388                 this.el.dom.style.display='';
4389                 Roo.get(document.body).removeClass('modal-open');
4390
4391             }
4392             this.fireEvent('hide', this);
4393         }
4394     },
4395     isVisible : function()
4396     {
4397         
4398         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4399         
4400     },
4401
4402     addButton : function(str, cb)
4403     {
4404
4405
4406         var b = Roo.apply({}, { html : str } );
4407         b.xns = b.xns || Roo.bootstrap;
4408         b.xtype = b.xtype || 'Button';
4409         if (typeof(b.listeners) == 'undefined') {
4410             b.listeners = { click : cb.createDelegate(this)  };
4411         }
4412
4413         var btn = Roo.factory(b);
4414
4415         btn.render(this.getButtonContainer());
4416
4417         return btn;
4418
4419     },
4420
4421     setDefaultButton : function(btn)
4422     {
4423         //this.el.select('.modal-footer').()
4424     },
4425
4426     resizeTo: function(w,h)
4427     {
4428         this.dialogEl.setWidth(w);
4429         
4430         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4431
4432         this.bodyEl.setHeight(h - diff);
4433         
4434         this.fireEvent('resize', this);
4435     },
4436     
4437     setContentSize  : function(w, h)
4438     {
4439
4440     },
4441     onButtonClick: function(btn,e)
4442     {
4443         //Roo.log([a,b,c]);
4444         this.fireEvent('btnclick', btn.name, e);
4445     },
4446      /**
4447      * Set the title of the Dialog
4448      * @param {String} str new Title
4449      */
4450     setTitle: function(str) {
4451         this.titleEl.dom.innerHTML = str;
4452         this.title = str;
4453     },
4454     /**
4455      * Set the body of the Dialog
4456      * @param {String} str new Title
4457      */
4458     setBody: function(str) {
4459         this.bodyEl.dom.innerHTML = str;
4460     },
4461     /**
4462      * Set the body of the Dialog using the template
4463      * @param {Obj} data - apply this data to the template and replace the body contents.
4464      */
4465     applyBody: function(obj)
4466     {
4467         if (!this.tmpl) {
4468             Roo.log("Error - using apply Body without a template");
4469             //code
4470         }
4471         this.tmpl.overwrite(this.bodyEl, obj);
4472     },
4473     
4474     getChildHeight : function(child_nodes)
4475     {
4476         if(
4477             !child_nodes ||
4478             child_nodes.length == 0
4479         ) {
4480             return 0;
4481         }
4482         
4483         var child_height = 0;
4484         
4485         for(var i = 0; i < child_nodes.length; i++) {
4486             
4487             /*
4488             * for modal with tabs...
4489             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4490                 
4491                 var layout_childs = child_nodes[i].childNodes;
4492                 
4493                 for(var j = 0; j < layout_childs.length; j++) {
4494                     
4495                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4496                         
4497                         var layout_body_childs = layout_childs[j].childNodes;
4498                         
4499                         for(var k = 0; k < layout_body_childs.length; k++) {
4500                             
4501                             if(layout_body_childs[k].classList.contains('navbar')) {
4502                                 child_height += layout_body_childs[k].offsetHeight;
4503                                 continue;
4504                             }
4505                             
4506                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4507                                 
4508                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4509                                 
4510                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4511                                     
4512                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4513                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4514                                         continue;
4515                                     }
4516                                     
4517                                 }
4518                                 
4519                             }
4520                             
4521                         }
4522                     }
4523                 }
4524                 continue;
4525             }
4526             */
4527             
4528             child_height += child_nodes[i].offsetHeight;
4529             // Roo.log(child_nodes[i].offsetHeight);
4530         }
4531         
4532         return child_height;
4533     },
4534     toggleHeaderInput : function(is_edit)
4535     {
4536         if (!this.editableTitle) {
4537             return; // not editable.
4538         }
4539         if (is_edit && this.is_header_editing) {
4540             return; // already editing..
4541         }
4542         if (is_edit) {
4543     
4544             this.headerEditEl.dom.value = this.title;
4545             this.headerEditEl.removeClass('d-none');
4546             this.headerEditEl.dom.focus();
4547             this.titleEl.addClass('d-none');
4548             
4549             this.is_header_editing = true;
4550             return
4551         }
4552         // flip back to not editing.
4553         this.title = this.headerEditEl.dom.value;
4554         this.headerEditEl.addClass('d-none');
4555         this.titleEl.removeClass('d-none');
4556         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4557         this.is_header_editing = false;
4558         this.fireEvent('titlechanged', this, this.title);
4559     
4560             
4561         
4562     }
4563
4564 });
4565
4566
4567 Roo.apply(Roo.bootstrap.Modal,  {
4568     /**
4569          * Button config that displays a single OK button
4570          * @type Object
4571          */
4572         OK :  [{
4573             name : 'ok',
4574             weight : 'primary',
4575             html : 'OK'
4576         }],
4577         /**
4578          * Button config that displays Yes and No buttons
4579          * @type Object
4580          */
4581         YESNO : [
4582             {
4583                 name  : 'no',
4584                 html : 'No'
4585             },
4586             {
4587                 name  :'yes',
4588                 weight : 'primary',
4589                 html : 'Yes'
4590             }
4591         ],
4592
4593         /**
4594          * Button config that displays OK and Cancel buttons
4595          * @type Object
4596          */
4597         OKCANCEL : [
4598             {
4599                name : 'cancel',
4600                 html : 'Cancel'
4601             },
4602             {
4603                 name : 'ok',
4604                 weight : 'primary',
4605                 html : 'OK'
4606             }
4607         ],
4608         /**
4609          * Button config that displays Yes, No and Cancel buttons
4610          * @type Object
4611          */
4612         YESNOCANCEL : [
4613             {
4614                 name : 'yes',
4615                 weight : 'primary',
4616                 html : 'Yes'
4617             },
4618             {
4619                 name : 'no',
4620                 html : 'No'
4621             },
4622             {
4623                 name : 'cancel',
4624                 html : 'Cancel'
4625             }
4626         ],
4627         
4628         zIndex : 10001
4629 });
4630
4631 /*
4632  * - LGPL
4633  *
4634  * messagebox - can be used as a replace
4635  * 
4636  */
4637 /**
4638  * @class Roo.MessageBox
4639  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4640  * Example usage:
4641  *<pre><code>
4642 // Basic alert:
4643 Roo.Msg.alert('Status', 'Changes saved successfully.');
4644
4645 // Prompt for user data:
4646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4647     if (btn == 'ok'){
4648         // process text value...
4649     }
4650 });
4651
4652 // Show a dialog using config options:
4653 Roo.Msg.show({
4654    title:'Save Changes?',
4655    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4656    buttons: Roo.Msg.YESNOCANCEL,
4657    fn: processResult,
4658    animEl: 'elId'
4659 });
4660 </code></pre>
4661  * @singleton
4662  */
4663 Roo.bootstrap.MessageBox = function(){
4664     var dlg, opt, mask, waitTimer;
4665     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4666     var buttons, activeTextEl, bwidth;
4667
4668     
4669     // private
4670     var handleButton = function(button){
4671         dlg.hide();
4672         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4673     };
4674
4675     // private
4676     var handleHide = function(){
4677         if(opt && opt.cls){
4678             dlg.el.removeClass(opt.cls);
4679         }
4680         //if(waitTimer){
4681         //    Roo.TaskMgr.stop(waitTimer);
4682         //    waitTimer = null;
4683         //}
4684     };
4685
4686     // private
4687     var updateButtons = function(b){
4688         var width = 0;
4689         if(!b){
4690             buttons["ok"].hide();
4691             buttons["cancel"].hide();
4692             buttons["yes"].hide();
4693             buttons["no"].hide();
4694             dlg.footerEl.hide();
4695             
4696             return width;
4697         }
4698         dlg.footerEl.show();
4699         for(var k in buttons){
4700             if(typeof buttons[k] != "function"){
4701                 if(b[k]){
4702                     buttons[k].show();
4703                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4704                     width += buttons[k].el.getWidth()+15;
4705                 }else{
4706                     buttons[k].hide();
4707                 }
4708             }
4709         }
4710         return width;
4711     };
4712
4713     // private
4714     var handleEsc = function(d, k, e){
4715         if(opt && opt.closable !== false){
4716             dlg.hide();
4717         }
4718         if(e){
4719             e.stopEvent();
4720         }
4721     };
4722
4723     return {
4724         /**
4725          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4726          * @return {Roo.BasicDialog} The BasicDialog element
4727          */
4728         getDialog : function(){
4729            if(!dlg){
4730                 dlg = new Roo.bootstrap.Modal( {
4731                     //draggable: true,
4732                     //resizable:false,
4733                     //constraintoviewport:false,
4734                     //fixedcenter:true,
4735                     //collapsible : false,
4736                     //shim:true,
4737                     //modal: true,
4738                 //    width: 'auto',
4739                   //  height:100,
4740                     //buttonAlign:"center",
4741                     closeClick : function(){
4742                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4743                             handleButton("no");
4744                         }else{
4745                             handleButton("cancel");
4746                         }
4747                     }
4748                 });
4749                 dlg.render();
4750                 dlg.on("hide", handleHide);
4751                 mask = dlg.mask;
4752                 //dlg.addKeyListener(27, handleEsc);
4753                 buttons = {};
4754                 this.buttons = buttons;
4755                 var bt = this.buttonText;
4756                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4757                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4758                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4759                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4760                 //Roo.log(buttons);
4761                 bodyEl = dlg.bodyEl.createChild({
4762
4763                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4764                         '<textarea class="roo-mb-textarea"></textarea>' +
4765                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4766                 });
4767                 msgEl = bodyEl.dom.firstChild;
4768                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4769                 textboxEl.enableDisplayMode();
4770                 textboxEl.addKeyListener([10,13], function(){
4771                     if(dlg.isVisible() && opt && opt.buttons){
4772                         if(opt.buttons.ok){
4773                             handleButton("ok");
4774                         }else if(opt.buttons.yes){
4775                             handleButton("yes");
4776                         }
4777                     }
4778                 });
4779                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4780                 textareaEl.enableDisplayMode();
4781                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4782                 progressEl.enableDisplayMode();
4783                 
4784                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4785                 var pf = progressEl.dom.firstChild;
4786                 if (pf) {
4787                     pp = Roo.get(pf.firstChild);
4788                     pp.setHeight(pf.offsetHeight);
4789                 }
4790                 
4791             }
4792             return dlg;
4793         },
4794
4795         /**
4796          * Updates the message box body text
4797          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4798          * the XHTML-compliant non-breaking space character '&amp;#160;')
4799          * @return {Roo.MessageBox} This message box
4800          */
4801         updateText : function(text)
4802         {
4803             if(!dlg.isVisible() && !opt.width){
4804                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4805                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4806             }
4807             msgEl.innerHTML = text || '&#160;';
4808       
4809             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4810             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4811             var w = Math.max(
4812                     Math.min(opt.width || cw , this.maxWidth), 
4813                     Math.max(opt.minWidth || this.minWidth, bwidth)
4814             );
4815             if(opt.prompt){
4816                 activeTextEl.setWidth(w);
4817             }
4818             if(dlg.isVisible()){
4819                 dlg.fixedcenter = false;
4820             }
4821             // to big, make it scroll. = But as usual stupid IE does not support
4822             // !important..
4823             
4824             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4825                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4826                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4827             } else {
4828                 bodyEl.dom.style.height = '';
4829                 bodyEl.dom.style.overflowY = '';
4830             }
4831             if (cw > w) {
4832                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4833             } else {
4834                 bodyEl.dom.style.overflowX = '';
4835             }
4836             
4837             dlg.setContentSize(w, bodyEl.getHeight());
4838             if(dlg.isVisible()){
4839                 dlg.fixedcenter = true;
4840             }
4841             return this;
4842         },
4843
4844         /**
4845          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4846          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4847          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4848          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4849          * @return {Roo.MessageBox} This message box
4850          */
4851         updateProgress : function(value, text){
4852             if(text){
4853                 this.updateText(text);
4854             }
4855             
4856             if (pp) { // weird bug on my firefox - for some reason this is not defined
4857                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4858                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4859             }
4860             return this;
4861         },        
4862
4863         /**
4864          * Returns true if the message box is currently displayed
4865          * @return {Boolean} True if the message box is visible, else false
4866          */
4867         isVisible : function(){
4868             return dlg && dlg.isVisible();  
4869         },
4870
4871         /**
4872          * Hides the message box if it is displayed
4873          */
4874         hide : function(){
4875             if(this.isVisible()){
4876                 dlg.hide();
4877             }  
4878         },
4879
4880         /**
4881          * Displays a new message box, or reinitializes an existing message box, based on the config options
4882          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4883          * The following config object properties are supported:
4884          * <pre>
4885 Property    Type             Description
4886 ----------  ---------------  ------------------------------------------------------------------------------------
4887 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4888                                    closes (defaults to undefined)
4889 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4890                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4891 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4892                                    progress and wait dialogs will ignore this property and always hide the
4893                                    close button as they can only be closed programmatically.
4894 cls               String           A custom CSS class to apply to the message box element
4895 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4896                                    displayed (defaults to 75)
4897 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4898                                    function will be btn (the name of the button that was clicked, if applicable,
4899                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4900                                    Progress and wait dialogs will ignore this option since they do not respond to
4901                                    user actions and can only be closed programmatically, so any required function
4902                                    should be called by the same code after it closes the dialog.
4903 icon              String           A CSS class that provides a background image to be used as an icon for
4904                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4905 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4906 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4907 modal             Boolean          False to allow user interaction with the page while the message box is
4908                                    displayed (defaults to true)
4909 msg               String           A string that will replace the existing message box body text (defaults
4910                                    to the XHTML-compliant non-breaking space character '&#160;')
4911 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4912 progress          Boolean          True to display a progress bar (defaults to false)
4913 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4914 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4915 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4916 title             String           The title text
4917 value             String           The string value to set into the active textbox element if displayed
4918 wait              Boolean          True to display a progress bar (defaults to false)
4919 width             Number           The width of the dialog in pixels
4920 </pre>
4921          *
4922          * Example usage:
4923          * <pre><code>
4924 Roo.Msg.show({
4925    title: 'Address',
4926    msg: 'Please enter your address:',
4927    width: 300,
4928    buttons: Roo.MessageBox.OKCANCEL,
4929    multiline: true,
4930    fn: saveAddress,
4931    animEl: 'addAddressBtn'
4932 });
4933 </code></pre>
4934          * @param {Object} config Configuration options
4935          * @return {Roo.MessageBox} This message box
4936          */
4937         show : function(options)
4938         {
4939             
4940             // this causes nightmares if you show one dialog after another
4941             // especially on callbacks..
4942              
4943             if(this.isVisible()){
4944                 
4945                 this.hide();
4946                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4947                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4948                 Roo.log("New Dialog Message:" +  options.msg )
4949                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4950                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4951                 
4952             }
4953             var d = this.getDialog();
4954             opt = options;
4955             d.setTitle(opt.title || "&#160;");
4956             d.closeEl.setDisplayed(opt.closable !== false);
4957             activeTextEl = textboxEl;
4958             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4959             if(opt.prompt){
4960                 if(opt.multiline){
4961                     textboxEl.hide();
4962                     textareaEl.show();
4963                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4964                         opt.multiline : this.defaultTextHeight);
4965                     activeTextEl = textareaEl;
4966                 }else{
4967                     textboxEl.show();
4968                     textareaEl.hide();
4969                 }
4970             }else{
4971                 textboxEl.hide();
4972                 textareaEl.hide();
4973             }
4974             progressEl.setDisplayed(opt.progress === true);
4975             if (opt.progress) {
4976                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4977             }
4978             this.updateProgress(0);
4979             activeTextEl.dom.value = opt.value || "";
4980             if(opt.prompt){
4981                 dlg.setDefaultButton(activeTextEl);
4982             }else{
4983                 var bs = opt.buttons;
4984                 var db = null;
4985                 if(bs && bs.ok){
4986                     db = buttons["ok"];
4987                 }else if(bs && bs.yes){
4988                     db = buttons["yes"];
4989                 }
4990                 dlg.setDefaultButton(db);
4991             }
4992             bwidth = updateButtons(opt.buttons);
4993             this.updateText(opt.msg);
4994             if(opt.cls){
4995                 d.el.addClass(opt.cls);
4996             }
4997             d.proxyDrag = opt.proxyDrag === true;
4998             d.modal = opt.modal !== false;
4999             d.mask = opt.modal !== false ? mask : false;
5000             if(!d.isVisible()){
5001                 // force it to the end of the z-index stack so it gets a cursor in FF
5002                 document.body.appendChild(dlg.el.dom);
5003                 d.animateTarget = null;
5004                 d.show(options.animEl);
5005             }
5006             return this;
5007         },
5008
5009         /**
5010          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5011          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5012          * and closing the message box when the process is complete.
5013          * @param {String} title The title bar text
5014          * @param {String} msg The message box body text
5015          * @return {Roo.MessageBox} This message box
5016          */
5017         progress : function(title, msg){
5018             this.show({
5019                 title : title,
5020                 msg : msg,
5021                 buttons: false,
5022                 progress:true,
5023                 closable:false,
5024                 minWidth: this.minProgressWidth,
5025                 modal : true
5026             });
5027             return this;
5028         },
5029
5030         /**
5031          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5032          * If a callback function is passed it will be called after the user clicks the button, and the
5033          * id of the button that was clicked will be passed as the only parameter to the callback
5034          * (could also be the top-right close button).
5035          * @param {String} title The title bar text
5036          * @param {String} msg The message box body text
5037          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5038          * @param {Object} scope (optional) The scope of the callback function
5039          * @return {Roo.MessageBox} This message box
5040          */
5041         alert : function(title, msg, fn, scope)
5042         {
5043             this.show({
5044                 title : title,
5045                 msg : msg,
5046                 buttons: this.OK,
5047                 fn: fn,
5048                 closable : false,
5049                 scope : scope,
5050                 modal : true
5051             });
5052             return this;
5053         },
5054
5055         /**
5056          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5057          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5058          * You are responsible for closing the message box when the process is complete.
5059          * @param {String} msg The message box body text
5060          * @param {String} title (optional) The title bar text
5061          * @return {Roo.MessageBox} This message box
5062          */
5063         wait : function(msg, title){
5064             this.show({
5065                 title : title,
5066                 msg : msg,
5067                 buttons: false,
5068                 closable:false,
5069                 progress:true,
5070                 modal:true,
5071                 width:300,
5072                 wait:true
5073             });
5074             waitTimer = Roo.TaskMgr.start({
5075                 run: function(i){
5076                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5077                 },
5078                 interval: 1000
5079             });
5080             return this;
5081         },
5082
5083         /**
5084          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5085          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5086          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5087          * @param {String} title The title bar text
5088          * @param {String} msg The message box body text
5089          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5090          * @param {Object} scope (optional) The scope of the callback function
5091          * @return {Roo.MessageBox} This message box
5092          */
5093         confirm : function(title, msg, fn, scope){
5094             this.show({
5095                 title : title,
5096                 msg : msg,
5097                 buttons: this.YESNO,
5098                 fn: fn,
5099                 scope : scope,
5100                 modal : true
5101             });
5102             return this;
5103         },
5104
5105         /**
5106          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5107          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5108          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5109          * (could also be the top-right close button) and the text that was entered will be passed as the two
5110          * parameters to the callback.
5111          * @param {String} title The title bar text
5112          * @param {String} msg The message box body text
5113          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114          * @param {Object} scope (optional) The scope of the callback function
5115          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5116          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5117          * @return {Roo.MessageBox} This message box
5118          */
5119         prompt : function(title, msg, fn, scope, multiline){
5120             this.show({
5121                 title : title,
5122                 msg : msg,
5123                 buttons: this.OKCANCEL,
5124                 fn: fn,
5125                 minWidth:250,
5126                 scope : scope,
5127                 prompt:true,
5128                 multiline: multiline,
5129                 modal : true
5130             });
5131             return this;
5132         },
5133
5134         /**
5135          * Button config that displays a single OK button
5136          * @type Object
5137          */
5138         OK : {ok:true},
5139         /**
5140          * Button config that displays Yes and No buttons
5141          * @type Object
5142          */
5143         YESNO : {yes:true, no:true},
5144         /**
5145          * Button config that displays OK and Cancel buttons
5146          * @type Object
5147          */
5148         OKCANCEL : {ok:true, cancel:true},
5149         /**
5150          * Button config that displays Yes, No and Cancel buttons
5151          * @type Object
5152          */
5153         YESNOCANCEL : {yes:true, no:true, cancel:true},
5154
5155         /**
5156          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5157          * @type Number
5158          */
5159         defaultTextHeight : 75,
5160         /**
5161          * The maximum width in pixels of the message box (defaults to 600)
5162          * @type Number
5163          */
5164         maxWidth : 600,
5165         /**
5166          * The minimum width in pixels of the message box (defaults to 100)
5167          * @type Number
5168          */
5169         minWidth : 100,
5170         /**
5171          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5172          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5173          * @type Number
5174          */
5175         minProgressWidth : 250,
5176         /**
5177          * An object containing the default button text strings that can be overriden for localized language support.
5178          * Supported properties are: ok, cancel, yes and no.
5179          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5180          * @type Object
5181          */
5182         buttonText : {
5183             ok : "OK",
5184             cancel : "Cancel",
5185             yes : "Yes",
5186             no : "No"
5187         }
5188     };
5189 }();
5190
5191 /**
5192  * Shorthand for {@link Roo.MessageBox}
5193  */
5194 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5195 Roo.Msg = Roo.Msg || Roo.MessageBox;
5196 /*
5197  * - LGPL
5198  *
5199  * navbar
5200  * 
5201  */
5202
5203 /**
5204  * @class Roo.bootstrap.Navbar
5205  * @extends Roo.bootstrap.Component
5206  * Bootstrap Navbar class
5207
5208  * @constructor
5209  * Create a new Navbar
5210  * @param {Object} config The config object
5211  */
5212
5213
5214 Roo.bootstrap.Navbar = function(config){
5215     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5216     this.addEvents({
5217         // raw events
5218         /**
5219          * @event beforetoggle
5220          * Fire before toggle the menu
5221          * @param {Roo.EventObject} e
5222          */
5223         "beforetoggle" : true
5224     });
5225 };
5226
5227 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5228     
5229     
5230    
5231     // private
5232     navItems : false,
5233     loadMask : false,
5234     
5235     
5236     getAutoCreate : function(){
5237         
5238         
5239         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5240         
5241     },
5242     
5243     initEvents :function ()
5244     {
5245         //Roo.log(this.el.select('.navbar-toggle',true));
5246         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5247         
5248         var mark = {
5249             tag: "div",
5250             cls:"x-dlg-mask"
5251         };
5252         
5253         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5254         
5255         var size = this.el.getSize();
5256         this.maskEl.setSize(size.width, size.height);
5257         this.maskEl.enableDisplayMode("block");
5258         this.maskEl.hide();
5259         
5260         if(this.loadMask){
5261             this.maskEl.show();
5262         }
5263     },
5264     
5265     
5266     getChildContainer : function()
5267     {
5268         if (this.el && this.el.select('.collapse').getCount()) {
5269             return this.el.select('.collapse',true).first();
5270         }
5271         
5272         return this.el;
5273     },
5274     
5275     mask : function()
5276     {
5277         this.maskEl.show();
5278     },
5279     
5280     unmask : function()
5281     {
5282         this.maskEl.hide();
5283     },
5284     onToggle : function()
5285     {
5286         
5287         if(this.fireEvent('beforetoggle', this) === false){
5288             return;
5289         }
5290         var ce = this.el.select('.navbar-collapse',true).first();
5291       
5292         if (!ce.hasClass('show')) {
5293            this.expand();
5294         } else {
5295             this.collapse();
5296         }
5297         
5298         
5299     
5300     },
5301     /**
5302      * Expand the navbar pulldown 
5303      */
5304     expand : function ()
5305     {
5306        
5307         var ce = this.el.select('.navbar-collapse',true).first();
5308         if (ce.hasClass('collapsing')) {
5309             return;
5310         }
5311         ce.dom.style.height = '';
5312                // show it...
5313         ce.addClass('in'); // old...
5314         ce.removeClass('collapse');
5315         ce.addClass('show');
5316         var h = ce.getHeight();
5317         Roo.log(h);
5318         ce.removeClass('show');
5319         // at this point we should be able to see it..
5320         ce.addClass('collapsing');
5321         
5322         ce.setHeight(0); // resize it ...
5323         ce.on('transitionend', function() {
5324             //Roo.log('done transition');
5325             ce.removeClass('collapsing');
5326             ce.addClass('show');
5327             ce.removeClass('collapse');
5328
5329             ce.dom.style.height = '';
5330         }, this, { single: true} );
5331         ce.setHeight(h);
5332         ce.dom.scrollTop = 0;
5333     },
5334     /**
5335      * Collapse the navbar pulldown 
5336      */
5337     collapse : function()
5338     {
5339          var ce = this.el.select('.navbar-collapse',true).first();
5340        
5341         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5342             // it's collapsed or collapsing..
5343             return;
5344         }
5345         ce.removeClass('in'); // old...
5346         ce.setHeight(ce.getHeight());
5347         ce.removeClass('show');
5348         ce.addClass('collapsing');
5349         
5350         ce.on('transitionend', function() {
5351             ce.dom.style.height = '';
5352             ce.removeClass('collapsing');
5353             ce.addClass('collapse');
5354         }, this, { single: true} );
5355         ce.setHeight(0);
5356     }
5357     
5358     
5359     
5360 });
5361
5362
5363
5364  
5365
5366  /*
5367  * - LGPL
5368  *
5369  * navbar
5370  * 
5371  */
5372
5373 /**
5374  * @class Roo.bootstrap.NavSimplebar
5375  * @extends Roo.bootstrap.Navbar
5376  * Bootstrap Sidebar class
5377  *
5378  * @cfg {Boolean} inverse is inverted color
5379  * 
5380  * @cfg {String} type (nav | pills | tabs)
5381  * @cfg {Boolean} arrangement stacked | justified
5382  * @cfg {String} align (left | right) alignment
5383  * 
5384  * @cfg {Boolean} main (true|false) main nav bar? default false
5385  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5386  * 
5387  * @cfg {String} tag (header|footer|nav|div) default is nav 
5388
5389  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5390  * 
5391  * 
5392  * @constructor
5393  * Create a new Sidebar
5394  * @param {Object} config The config object
5395  */
5396
5397
5398 Roo.bootstrap.NavSimplebar = function(config){
5399     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5400 };
5401
5402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5403     
5404     inverse: false,
5405     
5406     type: false,
5407     arrangement: '',
5408     align : false,
5409     
5410     weight : 'light',
5411     
5412     main : false,
5413     
5414     
5415     tag : false,
5416     
5417     
5418     getAutoCreate : function(){
5419         
5420         
5421         var cfg = {
5422             tag : this.tag || 'div',
5423             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5424         };
5425         if (['light','white'].indexOf(this.weight) > -1) {
5426             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5427         }
5428         cfg.cls += ' bg-' + this.weight;
5429         
5430         if (this.inverse) {
5431             cfg.cls += ' navbar-inverse';
5432             
5433         }
5434         
5435         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5436         
5437         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5438             return cfg;
5439         }
5440         
5441         
5442     
5443         
5444         cfg.cn = [
5445             {
5446                 cls: 'nav nav-' + this.xtype,
5447                 tag : 'ul'
5448             }
5449         ];
5450         
5451          
5452         this.type = this.type || 'nav';
5453         if (['tabs','pills'].indexOf(this.type) != -1) {
5454             cfg.cn[0].cls += ' nav-' + this.type
5455         
5456         
5457         } else {
5458             if (this.type!=='nav') {
5459                 Roo.log('nav type must be nav/tabs/pills')
5460             }
5461             cfg.cn[0].cls += ' navbar-nav'
5462         }
5463         
5464         
5465         
5466         
5467         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5468             cfg.cn[0].cls += ' nav-' + this.arrangement;
5469         }
5470         
5471         
5472         if (this.align === 'right') {
5473             cfg.cn[0].cls += ' navbar-right';
5474         }
5475         
5476         
5477         
5478         
5479         return cfg;
5480     
5481         
5482     }
5483     
5484     
5485     
5486 });
5487
5488
5489
5490  
5491
5492  
5493        /*
5494  * - LGPL
5495  *
5496  * navbar
5497  * navbar-fixed-top
5498  * navbar-expand-md  fixed-top 
5499  */
5500
5501 /**
5502  * @class Roo.bootstrap.NavHeaderbar
5503  * @extends Roo.bootstrap.NavSimplebar
5504  * Bootstrap Sidebar class
5505  *
5506  * @cfg {String} brand what is brand
5507  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5508  * @cfg {String} brand_href href of the brand
5509  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5510  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5511  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5512  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5513  * 
5514  * @constructor
5515  * Create a new Sidebar
5516  * @param {Object} config The config object
5517  */
5518
5519
5520 Roo.bootstrap.NavHeaderbar = function(config){
5521     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5522       
5523 };
5524
5525 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5526     
5527     position: '',
5528     brand: '',
5529     brand_href: false,
5530     srButton : true,
5531     autohide : false,
5532     desktopCenter : false,
5533    
5534     
5535     getAutoCreate : function(){
5536         
5537         var   cfg = {
5538             tag: this.nav || 'nav',
5539             cls: 'navbar navbar-expand-md',
5540             role: 'navigation',
5541             cn: []
5542         };
5543         
5544         var cn = cfg.cn;
5545         if (this.desktopCenter) {
5546             cn.push({cls : 'container', cn : []});
5547             cn = cn[0].cn;
5548         }
5549         
5550         if(this.srButton){
5551             var btn = {
5552                 tag: 'button',
5553                 type: 'button',
5554                 cls: 'navbar-toggle navbar-toggler',
5555                 'data-toggle': 'collapse',
5556                 cn: [
5557                     {
5558                         tag: 'span',
5559                         cls: 'sr-only',
5560                         html: 'Toggle navigation'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar navbar-toggler-icon'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     },
5570                     {
5571                         tag: 'span',
5572                         cls: 'icon-bar'
5573                     }
5574                 ]
5575             };
5576             
5577             cn.push( Roo.bootstrap.version == 4 ? btn : {
5578                 tag: 'div',
5579                 cls: 'navbar-header',
5580                 cn: [
5581                     btn
5582                 ]
5583             });
5584         }
5585         
5586         cn.push({
5587             tag: 'div',
5588             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5589             cn : []
5590         });
5591         
5592         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5593         
5594         if (['light','white'].indexOf(this.weight) > -1) {
5595             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5596         }
5597         cfg.cls += ' bg-' + this.weight;
5598         
5599         
5600         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5601             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5602             
5603             // tag can override this..
5604             
5605             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5606         }
5607         
5608         if (this.brand !== '') {
5609             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5610             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5611                 tag: 'a',
5612                 href: this.brand_href ? this.brand_href : '#',
5613                 cls: 'navbar-brand',
5614                 cn: [
5615                 this.brand
5616                 ]
5617             });
5618         }
5619         
5620         if(this.main){
5621             cfg.cls += ' main-nav';
5622         }
5623         
5624         
5625         return cfg;
5626
5627         
5628     },
5629     getHeaderChildContainer : function()
5630     {
5631         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5632             return this.el.select('.navbar-header',true).first();
5633         }
5634         
5635         return this.getChildContainer();
5636     },
5637     
5638     getChildContainer : function()
5639     {
5640          
5641         return this.el.select('.roo-navbar-collapse',true).first();
5642          
5643         
5644     },
5645     
5646     initEvents : function()
5647     {
5648         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5649         
5650         if (this.autohide) {
5651             
5652             var prevScroll = 0;
5653             var ft = this.el;
5654             
5655             Roo.get(document).on('scroll',function(e) {
5656                 var ns = Roo.get(document).getScroll().top;
5657                 var os = prevScroll;
5658                 prevScroll = ns;
5659                 
5660                 if(ns > os){
5661                     ft.removeClass('slideDown');
5662                     ft.addClass('slideUp');
5663                     return;
5664                 }
5665                 ft.removeClass('slideUp');
5666                 ft.addClass('slideDown');
5667                  
5668               
5669           },this);
5670         }
5671     }    
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSidebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  * 
5691  * @constructor
5692  * Create a new Sidebar
5693  * @param {Object} config The config object
5694  */
5695
5696
5697 Roo.bootstrap.NavSidebar = function(config){
5698     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5699 };
5700
5701 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5702     
5703     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5704     
5705     getAutoCreate : function(){
5706         
5707         
5708         return  {
5709             tag: 'div',
5710             cls: 'sidebar sidebar-nav'
5711         };
5712     
5713         
5714     }
5715     
5716     
5717     
5718 });
5719
5720
5721
5722  
5723
5724  /*
5725  * - LGPL
5726  *
5727  * nav group
5728  * 
5729  */
5730
5731 /**
5732  * @class Roo.bootstrap.NavGroup
5733  * @extends Roo.bootstrap.Component
5734  * Bootstrap NavGroup class
5735  * @cfg {String} align (left|right)
5736  * @cfg {Boolean} inverse
5737  * @cfg {String} type (nav|pills|tab) default nav
5738  * @cfg {String} navId - reference Id for navbar.
5739  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5740  * 
5741  * @constructor
5742  * Create a new nav group
5743  * @param {Object} config The config object
5744  */
5745
5746 Roo.bootstrap.NavGroup = function(config){
5747     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5748     this.navItems = [];
5749    
5750     Roo.bootstrap.NavGroup.register(this);
5751      this.addEvents({
5752         /**
5753              * @event changed
5754              * Fires when the active item changes
5755              * @param {Roo.bootstrap.NavGroup} this
5756              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5757              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5758          */
5759         'changed': true
5760      });
5761     
5762 };
5763
5764 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5765     
5766     align: '',
5767     inverse: false,
5768     form: false,
5769     type: 'nav',
5770     navId : '',
5771     // private
5772     pilltype : true,
5773     
5774     navItems : false, 
5775     
5776     getAutoCreate : function()
5777     {
5778         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5779         
5780         cfg = {
5781             tag : 'ul',
5782             cls: 'nav' 
5783         };
5784         if (Roo.bootstrap.version == 4) {
5785             if (['tabs','pills'].indexOf(this.type) != -1) {
5786                 cfg.cls += ' nav-' + this.type; 
5787             } else {
5788                 // trying to remove so header bar can right align top?
5789                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5790                     // do not use on header bar... 
5791                     cfg.cls += ' navbar-nav';
5792                 }
5793             }
5794             
5795         } else {
5796             if (['tabs','pills'].indexOf(this.type) != -1) {
5797                 cfg.cls += ' nav-' + this.type
5798             } else {
5799                 if (this.type !== 'nav') {
5800                     Roo.log('nav type must be nav/tabs/pills')
5801                 }
5802                 cfg.cls += ' navbar-nav'
5803             }
5804         }
5805         
5806         if (this.parent() && this.parent().sidebar) {
5807             cfg = {
5808                 tag: 'ul',
5809                 cls: 'dashboard-menu sidebar-menu'
5810             };
5811             
5812             return cfg;
5813         }
5814         
5815         if (this.form === true) {
5816             cfg = {
5817                 tag: 'form',
5818                 cls: 'navbar-form form-inline'
5819             };
5820             //nav navbar-right ml-md-auto
5821             if (this.align === 'right') {
5822                 cfg.cls += ' navbar-right ml-md-auto';
5823             } else {
5824                 cfg.cls += ' navbar-left';
5825             }
5826         }
5827         
5828         if (this.align === 'right') {
5829             cfg.cls += ' navbar-right ml-md-auto';
5830         } else {
5831             cfg.cls += ' mr-auto';
5832         }
5833         
5834         if (this.inverse) {
5835             cfg.cls += ' navbar-inverse';
5836             
5837         }
5838         
5839         
5840         return cfg;
5841     },
5842     /**
5843     * sets the active Navigation item
5844     * @param {Roo.bootstrap.NavItem} the new current navitem
5845     */
5846     setActiveItem : function(item)
5847     {
5848         var prev = false;
5849         Roo.each(this.navItems, function(v){
5850             if (v == item) {
5851                 return ;
5852             }
5853             if (v.isActive()) {
5854                 v.setActive(false, true);
5855                 prev = v;
5856                 
5857             }
5858             
5859         });
5860
5861         item.setActive(true, true);
5862         this.fireEvent('changed', this, item, prev);
5863         
5864         
5865     },
5866     /**
5867     * gets the active Navigation item
5868     * @return {Roo.bootstrap.NavItem} the current navitem
5869     */
5870     getActive : function()
5871     {
5872         
5873         var prev = false;
5874         Roo.each(this.navItems, function(v){
5875             
5876             if (v.isActive()) {
5877                 prev = v;
5878                 
5879             }
5880             
5881         });
5882         return prev;
5883     },
5884     
5885     indexOfNav : function()
5886     {
5887         
5888         var prev = false;
5889         Roo.each(this.navItems, function(v,i){
5890             
5891             if (v.isActive()) {
5892                 prev = i;
5893                 
5894             }
5895             
5896         });
5897         return prev;
5898     },
5899     /**
5900     * adds a Navigation item
5901     * @param {Roo.bootstrap.NavItem} the navitem to add
5902     */
5903     addItem : function(cfg)
5904     {
5905         if (this.form && Roo.bootstrap.version == 4) {
5906             cfg.tag = 'div';
5907         }
5908         var cn = new Roo.bootstrap.NavItem(cfg);
5909         this.register(cn);
5910         cn.parentId = this.id;
5911         cn.onRender(this.el, null);
5912         return cn;
5913     },
5914     /**
5915     * register a Navigation item
5916     * @param {Roo.bootstrap.NavItem} the navitem to add
5917     */
5918     register : function(item)
5919     {
5920         this.navItems.push( item);
5921         item.navId = this.navId;
5922     
5923     },
5924     
5925     /**
5926     * clear all the Navigation item
5927     */
5928    
5929     clearAll : function()
5930     {
5931         this.navItems = [];
5932         this.el.dom.innerHTML = '';
5933     },
5934     
5935     getNavItem: function(tabId)
5936     {
5937         var ret = false;
5938         Roo.each(this.navItems, function(e) {
5939             if (e.tabId == tabId) {
5940                ret =  e;
5941                return false;
5942             }
5943             return true;
5944             
5945         });
5946         return ret;
5947     },
5948     
5949     setActiveNext : function()
5950     {
5951         var i = this.indexOfNav(this.getActive());
5952         if (i > this.navItems.length) {
5953             return;
5954         }
5955         this.setActiveItem(this.navItems[i+1]);
5956     },
5957     setActivePrev : function()
5958     {
5959         var i = this.indexOfNav(this.getActive());
5960         if (i  < 1) {
5961             return;
5962         }
5963         this.setActiveItem(this.navItems[i-1]);
5964     },
5965     clearWasActive : function(except) {
5966         Roo.each(this.navItems, function(e) {
5967             if (e.tabId != except.tabId && e.was_active) {
5968                e.was_active = false;
5969                return false;
5970             }
5971             return true;
5972             
5973         });
5974     },
5975     getWasActive : function ()
5976     {
5977         var r = false;
5978         Roo.each(this.navItems, function(e) {
5979             if (e.was_active) {
5980                r = e;
5981                return false;
5982             }
5983             return true;
5984             
5985         });
5986         return r;
5987     }
5988     
5989     
5990 });
5991
5992  
5993 Roo.apply(Roo.bootstrap.NavGroup, {
5994     
5995     groups: {},
5996      /**
5997     * register a Navigation Group
5998     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5999     */
6000     register : function(navgrp)
6001     {
6002         this.groups[navgrp.navId] = navgrp;
6003         
6004     },
6005     /**
6006     * fetch a Navigation Group based on the navigation ID
6007     * @param {string} the navgroup to add
6008     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6009     */
6010     get: function(navId) {
6011         if (typeof(this.groups[navId]) == 'undefined') {
6012             return false;
6013             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6014         }
6015         return this.groups[navId] ;
6016     }
6017     
6018     
6019     
6020 });
6021
6022  /*
6023  * - LGPL
6024  *
6025  * row
6026  * 
6027  */
6028
6029 /**
6030  * @class Roo.bootstrap.NavItem
6031  * @extends Roo.bootstrap.Component
6032  * Bootstrap Navbar.NavItem class
6033  * @cfg {String} href  link to
6034  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6035  * @cfg {Boolean} button_outline show and outlined button
6036  * @cfg {String} html content of button
6037  * @cfg {String} badge text inside badge
6038  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6039  * @cfg {String} glyphicon DEPRICATED - use fa
6040  * @cfg {String} icon DEPRICATED - use fa
6041  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6042  * @cfg {Boolean} active Is item active
6043  * @cfg {Boolean} disabled Is item disabled
6044  * @cfg {String} linkcls  Link Class
6045  * @cfg {Boolean} preventDefault (true | false) default false
6046  * @cfg {String} tabId the tab that this item activates.
6047  * @cfg {String} tagtype (a|span) render as a href or span?
6048  * @cfg {Boolean} animateRef (true|false) link to element default false  
6049   
6050  * @constructor
6051  * Create a new Navbar Item
6052  * @param {Object} config The config object
6053  */
6054 Roo.bootstrap.NavItem = function(config){
6055     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6056     this.addEvents({
6057         // raw events
6058         /**
6059          * @event click
6060          * The raw click event for the entire grid.
6061          * @param {Roo.EventObject} e
6062          */
6063         "click" : true,
6064          /**
6065             * @event changed
6066             * Fires when the active item active state changes
6067             * @param {Roo.bootstrap.NavItem} this
6068             * @param {boolean} state the new state
6069              
6070          */
6071         'changed': true,
6072         /**
6073             * @event scrollto
6074             * Fires when scroll to element
6075             * @param {Roo.bootstrap.NavItem} this
6076             * @param {Object} options
6077             * @param {Roo.EventObject} e
6078              
6079          */
6080         'scrollto': true
6081     });
6082    
6083 };
6084
6085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6086     
6087     href: false,
6088     html: '',
6089     badge: '',
6090     icon: false,
6091     fa : false,
6092     glyphicon: false,
6093     active: false,
6094     preventDefault : false,
6095     tabId : false,
6096     tagtype : 'a',
6097     tag: 'li',
6098     disabled : false,
6099     animateRef : false,
6100     was_active : false,
6101     button_weight : '',
6102     button_outline : false,
6103     linkcls : '',
6104     navLink: false,
6105     
6106     getAutoCreate : function(){
6107          
6108         var cfg = {
6109             tag: this.tag,
6110             cls: 'nav-item'
6111         };
6112         
6113         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6114         
6115         if (this.active) {
6116             cfg.cls +=  ' active' ;
6117         }
6118         if (this.disabled) {
6119             cfg.cls += ' disabled';
6120         }
6121         
6122         // BS4 only?
6123         if (this.button_weight.length) {
6124             cfg.tag = this.href ? 'a' : 'button';
6125             cfg.html = this.html || '';
6126             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6127             if (this.href) {
6128                 cfg.href = this.href;
6129             }
6130             if (this.fa) {
6131                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6132             }
6133             
6134             // menu .. should add dropdown-menu class - so no need for carat..
6135             
6136             if (this.badge !== '') {
6137                  
6138                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6139             }
6140             return cfg;
6141         }
6142         
6143         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6144             cfg.cn = [
6145                 {
6146                     tag: this.tagtype,
6147                     href : this.href || "#",
6148                     html: this.html || ''
6149                 }
6150             ];
6151             if (this.tagtype == 'a') {
6152                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6153         
6154             }
6155             if (this.icon) {
6156                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if (this.fa) {
6159                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6160             }
6161             if(this.glyphicon) {
6162                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6163             }
6164             
6165             if (this.menu) {
6166                 
6167                 cfg.cn[0].html += " <span class='caret'></span>";
6168              
6169             }
6170             
6171             if (this.badge !== '') {
6172                  
6173                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6174             }
6175         }
6176         
6177         
6178         
6179         return cfg;
6180     },
6181     onRender : function(ct, position)
6182     {
6183        // Roo.log("Call onRender: " + this.xtype);
6184         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6185             this.tag = 'div';
6186         }
6187         
6188         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6189         this.navLink = this.el.select('.nav-link',true).first();
6190         return ret;
6191     },
6192       
6193     
6194     initEvents: function() 
6195     {
6196         if (typeof (this.menu) != 'undefined') {
6197             this.menu.parentType = this.xtype;
6198             this.menu.triggerEl = this.el;
6199             this.menu = this.addxtype(Roo.apply({}, this.menu));
6200         }
6201         
6202         this.el.on('click', this.onClick, this);
6203         
6204         //if(this.tagtype == 'span'){
6205         //    this.el.select('span',true).on('click', this.onClick, this);
6206         //}
6207        
6208         // at this point parent should be available..
6209         this.parent().register(this);
6210     },
6211     
6212     onClick : function(e)
6213     {
6214         if (e.getTarget('.dropdown-menu-item')) {
6215             // did you click on a menu itemm.... - then don't trigger onclick..
6216             return;
6217         }
6218         
6219         if(
6220                 this.preventDefault || 
6221                 this.href == '#' 
6222         ){
6223             Roo.log("NavItem - prevent Default?");
6224             e.preventDefault();
6225         }
6226         
6227         if (this.disabled) {
6228             return;
6229         }
6230         
6231         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6232         if (tg && tg.transition) {
6233             Roo.log("waiting for the transitionend");
6234             return;
6235         }
6236         
6237         
6238         
6239         //Roo.log("fire event clicked");
6240         if(this.fireEvent('click', this, e) === false){
6241             return;
6242         };
6243         
6244         if(this.tagtype == 'span'){
6245             return;
6246         }
6247         
6248         //Roo.log(this.href);
6249         var ael = this.el.select('a',true).first();
6250         //Roo.log(ael);
6251         
6252         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6253             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6254             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6255                 return; // ignore... - it's a 'hash' to another page.
6256             }
6257             Roo.log("NavItem - prevent Default?");
6258             e.preventDefault();
6259             this.scrollToElement(e);
6260         }
6261         
6262         
6263         var p =  this.parent();
6264    
6265         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6266             if (typeof(p.setActiveItem) !== 'undefined') {
6267                 p.setActiveItem(this);
6268             }
6269         }
6270         
6271         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6272         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6273             // remove the collapsed menu expand...
6274             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6275         }
6276     },
6277     
6278     isActive: function () {
6279         return this.active
6280     },
6281     setActive : function(state, fire, is_was_active)
6282     {
6283         if (this.active && !state && this.navId) {
6284             this.was_active = true;
6285             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6286             if (nv) {
6287                 nv.clearWasActive(this);
6288             }
6289             
6290         }
6291         this.active = state;
6292         
6293         if (!state ) {
6294             this.el.removeClass('active');
6295             this.navLink ? this.navLink.removeClass('active') : false;
6296         } else if (!this.el.hasClass('active')) {
6297             
6298             this.el.addClass('active');
6299             if (Roo.bootstrap.version == 4 && this.navLink ) {
6300                 this.navLink.addClass('active');
6301             }
6302             
6303         }
6304         if (fire) {
6305             this.fireEvent('changed', this, state);
6306         }
6307         
6308         // show a panel if it's registered and related..
6309         
6310         if (!this.navId || !this.tabId || !state || is_was_active) {
6311             return;
6312         }
6313         
6314         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6315         if (!tg) {
6316             return;
6317         }
6318         var pan = tg.getPanelByName(this.tabId);
6319         if (!pan) {
6320             return;
6321         }
6322         // if we can not flip to new panel - go back to old nav highlight..
6323         if (false == tg.showPanel(pan)) {
6324             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6325             if (nv) {
6326                 var onav = nv.getWasActive();
6327                 if (onav) {
6328                     onav.setActive(true, false, true);
6329                 }
6330             }
6331             
6332         }
6333         
6334         
6335         
6336     },
6337      // this should not be here...
6338     setDisabled : function(state)
6339     {
6340         this.disabled = state;
6341         if (!state ) {
6342             this.el.removeClass('disabled');
6343         } else if (!this.el.hasClass('disabled')) {
6344             this.el.addClass('disabled');
6345         }
6346         
6347     },
6348     
6349     /**
6350      * Fetch the element to display the tooltip on.
6351      * @return {Roo.Element} defaults to this.el
6352      */
6353     tooltipEl : function()
6354     {
6355         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6356     },
6357     
6358     scrollToElement : function(e)
6359     {
6360         var c = document.body;
6361         
6362         /*
6363          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6364          */
6365         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6366             c = document.documentElement;
6367         }
6368         
6369         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6370         
6371         if(!target){
6372             return;
6373         }
6374
6375         var o = target.calcOffsetsTo(c);
6376         
6377         var options = {
6378             target : target,
6379             value : o[1]
6380         };
6381         
6382         this.fireEvent('scrollto', this, options, e);
6383         
6384         Roo.get(c).scrollTo('top', options.value, true);
6385         
6386         return;
6387     }
6388 });
6389  
6390
6391  /*
6392  * - LGPL
6393  *
6394  * sidebar item
6395  *
6396  *  li
6397  *    <span> icon </span>
6398  *    <span> text </span>
6399  *    <span>badge </span>
6400  */
6401
6402 /**
6403  * @class Roo.bootstrap.NavSidebarItem
6404  * @extends Roo.bootstrap.NavItem
6405  * Bootstrap Navbar.NavSidebarItem class
6406  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6407  * {Boolean} open is the menu open
6408  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6409  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6410  * {String} buttonSize (sm|md|lg)the extra classes for the button
6411  * {Boolean} showArrow show arrow next to the text (default true)
6412  * @constructor
6413  * Create a new Navbar Button
6414  * @param {Object} config The config object
6415  */
6416 Roo.bootstrap.NavSidebarItem = function(config){
6417     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6418     this.addEvents({
6419         // raw events
6420         /**
6421          * @event click
6422          * The raw click event for the entire grid.
6423          * @param {Roo.EventObject} e
6424          */
6425         "click" : true,
6426          /**
6427             * @event changed
6428             * Fires when the active item active state changes
6429             * @param {Roo.bootstrap.NavSidebarItem} this
6430             * @param {boolean} state the new state
6431              
6432          */
6433         'changed': true
6434     });
6435    
6436 };
6437
6438 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6439     
6440     badgeWeight : 'default',
6441     
6442     open: false,
6443     
6444     buttonView : false,
6445     
6446     buttonWeight : 'default',
6447     
6448     buttonSize : 'md',
6449     
6450     showArrow : true,
6451     
6452     getAutoCreate : function(){
6453         
6454         
6455         var a = {
6456                 tag: 'a',
6457                 href : this.href || '#',
6458                 cls: '',
6459                 html : '',
6460                 cn : []
6461         };
6462         
6463         if(this.buttonView){
6464             a = {
6465                 tag: 'button',
6466                 href : this.href || '#',
6467                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6468                 html : this.html,
6469                 cn : []
6470             };
6471         }
6472         
6473         var cfg = {
6474             tag: 'li',
6475             cls: '',
6476             cn: [ a ]
6477         };
6478         
6479         if (this.active) {
6480             cfg.cls += ' active';
6481         }
6482         
6483         if (this.disabled) {
6484             cfg.cls += ' disabled';
6485         }
6486         if (this.open) {
6487             cfg.cls += ' open x-open';
6488         }
6489         // left icon..
6490         if (this.glyphicon || this.icon) {
6491             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6492             a.cn.push({ tag : 'i', cls : c }) ;
6493         }
6494         
6495         if(!this.buttonView){
6496             var span = {
6497                 tag: 'span',
6498                 html : this.html || ''
6499             };
6500
6501             a.cn.push(span);
6502             
6503         }
6504         
6505         if (this.badge !== '') {
6506             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6507         }
6508         
6509         if (this.menu) {
6510             
6511             if(this.showArrow){
6512                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6513             }
6514             
6515             a.cls += ' dropdown-toggle treeview' ;
6516         }
6517         
6518         return cfg;
6519     },
6520     
6521     initEvents : function()
6522     { 
6523         if (typeof (this.menu) != 'undefined') {
6524             this.menu.parentType = this.xtype;
6525             this.menu.triggerEl = this.el;
6526             this.menu = this.addxtype(Roo.apply({}, this.menu));
6527         }
6528         
6529         this.el.on('click', this.onClick, this);
6530         
6531         if(this.badge !== ''){
6532             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6533         }
6534         
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if(this.disabled){
6540             e.preventDefault();
6541             return;
6542         }
6543         
6544         if(this.preventDefault){
6545             e.preventDefault();
6546         }
6547         
6548         this.fireEvent('click', this, e);
6549     },
6550     
6551     disable : function()
6552     {
6553         this.setDisabled(true);
6554     },
6555     
6556     enable : function()
6557     {
6558         this.setDisabled(false);
6559     },
6560     
6561     setDisabled : function(state)
6562     {
6563         if(this.disabled == state){
6564             return;
6565         }
6566         
6567         this.disabled = state;
6568         
6569         if (state) {
6570             this.el.addClass('disabled');
6571             return;
6572         }
6573         
6574         this.el.removeClass('disabled');
6575         
6576         return;
6577     },
6578     
6579     setActive : function(state)
6580     {
6581         if(this.active == state){
6582             return;
6583         }
6584         
6585         this.active = state;
6586         
6587         if (state) {
6588             this.el.addClass('active');
6589             return;
6590         }
6591         
6592         this.el.removeClass('active');
6593         
6594         return;
6595     },
6596     
6597     isActive: function () 
6598     {
6599         return this.active;
6600     },
6601     
6602     setBadge : function(str)
6603     {
6604         if(!this.badgeEl){
6605             return;
6606         }
6607         
6608         this.badgeEl.dom.innerHTML = str;
6609     }
6610     
6611    
6612      
6613  
6614 });
6615  
6616
6617  /*
6618  * - LGPL
6619  *
6620  *  Breadcrumb Nav
6621  * 
6622  */
6623 Roo.namespace('Roo.bootstrap.breadcrumb');
6624
6625
6626 /**
6627  * @class Roo.bootstrap.breadcrumb.Nav
6628  * @extends Roo.bootstrap.Component
6629  * Bootstrap Breadcrumb Nav Class
6630  *  
6631  * @children Roo.bootstrap.breadcrumb.Item
6632  * 
6633  * @constructor
6634  * Create a new breadcrumb.Nav
6635  * @param {Object} config The config object
6636  */
6637
6638
6639 Roo.bootstrap.breadcrumb.Nav = function(config){
6640     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6641     
6642     
6643 };
6644
6645 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6646     
6647     getAutoCreate : function()
6648     {
6649
6650         var cfg = {
6651             tag: 'nav',
6652             cn : [
6653                 {
6654                     tag : 'ol',
6655                     cls : 'breadcrumb'
6656                 }
6657             ]
6658             
6659         };
6660           
6661         return cfg;
6662     },
6663     
6664     initEvents: function()
6665     {
6666         this.olEl = this.el.select('ol',true).first();    
6667     },
6668     getChildContainer : function()
6669     {
6670         return this.olEl;  
6671     }
6672     
6673 });
6674
6675  /*
6676  * - LGPL
6677  *
6678  *  Breadcrumb Item
6679  * 
6680  */
6681
6682
6683 /**
6684  * @class Roo.bootstrap.breadcrumb.Nav
6685  * @extends Roo.bootstrap.Component
6686  * Bootstrap Breadcrumb Nav Class
6687  *  
6688  * @children Roo.bootstrap.breadcrumb.Component
6689  * @cfg {String} html the content of the link.
6690  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6691  * @cfg {Boolean} active is it active
6692
6693  * 
6694  * @constructor
6695  * Create a new breadcrumb.Nav
6696  * @param {Object} config The config object
6697  */
6698
6699 Roo.bootstrap.breadcrumb.Item = function(config){
6700     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6701     this.addEvents({
6702         // img events
6703         /**
6704          * @event click
6705          * The img click event for the img.
6706          * @param {Roo.EventObject} e
6707          */
6708         "click" : true
6709     });
6710     
6711 };
6712
6713 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6714     
6715     href: false,
6716     html : '',
6717     
6718     getAutoCreate : function()
6719     {
6720
6721         var cfg = {
6722             tag: 'li',
6723             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6724         };
6725         if (this.href !== false) {
6726             cfg.cn = [{
6727                 tag : 'a',
6728                 href : this.href,
6729                 html : this.html
6730             }];
6731         } else {
6732             cfg.html = this.html;
6733         }
6734         
6735         return cfg;
6736     },
6737     
6738     initEvents: function()
6739     {
6740         if (this.href) {
6741             this.el.select('a', true).first().on('click',this.onClick, this)
6742         }
6743         
6744     },
6745     onClick : function(e)
6746     {
6747         e.preventDefault();
6748         this.fireEvent('click',this,  e);
6749     }
6750     
6751 });
6752
6753  /*
6754  * - LGPL
6755  *
6756  * row
6757  * 
6758  */
6759
6760 /**
6761  * @class Roo.bootstrap.Row
6762  * @extends Roo.bootstrap.Component
6763  * Bootstrap Row class (contains columns...)
6764  * 
6765  * @constructor
6766  * Create a new Row
6767  * @param {Object} config The config object
6768  */
6769
6770 Roo.bootstrap.Row = function(config){
6771     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6772 };
6773
6774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6775     
6776     getAutoCreate : function(){
6777        return {
6778             cls: 'row clearfix'
6779        };
6780     }
6781     
6782     
6783 });
6784
6785  
6786
6787  /*
6788  * - LGPL
6789  *
6790  * pagination
6791  * 
6792  */
6793
6794 /**
6795  * @class Roo.bootstrap.Pagination
6796  * @extends Roo.bootstrap.Component
6797  * Bootstrap Pagination class
6798  * @cfg {String} size xs | sm | md | lg
6799  * @cfg {Boolean} inverse false | true
6800  * 
6801  * @constructor
6802  * Create a new Pagination
6803  * @param {Object} config The config object
6804  */
6805
6806 Roo.bootstrap.Pagination = function(config){
6807     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6808 };
6809
6810 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6811     
6812     cls: false,
6813     size: false,
6814     inverse: false,
6815     
6816     getAutoCreate : function(){
6817         var cfg = {
6818             tag: 'ul',
6819                 cls: 'pagination'
6820         };
6821         if (this.inverse) {
6822             cfg.cls += ' inverse';
6823         }
6824         if (this.html) {
6825             cfg.html=this.html;
6826         }
6827         if (this.cls) {
6828             cfg.cls += " " + this.cls;
6829         }
6830         return cfg;
6831     }
6832    
6833 });
6834
6835  
6836
6837  /*
6838  * - LGPL
6839  *
6840  * Pagination item
6841  * 
6842  */
6843
6844
6845 /**
6846  * @class Roo.bootstrap.PaginationItem
6847  * @extends Roo.bootstrap.Component
6848  * Bootstrap PaginationItem class
6849  * @cfg {String} html text
6850  * @cfg {String} href the link
6851  * @cfg {Boolean} preventDefault (true | false) default true
6852  * @cfg {Boolean} active (true | false) default false
6853  * @cfg {Boolean} disabled default false
6854  * 
6855  * 
6856  * @constructor
6857  * Create a new PaginationItem
6858  * @param {Object} config The config object
6859  */
6860
6861
6862 Roo.bootstrap.PaginationItem = function(config){
6863     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6864     this.addEvents({
6865         // raw events
6866         /**
6867          * @event click
6868          * The raw click event for the entire grid.
6869          * @param {Roo.EventObject} e
6870          */
6871         "click" : true
6872     });
6873 };
6874
6875 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6876     
6877     href : false,
6878     html : false,
6879     preventDefault: true,
6880     active : false,
6881     cls : false,
6882     disabled: false,
6883     
6884     getAutoCreate : function(){
6885         var cfg= {
6886             tag: 'li',
6887             cn: [
6888                 {
6889                     tag : 'a',
6890                     href : this.href ? this.href : '#',
6891                     html : this.html ? this.html : ''
6892                 }
6893             ]
6894         };
6895         
6896         if(this.cls){
6897             cfg.cls = this.cls;
6898         }
6899         
6900         if(this.disabled){
6901             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6902         }
6903         
6904         if(this.active){
6905             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6906         }
6907         
6908         return cfg;
6909     },
6910     
6911     initEvents: function() {
6912         
6913         this.el.on('click', this.onClick, this);
6914         
6915     },
6916     onClick : function(e)
6917     {
6918         Roo.log('PaginationItem on click ');
6919         if(this.preventDefault){
6920             e.preventDefault();
6921         }
6922         
6923         if(this.disabled){
6924             return;
6925         }
6926         
6927         this.fireEvent('click', this, e);
6928     }
6929    
6930 });
6931
6932  
6933
6934  /*
6935  * - LGPL
6936  *
6937  * slider
6938  * 
6939  */
6940
6941
6942 /**
6943  * @class Roo.bootstrap.Slider
6944  * @extends Roo.bootstrap.Component
6945  * Bootstrap Slider class
6946  *    
6947  * @constructor
6948  * Create a new Slider
6949  * @param {Object} config The config object
6950  */
6951
6952 Roo.bootstrap.Slider = function(config){
6953     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6954 };
6955
6956 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6957     
6958     getAutoCreate : function(){
6959         
6960         var cfg = {
6961             tag: 'div',
6962             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6963             cn: [
6964                 {
6965                     tag: 'a',
6966                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6967                 }
6968             ]
6969         };
6970         
6971         return cfg;
6972     }
6973    
6974 });
6975
6976  /*
6977  * Based on:
6978  * Ext JS Library 1.1.1
6979  * Copyright(c) 2006-2007, Ext JS, LLC.
6980  *
6981  * Originally Released Under LGPL - original licence link has changed is not relivant.
6982  *
6983  * Fork - LGPL
6984  * <script type="text/javascript">
6985  */
6986  
6987
6988 /**
6989  * @class Roo.grid.ColumnModel
6990  * @extends Roo.util.Observable
6991  * This is the default implementation of a ColumnModel used by the Grid. It defines
6992  * the columns in the grid.
6993  * <br>Usage:<br>
6994  <pre><code>
6995  var colModel = new Roo.grid.ColumnModel([
6996         {header: "Ticker", width: 60, sortable: true, locked: true},
6997         {header: "Company Name", width: 150, sortable: true},
6998         {header: "Market Cap.", width: 100, sortable: true},
6999         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7000         {header: "Employees", width: 100, sortable: true, resizable: false}
7001  ]);
7002  </code></pre>
7003  * <p>
7004  
7005  * The config options listed for this class are options which may appear in each
7006  * individual column definition.
7007  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7008  * @constructor
7009  * @param {Object} config An Array of column config objects. See this class's
7010  * config objects for details.
7011 */
7012 Roo.grid.ColumnModel = function(config){
7013         /**
7014      * The config passed into the constructor
7015      */
7016     this.config = config;
7017     this.lookup = {};
7018
7019     // if no id, create one
7020     // if the column does not have a dataIndex mapping,
7021     // map it to the order it is in the config
7022     for(var i = 0, len = config.length; i < len; i++){
7023         var c = config[i];
7024         if(typeof c.dataIndex == "undefined"){
7025             c.dataIndex = i;
7026         }
7027         if(typeof c.renderer == "string"){
7028             c.renderer = Roo.util.Format[c.renderer];
7029         }
7030         if(typeof c.id == "undefined"){
7031             c.id = Roo.id();
7032         }
7033         if(c.editor && c.editor.xtype){
7034             c.editor  = Roo.factory(c.editor, Roo.grid);
7035         }
7036         if(c.editor && c.editor.isFormField){
7037             c.editor = new Roo.grid.GridEditor(c.editor);
7038         }
7039         this.lookup[c.id] = c;
7040     }
7041
7042     /**
7043      * The width of columns which have no width specified (defaults to 100)
7044      * @type Number
7045      */
7046     this.defaultWidth = 100;
7047
7048     /**
7049      * Default sortable of columns which have no sortable specified (defaults to false)
7050      * @type Boolean
7051      */
7052     this.defaultSortable = false;
7053
7054     this.addEvents({
7055         /**
7056              * @event widthchange
7057              * Fires when the width of a column changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newWidth The new width
7061              */
7062             "widthchange": true,
7063         /**
7064              * @event headerchange
7065              * Fires when the text of a header changes.
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Number} newText The new header text
7069              */
7070             "headerchange": true,
7071         /**
7072              * @event hiddenchange
7073              * Fires when a column is hidden or "unhidden".
7074              * @param {ColumnModel} this
7075              * @param {Number} columnIndex The column index
7076              * @param {Boolean} hidden true if hidden, false otherwise
7077              */
7078             "hiddenchange": true,
7079             /**
7080          * @event columnmoved
7081          * Fires when a column is moved.
7082          * @param {ColumnModel} this
7083          * @param {Number} oldIndex
7084          * @param {Number} newIndex
7085          */
7086         "columnmoved" : true,
7087         /**
7088          * @event columlockchange
7089          * Fires when a column's locked state is changed
7090          * @param {ColumnModel} this
7091          * @param {Number} colIndex
7092          * @param {Boolean} locked true if locked
7093          */
7094         "columnlockchange" : true
7095     });
7096     Roo.grid.ColumnModel.superclass.constructor.call(this);
7097 };
7098 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7099     /**
7100      * @cfg {String} header The header text to display in the Grid view.
7101      */
7102     /**
7103      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7104      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7105      * specified, the column's index is used as an index into the Record's data Array.
7106      */
7107     /**
7108      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7109      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7110      */
7111     /**
7112      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7113      * Defaults to the value of the {@link #defaultSortable} property.
7114      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7115      */
7116     /**
7117      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7121      */
7122     /**
7123      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7124      */
7125     /**
7126      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7127      */
7128     /**
7129      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7130      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7131      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7132      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7133      */
7134        /**
7135      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7136      */
7137     /**
7138      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7142      */
7143     /**
7144      * @cfg {String} cursor (Optional)
7145      */
7146     /**
7147      * @cfg {String} tooltip (Optional)
7148      */
7149     /**
7150      * @cfg {Number} xs (Optional)
7151      */
7152     /**
7153      * @cfg {Number} sm (Optional)
7154      */
7155     /**
7156      * @cfg {Number} md (Optional)
7157      */
7158     /**
7159      * @cfg {Number} lg (Optional)
7160      */
7161     /**
7162      * Returns the id of the column at the specified index.
7163      * @param {Number} index The column index
7164      * @return {String} the id
7165      */
7166     getColumnId : function(index){
7167         return this.config[index].id;
7168     },
7169
7170     /**
7171      * Returns the column for a specified id.
7172      * @param {String} id The column id
7173      * @return {Object} the column
7174      */
7175     getColumnById : function(id){
7176         return this.lookup[id];
7177     },
7178
7179     
7180     /**
7181      * Returns the column for a specified dataIndex.
7182      * @param {String} dataIndex The column dataIndex
7183      * @return {Object|Boolean} the column or false if not found
7184      */
7185     getColumnByDataIndex: function(dataIndex){
7186         var index = this.findColumnIndex(dataIndex);
7187         return index > -1 ? this.config[index] : false;
7188     },
7189     
7190     /**
7191      * Returns the index for a specified column id.
7192      * @param {String} id The column id
7193      * @return {Number} the index, or -1 if not found
7194      */
7195     getIndexById : function(id){
7196         for(var i = 0, len = this.config.length; i < len; i++){
7197             if(this.config[i].id == id){
7198                 return i;
7199             }
7200         }
7201         return -1;
7202     },
7203     
7204     /**
7205      * Returns the index for a specified column dataIndex.
7206      * @param {String} dataIndex The column dataIndex
7207      * @return {Number} the index, or -1 if not found
7208      */
7209     
7210     findColumnIndex : function(dataIndex){
7211         for(var i = 0, len = this.config.length; i < len; i++){
7212             if(this.config[i].dataIndex == dataIndex){
7213                 return i;
7214             }
7215         }
7216         return -1;
7217     },
7218     
7219     
7220     moveColumn : function(oldIndex, newIndex){
7221         var c = this.config[oldIndex];
7222         this.config.splice(oldIndex, 1);
7223         this.config.splice(newIndex, 0, c);
7224         this.dataMap = null;
7225         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7226     },
7227
7228     isLocked : function(colIndex){
7229         return this.config[colIndex].locked === true;
7230     },
7231
7232     setLocked : function(colIndex, value, suppressEvent){
7233         if(this.isLocked(colIndex) == value){
7234             return;
7235         }
7236         this.config[colIndex].locked = value;
7237         if(!suppressEvent){
7238             this.fireEvent("columnlockchange", this, colIndex, value);
7239         }
7240     },
7241
7242     getTotalLockedWidth : function(){
7243         var totalWidth = 0;
7244         for(var i = 0; i < this.config.length; i++){
7245             if(this.isLocked(i) && !this.isHidden(i)){
7246                 this.totalWidth += this.getColumnWidth(i);
7247             }
7248         }
7249         return totalWidth;
7250     },
7251
7252     getLockedCount : function(){
7253         for(var i = 0, len = this.config.length; i < len; i++){
7254             if(!this.isLocked(i)){
7255                 return i;
7256             }
7257         }
7258         
7259         return this.config.length;
7260     },
7261
7262     /**
7263      * Returns the number of columns.
7264      * @return {Number}
7265      */
7266     getColumnCount : function(visibleOnly){
7267         if(visibleOnly === true){
7268             var c = 0;
7269             for(var i = 0, len = this.config.length; i < len; i++){
7270                 if(!this.isHidden(i)){
7271                     c++;
7272                 }
7273             }
7274             return c;
7275         }
7276         return this.config.length;
7277     },
7278
7279     /**
7280      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7281      * @param {Function} fn
7282      * @param {Object} scope (optional)
7283      * @return {Array} result
7284      */
7285     getColumnsBy : function(fn, scope){
7286         var r = [];
7287         for(var i = 0, len = this.config.length; i < len; i++){
7288             var c = this.config[i];
7289             if(fn.call(scope||this, c, i) === true){
7290                 r[r.length] = c;
7291             }
7292         }
7293         return r;
7294     },
7295
7296     /**
7297      * Returns true if the specified column is sortable.
7298      * @param {Number} col The column index
7299      * @return {Boolean}
7300      */
7301     isSortable : function(col){
7302         if(typeof this.config[col].sortable == "undefined"){
7303             return this.defaultSortable;
7304         }
7305         return this.config[col].sortable;
7306     },
7307
7308     /**
7309      * Returns the rendering (formatting) function defined for the column.
7310      * @param {Number} col The column index.
7311      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7312      */
7313     getRenderer : function(col){
7314         if(!this.config[col].renderer){
7315             return Roo.grid.ColumnModel.defaultRenderer;
7316         }
7317         return this.config[col].renderer;
7318     },
7319
7320     /**
7321      * Sets the rendering (formatting) function for a column.
7322      * @param {Number} col The column index
7323      * @param {Function} fn The function to use to process the cell's raw data
7324      * to return HTML markup for the grid view. The render function is called with
7325      * the following parameters:<ul>
7326      * <li>Data value.</li>
7327      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7328      * <li>css A CSS style string to apply to the table cell.</li>
7329      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7330      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7331      * <li>Row index</li>
7332      * <li>Column index</li>
7333      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7334      */
7335     setRenderer : function(col, fn){
7336         this.config[col].renderer = fn;
7337     },
7338
7339     /**
7340      * Returns the width for the specified column.
7341      * @param {Number} col The column index
7342      * @return {Number}
7343      */
7344     getColumnWidth : function(col){
7345         return this.config[col].width * 1 || this.defaultWidth;
7346     },
7347
7348     /**
7349      * Sets the width for a column.
7350      * @param {Number} col The column index
7351      * @param {Number} width The new width
7352      */
7353     setColumnWidth : function(col, width, suppressEvent){
7354         this.config[col].width = width;
7355         this.totalWidth = null;
7356         if(!suppressEvent){
7357              this.fireEvent("widthchange", this, col, width);
7358         }
7359     },
7360
7361     /**
7362      * Returns the total width of all columns.
7363      * @param {Boolean} includeHidden True to include hidden column widths
7364      * @return {Number}
7365      */
7366     getTotalWidth : function(includeHidden){
7367         if(!this.totalWidth){
7368             this.totalWidth = 0;
7369             for(var i = 0, len = this.config.length; i < len; i++){
7370                 if(includeHidden || !this.isHidden(i)){
7371                     this.totalWidth += this.getColumnWidth(i);
7372                 }
7373             }
7374         }
7375         return this.totalWidth;
7376     },
7377
7378     /**
7379      * Returns the header for the specified column.
7380      * @param {Number} col The column index
7381      * @return {String}
7382      */
7383     getColumnHeader : function(col){
7384         return this.config[col].header;
7385     },
7386
7387     /**
7388      * Sets the header for a column.
7389      * @param {Number} col The column index
7390      * @param {String} header The new header
7391      */
7392     setColumnHeader : function(col, header){
7393         this.config[col].header = header;
7394         this.fireEvent("headerchange", this, col, header);
7395     },
7396
7397     /**
7398      * Returns the tooltip for the specified column.
7399      * @param {Number} col The column index
7400      * @return {String}
7401      */
7402     getColumnTooltip : function(col){
7403             return this.config[col].tooltip;
7404     },
7405     /**
7406      * Sets the tooltip for a column.
7407      * @param {Number} col The column index
7408      * @param {String} tooltip The new tooltip
7409      */
7410     setColumnTooltip : function(col, tooltip){
7411             this.config[col].tooltip = tooltip;
7412     },
7413
7414     /**
7415      * Returns the dataIndex for the specified column.
7416      * @param {Number} col The column index
7417      * @return {Number}
7418      */
7419     getDataIndex : function(col){
7420         return this.config[col].dataIndex;
7421     },
7422
7423     /**
7424      * Sets the dataIndex for a column.
7425      * @param {Number} col The column index
7426      * @param {Number} dataIndex The new dataIndex
7427      */
7428     setDataIndex : function(col, dataIndex){
7429         this.config[col].dataIndex = dataIndex;
7430     },
7431
7432     
7433     
7434     /**
7435      * Returns true if the cell is editable.
7436      * @param {Number} colIndex The column index
7437      * @param {Number} rowIndex The row index - this is nto actually used..?
7438      * @return {Boolean}
7439      */
7440     isCellEditable : function(colIndex, rowIndex){
7441         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7442     },
7443
7444     /**
7445      * Returns the editor defined for the cell/column.
7446      * return false or null to disable editing.
7447      * @param {Number} colIndex The column index
7448      * @param {Number} rowIndex The row index
7449      * @return {Object}
7450      */
7451     getCellEditor : function(colIndex, rowIndex){
7452         return this.config[colIndex].editor;
7453     },
7454
7455     /**
7456      * Sets if a column is editable.
7457      * @param {Number} col The column index
7458      * @param {Boolean} editable True if the column is editable
7459      */
7460     setEditable : function(col, editable){
7461         this.config[col].editable = editable;
7462     },
7463
7464
7465     /**
7466      * Returns true if the column is hidden.
7467      * @param {Number} colIndex The column index
7468      * @return {Boolean}
7469      */
7470     isHidden : function(colIndex){
7471         return this.config[colIndex].hidden;
7472     },
7473
7474
7475     /**
7476      * Returns true if the column width cannot be changed
7477      */
7478     isFixed : function(colIndex){
7479         return this.config[colIndex].fixed;
7480     },
7481
7482     /**
7483      * Returns true if the column can be resized
7484      * @return {Boolean}
7485      */
7486     isResizable : function(colIndex){
7487         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7488     },
7489     /**
7490      * Sets if a column is hidden.
7491      * @param {Number} colIndex The column index
7492      * @param {Boolean} hidden True if the column is hidden
7493      */
7494     setHidden : function(colIndex, hidden){
7495         this.config[colIndex].hidden = hidden;
7496         this.totalWidth = null;
7497         this.fireEvent("hiddenchange", this, colIndex, hidden);
7498     },
7499
7500     /**
7501      * Sets the editor for a column.
7502      * @param {Number} col The column index
7503      * @param {Object} editor The editor object
7504      */
7505     setEditor : function(col, editor){
7506         this.config[col].editor = editor;
7507     }
7508 });
7509
7510 Roo.grid.ColumnModel.defaultRenderer = function(value)
7511 {
7512     if(typeof value == "object") {
7513         return value;
7514     }
7515         if(typeof value == "string" && value.length < 1){
7516             return "&#160;";
7517         }
7518     
7519         return String.format("{0}", value);
7520 };
7521
7522 // Alias for backwards compatibility
7523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7524 /*
7525  * Based on:
7526  * Ext JS Library 1.1.1
7527  * Copyright(c) 2006-2007, Ext JS, LLC.
7528  *
7529  * Originally Released Under LGPL - original licence link has changed is not relivant.
7530  *
7531  * Fork - LGPL
7532  * <script type="text/javascript">
7533  */
7534  
7535 /**
7536  * @class Roo.LoadMask
7537  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7538  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7539  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7540  * element's UpdateManager load indicator and will be destroyed after the initial load.
7541  * @constructor
7542  * Create a new LoadMask
7543  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7544  * @param {Object} config The config object
7545  */
7546 Roo.LoadMask = function(el, config){
7547     this.el = Roo.get(el);
7548     Roo.apply(this, config);
7549     if(this.store){
7550         this.store.on('beforeload', this.onBeforeLoad, this);
7551         this.store.on('load', this.onLoad, this);
7552         this.store.on('loadexception', this.onLoadException, this);
7553         this.removeMask = false;
7554     }else{
7555         var um = this.el.getUpdateManager();
7556         um.showLoadIndicator = false; // disable the default indicator
7557         um.on('beforeupdate', this.onBeforeLoad, this);
7558         um.on('update', this.onLoad, this);
7559         um.on('failure', this.onLoad, this);
7560         this.removeMask = true;
7561     }
7562 };
7563
7564 Roo.LoadMask.prototype = {
7565     /**
7566      * @cfg {Boolean} removeMask
7567      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7568      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7569      */
7570     /**
7571      * @cfg {String} msg
7572      * The text to display in a centered loading message box (defaults to 'Loading...')
7573      */
7574     msg : 'Loading...',
7575     /**
7576      * @cfg {String} msgCls
7577      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7578      */
7579     msgCls : 'x-mask-loading',
7580
7581     /**
7582      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7583      * @type Boolean
7584      */
7585     disabled: false,
7586
7587     /**
7588      * Disables the mask to prevent it from being displayed
7589      */
7590     disable : function(){
7591        this.disabled = true;
7592     },
7593
7594     /**
7595      * Enables the mask so that it can be displayed
7596      */
7597     enable : function(){
7598         this.disabled = false;
7599     },
7600     
7601     onLoadException : function()
7602     {
7603         Roo.log(arguments);
7604         
7605         if (typeof(arguments[3]) != 'undefined') {
7606             Roo.MessageBox.alert("Error loading",arguments[3]);
7607         } 
7608         /*
7609         try {
7610             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7611                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7612             }   
7613         } catch(e) {
7614             
7615         }
7616         */
7617     
7618         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7619     },
7620     // private
7621     onLoad : function()
7622     {
7623         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7624     },
7625
7626     // private
7627     onBeforeLoad : function(){
7628         if(!this.disabled){
7629             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7630         }
7631     },
7632
7633     // private
7634     destroy : function(){
7635         if(this.store){
7636             this.store.un('beforeload', this.onBeforeLoad, this);
7637             this.store.un('load', this.onLoad, this);
7638             this.store.un('loadexception', this.onLoadException, this);
7639         }else{
7640             var um = this.el.getUpdateManager();
7641             um.un('beforeupdate', this.onBeforeLoad, this);
7642             um.un('update', this.onLoad, this);
7643             um.un('failure', this.onLoad, this);
7644         }
7645     }
7646 };/*
7647  * - LGPL
7648  *
7649  * table
7650  * 
7651  */
7652
7653 /**
7654  * @class Roo.bootstrap.Table
7655  * @extends Roo.bootstrap.Component
7656  * Bootstrap Table class
7657  * @cfg {String} cls table class
7658  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7659  * @cfg {String} bgcolor Specifies the background color for a table
7660  * @cfg {Number} border Specifies whether the table cells should have borders or not
7661  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7662  * @cfg {Number} cellspacing Specifies the space between cells
7663  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7664  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7665  * @cfg {String} sortable Specifies that the table should be sortable
7666  * @cfg {String} summary Specifies a summary of the content of a table
7667  * @cfg {Number} width Specifies the width of a table
7668  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7669  * 
7670  * @cfg {boolean} striped Should the rows be alternative striped
7671  * @cfg {boolean} bordered Add borders to the table
7672  * @cfg {boolean} hover Add hover highlighting
7673  * @cfg {boolean} condensed Format condensed
7674  * @cfg {boolean} responsive Format condensed
7675  * @cfg {Boolean} loadMask (true|false) default false
7676  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7677  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7678  * @cfg {Boolean} rowSelection (true|false) default false
7679  * @cfg {Boolean} cellSelection (true|false) default false
7680  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7681  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7682  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7683  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7684  
7685  * 
7686  * @constructor
7687  * Create a new Table
7688  * @param {Object} config The config object
7689  */
7690
7691 Roo.bootstrap.Table = function(config){
7692     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7693     
7694   
7695     
7696     // BC...
7697     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7698     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7699     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7700     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7701     
7702     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7703     if (this.sm) {
7704         this.sm.grid = this;
7705         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7706         this.sm = this.selModel;
7707         this.sm.xmodule = this.xmodule || false;
7708     }
7709     
7710     if (this.cm && typeof(this.cm.config) == 'undefined') {
7711         this.colModel = new Roo.grid.ColumnModel(this.cm);
7712         this.cm = this.colModel;
7713         this.cm.xmodule = this.xmodule || false;
7714     }
7715     if (this.store) {
7716         this.store= Roo.factory(this.store, Roo.data);
7717         this.ds = this.store;
7718         this.ds.xmodule = this.xmodule || false;
7719          
7720     }
7721     if (this.footer && this.store) {
7722         this.footer.dataSource = this.ds;
7723         this.footer = Roo.factory(this.footer);
7724     }
7725     
7726     /** @private */
7727     this.addEvents({
7728         /**
7729          * @event cellclick
7730          * Fires when a cell is clicked
7731          * @param {Roo.bootstrap.Table} this
7732          * @param {Roo.Element} el
7733          * @param {Number} rowIndex
7734          * @param {Number} columnIndex
7735          * @param {Roo.EventObject} e
7736          */
7737         "cellclick" : true,
7738         /**
7739          * @event celldblclick
7740          * Fires when a cell is double clicked
7741          * @param {Roo.bootstrap.Table} this
7742          * @param {Roo.Element} el
7743          * @param {Number} rowIndex
7744          * @param {Number} columnIndex
7745          * @param {Roo.EventObject} e
7746          */
7747         "celldblclick" : true,
7748         /**
7749          * @event rowclick
7750          * Fires when a row is clicked
7751          * @param {Roo.bootstrap.Table} this
7752          * @param {Roo.Element} el
7753          * @param {Number} rowIndex
7754          * @param {Roo.EventObject} e
7755          */
7756         "rowclick" : true,
7757         /**
7758          * @event rowdblclick
7759          * Fires when a row is double clicked
7760          * @param {Roo.bootstrap.Table} this
7761          * @param {Roo.Element} el
7762          * @param {Number} rowIndex
7763          * @param {Roo.EventObject} e
7764          */
7765         "rowdblclick" : true,
7766         /**
7767          * @event mouseover
7768          * Fires when a mouseover occur
7769          * @param {Roo.bootstrap.Table} this
7770          * @param {Roo.Element} el
7771          * @param {Number} rowIndex
7772          * @param {Number} columnIndex
7773          * @param {Roo.EventObject} e
7774          */
7775         "mouseover" : true,
7776         /**
7777          * @event mouseout
7778          * Fires when a mouseout occur
7779          * @param {Roo.bootstrap.Table} this
7780          * @param {Roo.Element} el
7781          * @param {Number} rowIndex
7782          * @param {Number} columnIndex
7783          * @param {Roo.EventObject} e
7784          */
7785         "mouseout" : true,
7786         /**
7787          * @event rowclass
7788          * Fires when a row is rendered, so you can change add a style to it.
7789          * @param {Roo.bootstrap.Table} this
7790          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7791          */
7792         'rowclass' : true,
7793           /**
7794          * @event rowsrendered
7795          * Fires when all the  rows have been rendered
7796          * @param {Roo.bootstrap.Table} this
7797          */
7798         'rowsrendered' : true,
7799         /**
7800          * @event contextmenu
7801          * The raw contextmenu event for the entire grid.
7802          * @param {Roo.EventObject} e
7803          */
7804         "contextmenu" : true,
7805         /**
7806          * @event rowcontextmenu
7807          * Fires when a row is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Roo.EventObject} e
7811          */
7812         "rowcontextmenu" : true,
7813         /**
7814          * @event cellcontextmenu
7815          * Fires when a cell is right clicked
7816          * @param {Roo.bootstrap.Table} this
7817          * @param {Number} rowIndex
7818          * @param {Number} cellIndex
7819          * @param {Roo.EventObject} e
7820          */
7821          "cellcontextmenu" : true,
7822          /**
7823          * @event headercontextmenu
7824          * Fires when a header is right clicked
7825          * @param {Roo.bootstrap.Table} this
7826          * @param {Number} columnIndex
7827          * @param {Roo.EventObject} e
7828          */
7829         "headercontextmenu" : true
7830     });
7831 };
7832
7833 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7834     
7835     cls: false,
7836     align: false,
7837     bgcolor: false,
7838     border: false,
7839     cellpadding: false,
7840     cellspacing: false,
7841     frame: false,
7842     rules: false,
7843     sortable: false,
7844     summary: false,
7845     width: false,
7846     striped : false,
7847     scrollBody : false,
7848     bordered: false,
7849     hover:  false,
7850     condensed : false,
7851     responsive : false,
7852     sm : false,
7853     cm : false,
7854     store : false,
7855     loadMask : false,
7856     footerShow : true,
7857     headerShow : true,
7858   
7859     rowSelection : false,
7860     cellSelection : false,
7861     layout : false,
7862     
7863     // Roo.Element - the tbody
7864     mainBody: false,
7865     // Roo.Element - thead element
7866     mainHead: false,
7867     
7868     container: false, // used by gridpanel...
7869     
7870     lazyLoad : false,
7871     
7872     CSS : Roo.util.CSS,
7873     
7874     auto_hide_footer : false,
7875     
7876     getAutoCreate : function()
7877     {
7878         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7879         
7880         cfg = {
7881             tag: 'table',
7882             cls : 'table',
7883             cn : []
7884         };
7885         if (this.scrollBody) {
7886             cfg.cls += ' table-body-fixed';
7887         }    
7888         if (this.striped) {
7889             cfg.cls += ' table-striped';
7890         }
7891         
7892         if (this.hover) {
7893             cfg.cls += ' table-hover';
7894         }
7895         if (this.bordered) {
7896             cfg.cls += ' table-bordered';
7897         }
7898         if (this.condensed) {
7899             cfg.cls += ' table-condensed';
7900         }
7901         if (this.responsive) {
7902             cfg.cls += ' table-responsive';
7903         }
7904         
7905         if (this.cls) {
7906             cfg.cls+=  ' ' +this.cls;
7907         }
7908         
7909         // this lot should be simplifed...
7910         var _t = this;
7911         var cp = [
7912             'align',
7913             'bgcolor',
7914             'border',
7915             'cellpadding',
7916             'cellspacing',
7917             'frame',
7918             'rules',
7919             'sortable',
7920             'summary',
7921             'width'
7922         ].forEach(function(k) {
7923             if (_t[k]) {
7924                 cfg[k] = _t[k];
7925             }
7926         });
7927         
7928         
7929         if (this.layout) {
7930             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7931         }
7932         
7933         if(this.store || this.cm){
7934             if(this.headerShow){
7935                 cfg.cn.push(this.renderHeader());
7936             }
7937             
7938             cfg.cn.push(this.renderBody());
7939             
7940             if(this.footerShow){
7941                 cfg.cn.push(this.renderFooter());
7942             }
7943             // where does this come from?
7944             //cfg.cls+=  ' TableGrid';
7945         }
7946         
7947         return { cn : [ cfg ] };
7948     },
7949     
7950     initEvents : function()
7951     {   
7952         if(!this.store || !this.cm){
7953             return;
7954         }
7955         if (this.selModel) {
7956             this.selModel.initEvents();
7957         }
7958         
7959         
7960         //Roo.log('initEvents with ds!!!!');
7961         
7962         this.mainBody = this.el.select('tbody', true).first();
7963         this.mainHead = this.el.select('thead', true).first();
7964         this.mainFoot = this.el.select('tfoot', true).first();
7965         
7966         
7967         
7968         var _this = this;
7969         
7970         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7971             e.on('click', _this.sort, _this);
7972         });
7973         
7974         this.mainBody.on("click", this.onClick, this);
7975         this.mainBody.on("dblclick", this.onDblClick, this);
7976         
7977         // why is this done????? = it breaks dialogs??
7978         //this.parent().el.setStyle('position', 'relative');
7979         
7980         
7981         if (this.footer) {
7982             this.footer.parentId = this.id;
7983             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7984             
7985             if(this.lazyLoad){
7986                 this.el.select('tfoot tr td').first().addClass('hide');
7987             }
7988         } 
7989         
7990         if(this.loadMask) {
7991             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7992         }
7993         
7994         this.store.on('load', this.onLoad, this);
7995         this.store.on('beforeload', this.onBeforeLoad, this);
7996         this.store.on('update', this.onUpdate, this);
7997         this.store.on('add', this.onAdd, this);
7998         this.store.on("clear", this.clear, this);
7999         
8000         this.el.on("contextmenu", this.onContextMenu, this);
8001         
8002         this.mainBody.on('scroll', this.onBodyScroll, this);
8003         
8004         this.cm.on("headerchange", this.onHeaderChange, this);
8005         
8006         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8007         
8008     },
8009     
8010     onContextMenu : function(e, t)
8011     {
8012         this.processEvent("contextmenu", e);
8013     },
8014     
8015     processEvent : function(name, e)
8016     {
8017         if (name != 'touchstart' ) {
8018             this.fireEvent(name, e);    
8019         }
8020         
8021         var t = e.getTarget();
8022         
8023         var cell = Roo.get(t);
8024         
8025         if(!cell){
8026             return;
8027         }
8028         
8029         if(cell.findParent('tfoot', false, true)){
8030             return;
8031         }
8032         
8033         if(cell.findParent('thead', false, true)){
8034             
8035             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8036                 cell = Roo.get(t).findParent('th', false, true);
8037                 if (!cell) {
8038                     Roo.log("failed to find th in thead?");
8039                     Roo.log(e.getTarget());
8040                     return;
8041                 }
8042             }
8043             
8044             var cellIndex = cell.dom.cellIndex;
8045             
8046             var ename = name == 'touchstart' ? 'click' : name;
8047             this.fireEvent("header" + ename, this, cellIndex, e);
8048             
8049             return;
8050         }
8051         
8052         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8053             cell = Roo.get(t).findParent('td', false, true);
8054             if (!cell) {
8055                 Roo.log("failed to find th in tbody?");
8056                 Roo.log(e.getTarget());
8057                 return;
8058             }
8059         }
8060         
8061         var row = cell.findParent('tr', false, true);
8062         var cellIndex = cell.dom.cellIndex;
8063         var rowIndex = row.dom.rowIndex - 1;
8064         
8065         if(row !== false){
8066             
8067             this.fireEvent("row" + name, this, rowIndex, e);
8068             
8069             if(cell !== false){
8070             
8071                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8072             }
8073         }
8074         
8075     },
8076     
8077     onMouseover : function(e, el)
8078     {
8079         var cell = Roo.get(el);
8080         
8081         if(!cell){
8082             return;
8083         }
8084         
8085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8086             cell = cell.findParent('td', false, true);
8087         }
8088         
8089         var row = cell.findParent('tr', false, true);
8090         var cellIndex = cell.dom.cellIndex;
8091         var rowIndex = row.dom.rowIndex - 1; // start from 0
8092         
8093         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8094         
8095     },
8096     
8097     onMouseout : function(e, el)
8098     {
8099         var cell = Roo.get(el);
8100         
8101         if(!cell){
8102             return;
8103         }
8104         
8105         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8106             cell = cell.findParent('td', false, true);
8107         }
8108         
8109         var row = cell.findParent('tr', false, true);
8110         var cellIndex = cell.dom.cellIndex;
8111         var rowIndex = row.dom.rowIndex - 1; // start from 0
8112         
8113         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8114         
8115     },
8116     
8117     onClick : function(e, el)
8118     {
8119         var cell = Roo.get(el);
8120         
8121         if(!cell || (!this.cellSelection && !this.rowSelection)){
8122             return;
8123         }
8124         
8125         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126             cell = cell.findParent('td', false, true);
8127         }
8128         
8129         if(!cell || typeof(cell) == 'undefined'){
8130             return;
8131         }
8132         
8133         var row = cell.findParent('tr', false, true);
8134         
8135         if(!row || typeof(row) == 'undefined'){
8136             return;
8137         }
8138         
8139         var cellIndex = cell.dom.cellIndex;
8140         var rowIndex = this.getRowIndex(row);
8141         
8142         // why??? - should these not be based on SelectionModel?
8143         if(this.cellSelection){
8144             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8145         }
8146         
8147         if(this.rowSelection){
8148             this.fireEvent('rowclick', this, row, rowIndex, e);
8149         }
8150         
8151         
8152     },
8153         
8154     onDblClick : function(e,el)
8155     {
8156         var cell = Roo.get(el);
8157         
8158         if(!cell || (!this.cellSelection && !this.rowSelection)){
8159             return;
8160         }
8161         
8162         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8163             cell = cell.findParent('td', false, true);
8164         }
8165         
8166         if(!cell || typeof(cell) == 'undefined'){
8167             return;
8168         }
8169         
8170         var row = cell.findParent('tr', false, true);
8171         
8172         if(!row || typeof(row) == 'undefined'){
8173             return;
8174         }
8175         
8176         var cellIndex = cell.dom.cellIndex;
8177         var rowIndex = this.getRowIndex(row);
8178         
8179         if(this.cellSelection){
8180             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8181         }
8182         
8183         if(this.rowSelection){
8184             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8185         }
8186     },
8187     
8188     sort : function(e,el)
8189     {
8190         var col = Roo.get(el);
8191         
8192         if(!col.hasClass('sortable')){
8193             return;
8194         }
8195         
8196         var sort = col.attr('sort');
8197         var dir = 'ASC';
8198         
8199         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8200             dir = 'DESC';
8201         }
8202         
8203         this.store.sortInfo = {field : sort, direction : dir};
8204         
8205         if (this.footer) {
8206             Roo.log("calling footer first");
8207             this.footer.onClick('first');
8208         } else {
8209         
8210             this.store.load({ params : { start : 0 } });
8211         }
8212     },
8213     
8214     renderHeader : function()
8215     {
8216         var header = {
8217             tag: 'thead',
8218             cn : []
8219         };
8220         
8221         var cm = this.cm;
8222         this.totalWidth = 0;
8223         
8224         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8225             
8226             var config = cm.config[i];
8227             
8228             var c = {
8229                 tag: 'th',
8230                 cls : 'x-hcol-' + i,
8231                 style : '',
8232                 html: cm.getColumnHeader(i)
8233             };
8234             
8235             var hh = '';
8236             
8237             if(typeof(config.sortable) != 'undefined' && config.sortable){
8238                 c.cls = 'sortable';
8239                 c.html = '<i class="glyphicon"></i>' + c.html;
8240             }
8241             
8242             // could use BS4 hidden-..-down 
8243             
8244             if(typeof(config.lgHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.mdHeader) != 'undefined'){
8249                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8250             }
8251             
8252             if(typeof(config.smHeader) != 'undefined'){
8253                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8254             }
8255             
8256             if(typeof(config.xsHeader) != 'undefined'){
8257                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8258             }
8259             
8260             if(hh.length){
8261                 c.html = hh;
8262             }
8263             
8264             if(typeof(config.tooltip) != 'undefined'){
8265                 c.tooltip = config.tooltip;
8266             }
8267             
8268             if(typeof(config.colspan) != 'undefined'){
8269                 c.colspan = config.colspan;
8270             }
8271             
8272             if(typeof(config.hidden) != 'undefined' && config.hidden){
8273                 c.style += ' display:none;';
8274             }
8275             
8276             if(typeof(config.dataIndex) != 'undefined'){
8277                 c.sort = config.dataIndex;
8278             }
8279             
8280            
8281             
8282             if(typeof(config.align) != 'undefined' && config.align.length){
8283                 c.style += ' text-align:' + config.align + ';';
8284             }
8285             
8286             if(typeof(config.width) != 'undefined'){
8287                 c.style += ' width:' + config.width + 'px;';
8288                 this.totalWidth += config.width;
8289             } else {
8290                 this.totalWidth += 100; // assume minimum of 100 per column?
8291             }
8292             
8293             if(typeof(config.cls) != 'undefined'){
8294                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8295             }
8296             
8297             ['xs','sm','md','lg'].map(function(size){
8298                 
8299                 if(typeof(config[size]) == 'undefined'){
8300                     return;
8301                 }
8302                  
8303                 if (!config[size]) { // 0 = hidden
8304                     // BS 4 '0' is treated as hide that column and below.
8305                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8306                     return;
8307                 }
8308                 
8309                 c.cls += ' col-' + size + '-' + config[size] + (
8310                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8311                 );
8312                 
8313                 
8314             });
8315             
8316             header.cn.push(c)
8317         }
8318         
8319         return header;
8320     },
8321     
8322     renderBody : function()
8323     {
8324         var body = {
8325             tag: 'tbody',
8326             cn : [
8327                 {
8328                     tag: 'tr',
8329                     cn : [
8330                         {
8331                             tag : 'td',
8332                             colspan :  this.cm.getColumnCount()
8333                         }
8334                     ]
8335                 }
8336             ]
8337         };
8338         
8339         return body;
8340     },
8341     
8342     renderFooter : function()
8343     {
8344         var footer = {
8345             tag: 'tfoot',
8346             cn : [
8347                 {
8348                     tag: 'tr',
8349                     cn : [
8350                         {
8351                             tag : 'td',
8352                             colspan :  this.cm.getColumnCount()
8353                         }
8354                     ]
8355                 }
8356             ]
8357         };
8358         
8359         return footer;
8360     },
8361     
8362     
8363     
8364     onLoad : function()
8365     {
8366 //        Roo.log('ds onload');
8367         this.clear();
8368         
8369         var _this = this;
8370         var cm = this.cm;
8371         var ds = this.store;
8372         
8373         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8374             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8375             if (_this.store.sortInfo) {
8376                     
8377                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8378                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8379                 }
8380                 
8381                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8382                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8383                 }
8384             }
8385         });
8386         
8387         var tbody =  this.mainBody;
8388               
8389         if(ds.getCount() > 0){
8390             ds.data.each(function(d,rowIndex){
8391                 var row =  this.renderRow(cm, ds, rowIndex);
8392                 
8393                 tbody.createChild(row);
8394                 
8395                 var _this = this;
8396                 
8397                 if(row.cellObjects.length){
8398                     Roo.each(row.cellObjects, function(r){
8399                         _this.renderCellObject(r);
8400                     })
8401                 }
8402                 
8403             }, this);
8404         }
8405         
8406         var tfoot = this.el.select('tfoot', true).first();
8407         
8408         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8409             
8410             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8411             
8412             var total = this.ds.getTotalCount();
8413             
8414             if(this.footer.pageSize < total){
8415                 this.mainFoot.show();
8416             }
8417         }
8418         
8419         Roo.each(this.el.select('tbody td', true).elements, function(e){
8420             e.on('mouseover', _this.onMouseover, _this);
8421         });
8422         
8423         Roo.each(this.el.select('tbody td', true).elements, function(e){
8424             e.on('mouseout', _this.onMouseout, _this);
8425         });
8426         this.fireEvent('rowsrendered', this);
8427         
8428         this.autoSize();
8429     },
8430     
8431     
8432     onUpdate : function(ds,record)
8433     {
8434         this.refreshRow(record);
8435         this.autoSize();
8436     },
8437     
8438     onRemove : function(ds, record, index, isUpdate){
8439         if(isUpdate !== true){
8440             this.fireEvent("beforerowremoved", this, index, record);
8441         }
8442         var bt = this.mainBody.dom;
8443         
8444         var rows = this.el.select('tbody > tr', true).elements;
8445         
8446         if(typeof(rows[index]) != 'undefined'){
8447             bt.removeChild(rows[index].dom);
8448         }
8449         
8450 //        if(bt.rows[index]){
8451 //            bt.removeChild(bt.rows[index]);
8452 //        }
8453         
8454         if(isUpdate !== true){
8455             //this.stripeRows(index);
8456             //this.syncRowHeights(index, index);
8457             //this.layout();
8458             this.fireEvent("rowremoved", this, index, record);
8459         }
8460     },
8461     
8462     onAdd : function(ds, records, rowIndex)
8463     {
8464         //Roo.log('on Add called');
8465         // - note this does not handle multiple adding very well..
8466         var bt = this.mainBody.dom;
8467         for (var i =0 ; i < records.length;i++) {
8468             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8469             //Roo.log(records[i]);
8470             //Roo.log(this.store.getAt(rowIndex+i));
8471             this.insertRow(this.store, rowIndex + i, false);
8472             return;
8473         }
8474         
8475     },
8476     
8477     
8478     refreshRow : function(record){
8479         var ds = this.store, index;
8480         if(typeof record == 'number'){
8481             index = record;
8482             record = ds.getAt(index);
8483         }else{
8484             index = ds.indexOf(record);
8485             if (index < 0) {
8486                 return; // should not happen - but seems to 
8487             }
8488         }
8489         this.insertRow(ds, index, true);
8490         this.autoSize();
8491         this.onRemove(ds, record, index+1, true);
8492         this.autoSize();
8493         //this.syncRowHeights(index, index);
8494         //this.layout();
8495         this.fireEvent("rowupdated", this, index, record);
8496     },
8497     
8498     insertRow : function(dm, rowIndex, isUpdate){
8499         
8500         if(!isUpdate){
8501             this.fireEvent("beforerowsinserted", this, rowIndex);
8502         }
8503             //var s = this.getScrollState();
8504         var row = this.renderRow(this.cm, this.store, rowIndex);
8505         // insert before rowIndex..
8506         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8507         
8508         var _this = this;
8509                 
8510         if(row.cellObjects.length){
8511             Roo.each(row.cellObjects, function(r){
8512                 _this.renderCellObject(r);
8513             })
8514         }
8515             
8516         if(!isUpdate){
8517             this.fireEvent("rowsinserted", this, rowIndex);
8518             //this.syncRowHeights(firstRow, lastRow);
8519             //this.stripeRows(firstRow);
8520             //this.layout();
8521         }
8522         
8523     },
8524     
8525     
8526     getRowDom : function(rowIndex)
8527     {
8528         var rows = this.el.select('tbody > tr', true).elements;
8529         
8530         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8531         
8532     },
8533     // returns the object tree for a tr..
8534   
8535     
8536     renderRow : function(cm, ds, rowIndex) 
8537     {
8538         var d = ds.getAt(rowIndex);
8539         
8540         var row = {
8541             tag : 'tr',
8542             cls : 'x-row-' + rowIndex,
8543             cn : []
8544         };
8545             
8546         var cellObjects = [];
8547         
8548         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8549             var config = cm.config[i];
8550             
8551             var renderer = cm.getRenderer(i);
8552             var value = '';
8553             var id = false;
8554             
8555             if(typeof(renderer) !== 'undefined'){
8556                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8557             }
8558             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8559             // and are rendered into the cells after the row is rendered - using the id for the element.
8560             
8561             if(typeof(value) === 'object'){
8562                 id = Roo.id();
8563                 cellObjects.push({
8564                     container : id,
8565                     cfg : value 
8566                 })
8567             }
8568             
8569             var rowcfg = {
8570                 record: d,
8571                 rowIndex : rowIndex,
8572                 colIndex : i,
8573                 rowClass : ''
8574             };
8575
8576             this.fireEvent('rowclass', this, rowcfg);
8577             
8578             var td = {
8579                 tag: 'td',
8580                 cls : rowcfg.rowClass + ' x-col-' + i,
8581                 style: '',
8582                 html: (typeof(value) === 'object') ? '' : value
8583             };
8584             
8585             if (id) {
8586                 td.id = id;
8587             }
8588             
8589             if(typeof(config.colspan) != 'undefined'){
8590                 td.colspan = config.colspan;
8591             }
8592             
8593             if(typeof(config.hidden) != 'undefined' && config.hidden){
8594                 td.style += ' display:none;';
8595             }
8596             
8597             if(typeof(config.align) != 'undefined' && config.align.length){
8598                 td.style += ' text-align:' + config.align + ';';
8599             }
8600             if(typeof(config.valign) != 'undefined' && config.valign.length){
8601                 td.style += ' vertical-align:' + config.valign + ';';
8602             }
8603             
8604             if(typeof(config.width) != 'undefined'){
8605                 td.style += ' width:' +  config.width + 'px;';
8606             }
8607             
8608             if(typeof(config.cursor) != 'undefined'){
8609                 td.style += ' cursor:' +  config.cursor + ';';
8610             }
8611             
8612             if(typeof(config.cls) != 'undefined'){
8613                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8614             }
8615             
8616             ['xs','sm','md','lg'].map(function(size){
8617                 
8618                 if(typeof(config[size]) == 'undefined'){
8619                     return;
8620                 }
8621                 
8622                 
8623                   
8624                 if (!config[size]) { // 0 = hidden
8625                     // BS 4 '0' is treated as hide that column and below.
8626                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8627                     return;
8628                 }
8629                 
8630                 td.cls += ' col-' + size + '-' + config[size] + (
8631                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8632                 );
8633                  
8634
8635             });
8636             
8637             row.cn.push(td);
8638            
8639         }
8640         
8641         row.cellObjects = cellObjects;
8642         
8643         return row;
8644           
8645     },
8646     
8647     
8648     
8649     onBeforeLoad : function()
8650     {
8651         
8652     },
8653      /**
8654      * Remove all rows
8655      */
8656     clear : function()
8657     {
8658         this.el.select('tbody', true).first().dom.innerHTML = '';
8659     },
8660     /**
8661      * Show or hide a row.
8662      * @param {Number} rowIndex to show or hide
8663      * @param {Boolean} state hide
8664      */
8665     setRowVisibility : function(rowIndex, state)
8666     {
8667         var bt = this.mainBody.dom;
8668         
8669         var rows = this.el.select('tbody > tr', true).elements;
8670         
8671         if(typeof(rows[rowIndex]) == 'undefined'){
8672             return;
8673         }
8674         rows[rowIndex].dom.style.display = state ? '' : 'none';
8675     },
8676     
8677     
8678     getSelectionModel : function(){
8679         if(!this.selModel){
8680             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8681         }
8682         return this.selModel;
8683     },
8684     /*
8685      * Render the Roo.bootstrap object from renderder
8686      */
8687     renderCellObject : function(r)
8688     {
8689         var _this = this;
8690         
8691         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8692         
8693         var t = r.cfg.render(r.container);
8694         
8695         if(r.cfg.cn){
8696             Roo.each(r.cfg.cn, function(c){
8697                 var child = {
8698                     container: t.getChildContainer(),
8699                     cfg: c
8700                 };
8701                 _this.renderCellObject(child);
8702             })
8703         }
8704     },
8705     
8706     getRowIndex : function(row)
8707     {
8708         var rowIndex = -1;
8709         
8710         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8711             if(el != row){
8712                 return;
8713             }
8714             
8715             rowIndex = index;
8716         });
8717         
8718         return rowIndex;
8719     },
8720      /**
8721      * Returns the grid's underlying element = used by panel.Grid
8722      * @return {Element} The element
8723      */
8724     getGridEl : function(){
8725         return this.el;
8726     },
8727      /**
8728      * Forces a resize - used by panel.Grid
8729      * @return {Element} The element
8730      */
8731     autoSize : function()
8732     {
8733         //var ctr = Roo.get(this.container.dom.parentElement);
8734         var ctr = Roo.get(this.el.dom);
8735         
8736         var thd = this.getGridEl().select('thead',true).first();
8737         var tbd = this.getGridEl().select('tbody', true).first();
8738         var tfd = this.getGridEl().select('tfoot', true).first();
8739         
8740         var cw = ctr.getWidth();
8741         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8742         
8743         if (tbd) {
8744             
8745             tbd.setWidth(ctr.getWidth());
8746             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8747             // this needs fixing for various usage - currently only hydra job advers I think..
8748             //tdb.setHeight(
8749             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8750             //); 
8751             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8752             cw -= barsize;
8753         }
8754         cw = Math.max(cw, this.totalWidth);
8755         this.getGridEl().select('tbody tr',true).setWidth(cw);
8756         
8757         // resize 'expandable coloumn?
8758         
8759         return; // we doe not have a view in this design..
8760         
8761     },
8762     onBodyScroll: function()
8763     {
8764         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8765         if(this.mainHead){
8766             this.mainHead.setStyle({
8767                 'position' : 'relative',
8768                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8769             });
8770         }
8771         
8772         if(this.lazyLoad){
8773             
8774             var scrollHeight = this.mainBody.dom.scrollHeight;
8775             
8776             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8777             
8778             var height = this.mainBody.getHeight();
8779             
8780             if(scrollHeight - height == scrollTop) {
8781                 
8782                 var total = this.ds.getTotalCount();
8783                 
8784                 if(this.footer.cursor + this.footer.pageSize < total){
8785                     
8786                     this.footer.ds.load({
8787                         params : {
8788                             start : this.footer.cursor + this.footer.pageSize,
8789                             limit : this.footer.pageSize
8790                         },
8791                         add : true
8792                     });
8793                 }
8794             }
8795             
8796         }
8797     },
8798     
8799     onHeaderChange : function()
8800     {
8801         var header = this.renderHeader();
8802         var table = this.el.select('table', true).first();
8803         
8804         this.mainHead.remove();
8805         this.mainHead = table.createChild(header, this.mainBody, false);
8806     },
8807     
8808     onHiddenChange : function(colModel, colIndex, hidden)
8809     {
8810         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8811         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8812         
8813         this.CSS.updateRule(thSelector, "display", "");
8814         this.CSS.updateRule(tdSelector, "display", "");
8815         
8816         if(hidden){
8817             this.CSS.updateRule(thSelector, "display", "none");
8818             this.CSS.updateRule(tdSelector, "display", "none");
8819         }
8820         
8821         this.onHeaderChange();
8822         this.onLoad();
8823     },
8824     
8825     setColumnWidth: function(col_index, width)
8826     {
8827         // width = "md-2 xs-2..."
8828         if(!this.colModel.config[col_index]) {
8829             return;
8830         }
8831         
8832         var w = width.split(" ");
8833         
8834         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8835         
8836         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8837         
8838         
8839         for(var j = 0; j < w.length; j++) {
8840             
8841             if(!w[j]) {
8842                 continue;
8843             }
8844             
8845             var size_cls = w[j].split("-");
8846             
8847             if(!Number.isInteger(size_cls[1] * 1)) {
8848                 continue;
8849             }
8850             
8851             if(!this.colModel.config[col_index][size_cls[0]]) {
8852                 continue;
8853             }
8854             
8855             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8856                 continue;
8857             }
8858             
8859             h_row[0].classList.replace(
8860                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8861                 "col-"+size_cls[0]+"-"+size_cls[1]
8862             );
8863             
8864             for(var i = 0; i < rows.length; i++) {
8865                 
8866                 var size_cls = w[j].split("-");
8867                 
8868                 if(!Number.isInteger(size_cls[1] * 1)) {
8869                     continue;
8870                 }
8871                 
8872                 if(!this.colModel.config[col_index][size_cls[0]]) {
8873                     continue;
8874                 }
8875                 
8876                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8877                     continue;
8878                 }
8879                 
8880                 rows[i].classList.replace(
8881                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882                     "col-"+size_cls[0]+"-"+size_cls[1]
8883                 );
8884             }
8885             
8886             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8887         }
8888     }
8889 });
8890
8891  
8892
8893  /*
8894  * - LGPL
8895  *
8896  * table cell
8897  * 
8898  */
8899
8900 /**
8901  * @class Roo.bootstrap.TableCell
8902  * @extends Roo.bootstrap.Component
8903  * Bootstrap TableCell class
8904  * @cfg {String} html cell contain text
8905  * @cfg {String} cls cell class
8906  * @cfg {String} tag cell tag (td|th) default td
8907  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8908  * @cfg {String} align Aligns the content in a cell
8909  * @cfg {String} axis Categorizes cells
8910  * @cfg {String} bgcolor Specifies the background color of a cell
8911  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8912  * @cfg {Number} colspan Specifies the number of columns a cell should span
8913  * @cfg {String} headers Specifies one or more header cells a cell is related to
8914  * @cfg {Number} height Sets the height of a cell
8915  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8916  * @cfg {Number} rowspan Sets the number of rows a cell should span
8917  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8918  * @cfg {String} valign Vertical aligns the content in a cell
8919  * @cfg {Number} width Specifies the width of a cell
8920  * 
8921  * @constructor
8922  * Create a new TableCell
8923  * @param {Object} config The config object
8924  */
8925
8926 Roo.bootstrap.TableCell = function(config){
8927     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8928 };
8929
8930 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8931     
8932     html: false,
8933     cls: false,
8934     tag: false,
8935     abbr: false,
8936     align: false,
8937     axis: false,
8938     bgcolor: false,
8939     charoff: false,
8940     colspan: false,
8941     headers: false,
8942     height: false,
8943     nowrap: false,
8944     rowspan: false,
8945     scope: false,
8946     valign: false,
8947     width: false,
8948     
8949     
8950     getAutoCreate : function(){
8951         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8952         
8953         cfg = {
8954             tag: 'td'
8955         };
8956         
8957         if(this.tag){
8958             cfg.tag = this.tag;
8959         }
8960         
8961         if (this.html) {
8962             cfg.html=this.html
8963         }
8964         if (this.cls) {
8965             cfg.cls=this.cls
8966         }
8967         if (this.abbr) {
8968             cfg.abbr=this.abbr
8969         }
8970         if (this.align) {
8971             cfg.align=this.align
8972         }
8973         if (this.axis) {
8974             cfg.axis=this.axis
8975         }
8976         if (this.bgcolor) {
8977             cfg.bgcolor=this.bgcolor
8978         }
8979         if (this.charoff) {
8980             cfg.charoff=this.charoff
8981         }
8982         if (this.colspan) {
8983             cfg.colspan=this.colspan
8984         }
8985         if (this.headers) {
8986             cfg.headers=this.headers
8987         }
8988         if (this.height) {
8989             cfg.height=this.height
8990         }
8991         if (this.nowrap) {
8992             cfg.nowrap=this.nowrap
8993         }
8994         if (this.rowspan) {
8995             cfg.rowspan=this.rowspan
8996         }
8997         if (this.scope) {
8998             cfg.scope=this.scope
8999         }
9000         if (this.valign) {
9001             cfg.valign=this.valign
9002         }
9003         if (this.width) {
9004             cfg.width=this.width
9005         }
9006         
9007         
9008         return cfg;
9009     }
9010    
9011 });
9012
9013  
9014
9015  /*
9016  * - LGPL
9017  *
9018  * table row
9019  * 
9020  */
9021
9022 /**
9023  * @class Roo.bootstrap.TableRow
9024  * @extends Roo.bootstrap.Component
9025  * Bootstrap TableRow class
9026  * @cfg {String} cls row class
9027  * @cfg {String} align Aligns the content in a table row
9028  * @cfg {String} bgcolor Specifies a background color for a table row
9029  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9030  * @cfg {String} valign Vertical aligns the content in a table row
9031  * 
9032  * @constructor
9033  * Create a new TableRow
9034  * @param {Object} config The config object
9035  */
9036
9037 Roo.bootstrap.TableRow = function(config){
9038     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9039 };
9040
9041 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9042     
9043     cls: false,
9044     align: false,
9045     bgcolor: false,
9046     charoff: false,
9047     valign: false,
9048     
9049     getAutoCreate : function(){
9050         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9051         
9052         cfg = {
9053             tag: 'tr'
9054         };
9055             
9056         if(this.cls){
9057             cfg.cls = this.cls;
9058         }
9059         if(this.align){
9060             cfg.align = this.align;
9061         }
9062         if(this.bgcolor){
9063             cfg.bgcolor = this.bgcolor;
9064         }
9065         if(this.charoff){
9066             cfg.charoff = this.charoff;
9067         }
9068         if(this.valign){
9069             cfg.valign = this.valign;
9070         }
9071         
9072         return cfg;
9073     }
9074    
9075 });
9076
9077  
9078
9079  /*
9080  * - LGPL
9081  *
9082  * table body
9083  * 
9084  */
9085
9086 /**
9087  * @class Roo.bootstrap.TableBody
9088  * @extends Roo.bootstrap.Component
9089  * Bootstrap TableBody class
9090  * @cfg {String} cls element class
9091  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9092  * @cfg {String} align Aligns the content inside the element
9093  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9094  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9095  * 
9096  * @constructor
9097  * Create a new TableBody
9098  * @param {Object} config The config object
9099  */
9100
9101 Roo.bootstrap.TableBody = function(config){
9102     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9103 };
9104
9105 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9106     
9107     cls: false,
9108     tag: false,
9109     align: false,
9110     charoff: false,
9111     valign: false,
9112     
9113     getAutoCreate : function(){
9114         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9115         
9116         cfg = {
9117             tag: 'tbody'
9118         };
9119             
9120         if (this.cls) {
9121             cfg.cls=this.cls
9122         }
9123         if(this.tag){
9124             cfg.tag = this.tag;
9125         }
9126         
9127         if(this.align){
9128             cfg.align = this.align;
9129         }
9130         if(this.charoff){
9131             cfg.charoff = this.charoff;
9132         }
9133         if(this.valign){
9134             cfg.valign = this.valign;
9135         }
9136         
9137         return cfg;
9138     }
9139     
9140     
9141 //    initEvents : function()
9142 //    {
9143 //        
9144 //        if(!this.store){
9145 //            return;
9146 //        }
9147 //        
9148 //        this.store = Roo.factory(this.store, Roo.data);
9149 //        this.store.on('load', this.onLoad, this);
9150 //        
9151 //        this.store.load();
9152 //        
9153 //    },
9154 //    
9155 //    onLoad: function () 
9156 //    {   
9157 //        this.fireEvent('load', this);
9158 //    }
9159 //    
9160 //   
9161 });
9162
9163  
9164
9165  /*
9166  * Based on:
9167  * Ext JS Library 1.1.1
9168  * Copyright(c) 2006-2007, Ext JS, LLC.
9169  *
9170  * Originally Released Under LGPL - original licence link has changed is not relivant.
9171  *
9172  * Fork - LGPL
9173  * <script type="text/javascript">
9174  */
9175
9176 // as we use this in bootstrap.
9177 Roo.namespace('Roo.form');
9178  /**
9179  * @class Roo.form.Action
9180  * Internal Class used to handle form actions
9181  * @constructor
9182  * @param {Roo.form.BasicForm} el The form element or its id
9183  * @param {Object} config Configuration options
9184  */
9185
9186  
9187  
9188 // define the action interface
9189 Roo.form.Action = function(form, options){
9190     this.form = form;
9191     this.options = options || {};
9192 };
9193 /**
9194  * Client Validation Failed
9195  * @const 
9196  */
9197 Roo.form.Action.CLIENT_INVALID = 'client';
9198 /**
9199  * Server Validation Failed
9200  * @const 
9201  */
9202 Roo.form.Action.SERVER_INVALID = 'server';
9203  /**
9204  * Connect to Server Failed
9205  * @const 
9206  */
9207 Roo.form.Action.CONNECT_FAILURE = 'connect';
9208 /**
9209  * Reading Data from Server Failed
9210  * @const 
9211  */
9212 Roo.form.Action.LOAD_FAILURE = 'load';
9213
9214 Roo.form.Action.prototype = {
9215     type : 'default',
9216     failureType : undefined,
9217     response : undefined,
9218     result : undefined,
9219
9220     // interface method
9221     run : function(options){
9222
9223     },
9224
9225     // interface method
9226     success : function(response){
9227
9228     },
9229
9230     // interface method
9231     handleResponse : function(response){
9232
9233     },
9234
9235     // default connection failure
9236     failure : function(response){
9237         
9238         this.response = response;
9239         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9240         this.form.afterAction(this, false);
9241     },
9242
9243     processResponse : function(response){
9244         this.response = response;
9245         if(!response.responseText){
9246             return true;
9247         }
9248         this.result = this.handleResponse(response);
9249         return this.result;
9250     },
9251
9252     // utility functions used internally
9253     getUrl : function(appendParams){
9254         var url = this.options.url || this.form.url || this.form.el.dom.action;
9255         if(appendParams){
9256             var p = this.getParams();
9257             if(p){
9258                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9259             }
9260         }
9261         return url;
9262     },
9263
9264     getMethod : function(){
9265         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9266     },
9267
9268     getParams : function(){
9269         var bp = this.form.baseParams;
9270         var p = this.options.params;
9271         if(p){
9272             if(typeof p == "object"){
9273                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9274             }else if(typeof p == 'string' && bp){
9275                 p += '&' + Roo.urlEncode(bp);
9276             }
9277         }else if(bp){
9278             p = Roo.urlEncode(bp);
9279         }
9280         return p;
9281     },
9282
9283     createCallback : function(){
9284         return {
9285             success: this.success,
9286             failure: this.failure,
9287             scope: this,
9288             timeout: (this.form.timeout*1000),
9289             upload: this.form.fileUpload ? this.success : undefined
9290         };
9291     }
9292 };
9293
9294 Roo.form.Action.Submit = function(form, options){
9295     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9296 };
9297
9298 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9299     type : 'submit',
9300
9301     haveProgress : false,
9302     uploadComplete : false,
9303     
9304     // uploadProgress indicator.
9305     uploadProgress : function()
9306     {
9307         if (!this.form.progressUrl) {
9308             return;
9309         }
9310         
9311         if (!this.haveProgress) {
9312             Roo.MessageBox.progress("Uploading", "Uploading");
9313         }
9314         if (this.uploadComplete) {
9315            Roo.MessageBox.hide();
9316            return;
9317         }
9318         
9319         this.haveProgress = true;
9320    
9321         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9322         
9323         var c = new Roo.data.Connection();
9324         c.request({
9325             url : this.form.progressUrl,
9326             params: {
9327                 id : uid
9328             },
9329             method: 'GET',
9330             success : function(req){
9331                //console.log(data);
9332                 var rdata = false;
9333                 var edata;
9334                 try  {
9335                    rdata = Roo.decode(req.responseText)
9336                 } catch (e) {
9337                     Roo.log("Invalid data from server..");
9338                     Roo.log(edata);
9339                     return;
9340                 }
9341                 if (!rdata || !rdata.success) {
9342                     Roo.log(rdata);
9343                     Roo.MessageBox.alert(Roo.encode(rdata));
9344                     return;
9345                 }
9346                 var data = rdata.data;
9347                 
9348                 if (this.uploadComplete) {
9349                    Roo.MessageBox.hide();
9350                    return;
9351                 }
9352                    
9353                 if (data){
9354                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9355                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9356                     );
9357                 }
9358                 this.uploadProgress.defer(2000,this);
9359             },
9360        
9361             failure: function(data) {
9362                 Roo.log('progress url failed ');
9363                 Roo.log(data);
9364             },
9365             scope : this
9366         });
9367            
9368     },
9369     
9370     
9371     run : function()
9372     {
9373         // run get Values on the form, so it syncs any secondary forms.
9374         this.form.getValues();
9375         
9376         var o = this.options;
9377         var method = this.getMethod();
9378         var isPost = method == 'POST';
9379         if(o.clientValidation === false || this.form.isValid()){
9380             
9381             if (this.form.progressUrl) {
9382                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9383                     (new Date() * 1) + '' + Math.random());
9384                     
9385             } 
9386             
9387             
9388             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9389                 form:this.form.el.dom,
9390                 url:this.getUrl(!isPost),
9391                 method: method,
9392                 params:isPost ? this.getParams() : null,
9393                 isUpload: this.form.fileUpload,
9394                 formData : this.form.formData
9395             }));
9396             
9397             this.uploadProgress();
9398
9399         }else if (o.clientValidation !== false){ // client validation failed
9400             this.failureType = Roo.form.Action.CLIENT_INVALID;
9401             this.form.afterAction(this, false);
9402         }
9403     },
9404
9405     success : function(response)
9406     {
9407         this.uploadComplete= true;
9408         if (this.haveProgress) {
9409             Roo.MessageBox.hide();
9410         }
9411         
9412         
9413         var result = this.processResponse(response);
9414         if(result === true || result.success){
9415             this.form.afterAction(this, true);
9416             return;
9417         }
9418         if(result.errors){
9419             this.form.markInvalid(result.errors);
9420             this.failureType = Roo.form.Action.SERVER_INVALID;
9421         }
9422         this.form.afterAction(this, false);
9423     },
9424     failure : function(response)
9425     {
9426         this.uploadComplete= true;
9427         if (this.haveProgress) {
9428             Roo.MessageBox.hide();
9429         }
9430         
9431         this.response = response;
9432         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9433         this.form.afterAction(this, false);
9434     },
9435     
9436     handleResponse : function(response){
9437         if(this.form.errorReader){
9438             var rs = this.form.errorReader.read(response);
9439             var errors = [];
9440             if(rs.records){
9441                 for(var i = 0, len = rs.records.length; i < len; i++) {
9442                     var r = rs.records[i];
9443                     errors[i] = r.data;
9444                 }
9445             }
9446             if(errors.length < 1){
9447                 errors = null;
9448             }
9449             return {
9450                 success : rs.success,
9451                 errors : errors
9452             };
9453         }
9454         var ret = false;
9455         try {
9456             ret = Roo.decode(response.responseText);
9457         } catch (e) {
9458             ret = {
9459                 success: false,
9460                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9461                 errors : []
9462             };
9463         }
9464         return ret;
9465         
9466     }
9467 });
9468
9469
9470 Roo.form.Action.Load = function(form, options){
9471     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9472     this.reader = this.form.reader;
9473 };
9474
9475 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9476     type : 'load',
9477
9478     run : function(){
9479         
9480         Roo.Ajax.request(Roo.apply(
9481                 this.createCallback(), {
9482                     method:this.getMethod(),
9483                     url:this.getUrl(false),
9484                     params:this.getParams()
9485         }));
9486     },
9487
9488     success : function(response){
9489         
9490         var result = this.processResponse(response);
9491         if(result === true || !result.success || !result.data){
9492             this.failureType = Roo.form.Action.LOAD_FAILURE;
9493             this.form.afterAction(this, false);
9494             return;
9495         }
9496         this.form.clearInvalid();
9497         this.form.setValues(result.data);
9498         this.form.afterAction(this, true);
9499     },
9500
9501     handleResponse : function(response){
9502         if(this.form.reader){
9503             var rs = this.form.reader.read(response);
9504             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9505             return {
9506                 success : rs.success,
9507                 data : data
9508             };
9509         }
9510         return Roo.decode(response.responseText);
9511     }
9512 });
9513
9514 Roo.form.Action.ACTION_TYPES = {
9515     'load' : Roo.form.Action.Load,
9516     'submit' : Roo.form.Action.Submit
9517 };/*
9518  * - LGPL
9519  *
9520  * form
9521  *
9522  */
9523
9524 /**
9525  * @class Roo.bootstrap.Form
9526  * @extends Roo.bootstrap.Component
9527  * Bootstrap Form class
9528  * @cfg {String} method  GET | POST (default POST)
9529  * @cfg {String} labelAlign top | left (default top)
9530  * @cfg {String} align left  | right - for navbars
9531  * @cfg {Boolean} loadMask load mask when submit (default true)
9532
9533  *
9534  * @constructor
9535  * Create a new Form
9536  * @param {Object} config The config object
9537  */
9538
9539
9540 Roo.bootstrap.Form = function(config){
9541     
9542     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9543     
9544     Roo.bootstrap.Form.popover.apply();
9545     
9546     this.addEvents({
9547         /**
9548          * @event clientvalidation
9549          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9550          * @param {Form} this
9551          * @param {Boolean} valid true if the form has passed client-side validation
9552          */
9553         clientvalidation: true,
9554         /**
9555          * @event beforeaction
9556          * Fires before any action is performed. Return false to cancel the action.
9557          * @param {Form} this
9558          * @param {Action} action The action to be performed
9559          */
9560         beforeaction: true,
9561         /**
9562          * @event actionfailed
9563          * Fires when an action fails.
9564          * @param {Form} this
9565          * @param {Action} action The action that failed
9566          */
9567         actionfailed : true,
9568         /**
9569          * @event actioncomplete
9570          * Fires when an action is completed.
9571          * @param {Form} this
9572          * @param {Action} action The action that completed
9573          */
9574         actioncomplete : true
9575     });
9576 };
9577
9578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9579
9580      /**
9581      * @cfg {String} method
9582      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9583      */
9584     method : 'POST',
9585     /**
9586      * @cfg {String} url
9587      * The URL to use for form actions if one isn't supplied in the action options.
9588      */
9589     /**
9590      * @cfg {Boolean} fileUpload
9591      * Set to true if this form is a file upload.
9592      */
9593
9594     /**
9595      * @cfg {Object} baseParams
9596      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9597      */
9598
9599     /**
9600      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9601      */
9602     timeout: 30,
9603     /**
9604      * @cfg {Sting} align (left|right) for navbar forms
9605      */
9606     align : 'left',
9607
9608     // private
9609     activeAction : null,
9610
9611     /**
9612      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9613      * element by passing it or its id or mask the form itself by passing in true.
9614      * @type Mixed
9615      */
9616     waitMsgTarget : false,
9617
9618     loadMask : true,
9619     
9620     /**
9621      * @cfg {Boolean} errorMask (true|false) default false
9622      */
9623     errorMask : false,
9624     
9625     /**
9626      * @cfg {Number} maskOffset Default 100
9627      */
9628     maskOffset : 100,
9629     
9630     /**
9631      * @cfg {Boolean} maskBody
9632      */
9633     maskBody : false,
9634
9635     getAutoCreate : function(){
9636
9637         var cfg = {
9638             tag: 'form',
9639             method : this.method || 'POST',
9640             id : this.id || Roo.id(),
9641             cls : ''
9642         };
9643         if (this.parent().xtype.match(/^Nav/)) {
9644             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9645
9646         }
9647
9648         if (this.labelAlign == 'left' ) {
9649             cfg.cls += ' form-horizontal';
9650         }
9651
9652
9653         return cfg;
9654     },
9655     initEvents : function()
9656     {
9657         this.el.on('submit', this.onSubmit, this);
9658         // this was added as random key presses on the form where triggering form submit.
9659         this.el.on('keypress', function(e) {
9660             if (e.getCharCode() != 13) {
9661                 return true;
9662             }
9663             // we might need to allow it for textareas.. and some other items.
9664             // check e.getTarget().
9665
9666             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9667                 return true;
9668             }
9669
9670             Roo.log("keypress blocked");
9671
9672             e.preventDefault();
9673             return false;
9674         });
9675         
9676     },
9677     // private
9678     onSubmit : function(e){
9679         e.stopEvent();
9680     },
9681
9682      /**
9683      * Returns true if client-side validation on the form is successful.
9684      * @return Boolean
9685      */
9686     isValid : function(){
9687         var items = this.getItems();
9688         var valid = true;
9689         var target = false;
9690         
9691         items.each(function(f){
9692             
9693             if(f.validate()){
9694                 return;
9695             }
9696             
9697             Roo.log('invalid field: ' + f.name);
9698             
9699             valid = false;
9700
9701             if(!target && f.el.isVisible(true)){
9702                 target = f;
9703             }
9704            
9705         });
9706         
9707         if(this.errorMask && !valid){
9708             Roo.bootstrap.Form.popover.mask(this, target);
9709         }
9710         
9711         return valid;
9712     },
9713     
9714     /**
9715      * Returns true if any fields in this form have changed since their original load.
9716      * @return Boolean
9717      */
9718     isDirty : function(){
9719         var dirty = false;
9720         var items = this.getItems();
9721         items.each(function(f){
9722            if(f.isDirty()){
9723                dirty = true;
9724                return false;
9725            }
9726            return true;
9727         });
9728         return dirty;
9729     },
9730      /**
9731      * Performs a predefined action (submit or load) or custom actions you define on this form.
9732      * @param {String} actionName The name of the action type
9733      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9734      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9735      * accept other config options):
9736      * <pre>
9737 Property          Type             Description
9738 ----------------  ---------------  ----------------------------------------------------------------------------------
9739 url               String           The url for the action (defaults to the form's url)
9740 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9741 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9742 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9743                                    validate the form on the client (defaults to false)
9744      * </pre>
9745      * @return {BasicForm} this
9746      */
9747     doAction : function(action, options){
9748         if(typeof action == 'string'){
9749             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9750         }
9751         if(this.fireEvent('beforeaction', this, action) !== false){
9752             this.beforeAction(action);
9753             action.run.defer(100, action);
9754         }
9755         return this;
9756     },
9757
9758     // private
9759     beforeAction : function(action){
9760         var o = action.options;
9761         
9762         if(this.loadMask){
9763             
9764             if(this.maskBody){
9765                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9766             } else {
9767                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9768             }
9769         }
9770         // not really supported yet.. ??
9771
9772         //if(this.waitMsgTarget === true){
9773         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else if(this.waitMsgTarget){
9775         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9776         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9777         //}else {
9778         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9779        // }
9780
9781     },
9782
9783     // private
9784     afterAction : function(action, success){
9785         this.activeAction = null;
9786         var o = action.options;
9787
9788         if(this.loadMask){
9789             
9790             if(this.maskBody){
9791                 Roo.get(document.body).unmask();
9792             } else {
9793                 this.el.unmask();
9794             }
9795         }
9796         
9797         //if(this.waitMsgTarget === true){
9798 //            this.el.unmask();
9799         //}else if(this.waitMsgTarget){
9800         //    this.waitMsgTarget.unmask();
9801         //}else{
9802         //    Roo.MessageBox.updateProgress(1);
9803         //    Roo.MessageBox.hide();
9804        // }
9805         //
9806         if(success){
9807             if(o.reset){
9808                 this.reset();
9809             }
9810             Roo.callback(o.success, o.scope, [this, action]);
9811             this.fireEvent('actioncomplete', this, action);
9812
9813         }else{
9814
9815             // failure condition..
9816             // we have a scenario where updates need confirming.
9817             // eg. if a locking scenario exists..
9818             // we look for { errors : { needs_confirm : true }} in the response.
9819             if (
9820                 (typeof(action.result) != 'undefined')  &&
9821                 (typeof(action.result.errors) != 'undefined')  &&
9822                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9823            ){
9824                 var _t = this;
9825                 Roo.log("not supported yet");
9826                  /*
9827
9828                 Roo.MessageBox.confirm(
9829                     "Change requires confirmation",
9830                     action.result.errorMsg,
9831                     function(r) {
9832                         if (r != 'yes') {
9833                             return;
9834                         }
9835                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9836                     }
9837
9838                 );
9839                 */
9840
9841
9842                 return;
9843             }
9844
9845             Roo.callback(o.failure, o.scope, [this, action]);
9846             // show an error message if no failed handler is set..
9847             if (!this.hasListener('actionfailed')) {
9848                 Roo.log("need to add dialog support");
9849                 /*
9850                 Roo.MessageBox.alert("Error",
9851                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9852                         action.result.errorMsg :
9853                         "Saving Failed, please check your entries or try again"
9854                 );
9855                 */
9856             }
9857
9858             this.fireEvent('actionfailed', this, action);
9859         }
9860
9861     },
9862     /**
9863      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9864      * @param {String} id The value to search for
9865      * @return Field
9866      */
9867     findField : function(id){
9868         var items = this.getItems();
9869         var field = items.get(id);
9870         if(!field){
9871              items.each(function(f){
9872                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9873                     field = f;
9874                     return false;
9875                 }
9876                 return true;
9877             });
9878         }
9879         return field || null;
9880     },
9881      /**
9882      * Mark fields in this form invalid in bulk.
9883      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9884      * @return {BasicForm} this
9885      */
9886     markInvalid : function(errors){
9887         if(errors instanceof Array){
9888             for(var i = 0, len = errors.length; i < len; i++){
9889                 var fieldError = errors[i];
9890                 var f = this.findField(fieldError.id);
9891                 if(f){
9892                     f.markInvalid(fieldError.msg);
9893                 }
9894             }
9895         }else{
9896             var field, id;
9897             for(id in errors){
9898                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9899                     field.markInvalid(errors[id]);
9900                 }
9901             }
9902         }
9903         //Roo.each(this.childForms || [], function (f) {
9904         //    f.markInvalid(errors);
9905         //});
9906
9907         return this;
9908     },
9909
9910     /**
9911      * Set values for fields in this form in bulk.
9912      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9913      * @return {BasicForm} this
9914      */
9915     setValues : function(values){
9916         if(values instanceof Array){ // array of objects
9917             for(var i = 0, len = values.length; i < len; i++){
9918                 var v = values[i];
9919                 var f = this.findField(v.id);
9920                 if(f){
9921                     f.setValue(v.value);
9922                     if(this.trackResetOnLoad){
9923                         f.originalValue = f.getValue();
9924                     }
9925                 }
9926             }
9927         }else{ // object hash
9928             var field, id;
9929             for(id in values){
9930                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9931
9932                     if (field.setFromData &&
9933                         field.valueField &&
9934                         field.displayField &&
9935                         // combos' with local stores can
9936                         // be queried via setValue()
9937                         // to set their value..
9938                         (field.store && !field.store.isLocal)
9939                         ) {
9940                         // it's a combo
9941                         var sd = { };
9942                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9943                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9944                         field.setFromData(sd);
9945
9946                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9947                         
9948                         field.setFromData(values);
9949                         
9950                     } else {
9951                         field.setValue(values[id]);
9952                     }
9953
9954
9955                     if(this.trackResetOnLoad){
9956                         field.originalValue = field.getValue();
9957                     }
9958                 }
9959             }
9960         }
9961
9962         //Roo.each(this.childForms || [], function (f) {
9963         //    f.setValues(values);
9964         //});
9965
9966         return this;
9967     },
9968
9969     /**
9970      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9971      * they are returned as an array.
9972      * @param {Boolean} asString
9973      * @return {Object}
9974      */
9975     getValues : function(asString){
9976         //if (this.childForms) {
9977             // copy values from the child forms
9978         //    Roo.each(this.childForms, function (f) {
9979         //        this.setValues(f.getValues());
9980         //    }, this);
9981         //}
9982
9983
9984
9985         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9986         if(asString === true){
9987             return fs;
9988         }
9989         return Roo.urlDecode(fs);
9990     },
9991
9992     /**
9993      * Returns the fields in this form as an object with key/value pairs.
9994      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9995      * @return {Object}
9996      */
9997     getFieldValues : function(with_hidden)
9998     {
9999         var items = this.getItems();
10000         var ret = {};
10001         items.each(function(f){
10002             
10003             if (!f.getName()) {
10004                 return;
10005             }
10006             
10007             var v = f.getValue();
10008             
10009             if (f.inputType =='radio') {
10010                 if (typeof(ret[f.getName()]) == 'undefined') {
10011                     ret[f.getName()] = ''; // empty..
10012                 }
10013
10014                 if (!f.el.dom.checked) {
10015                     return;
10016
10017                 }
10018                 v = f.el.dom.value;
10019
10020             }
10021             
10022             if(f.xtype == 'MoneyField'){
10023                 ret[f.currencyName] = f.getCurrency();
10024             }
10025
10026             // not sure if this supported any more..
10027             if ((typeof(v) == 'object') && f.getRawValue) {
10028                 v = f.getRawValue() ; // dates..
10029             }
10030             // combo boxes where name != hiddenName...
10031             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10032                 ret[f.name] = f.getRawValue();
10033             }
10034             ret[f.getName()] = v;
10035         });
10036
10037         return ret;
10038     },
10039
10040     /**
10041      * Clears all invalid messages in this form.
10042      * @return {BasicForm} this
10043      */
10044     clearInvalid : function(){
10045         var items = this.getItems();
10046
10047         items.each(function(f){
10048            f.clearInvalid();
10049         });
10050
10051         return this;
10052     },
10053
10054     /**
10055      * Resets this form.
10056      * @return {BasicForm} this
10057      */
10058     reset : function(){
10059         var items = this.getItems();
10060         items.each(function(f){
10061             f.reset();
10062         });
10063
10064         Roo.each(this.childForms || [], function (f) {
10065             f.reset();
10066         });
10067
10068
10069         return this;
10070     },
10071     
10072     getItems : function()
10073     {
10074         var r=new Roo.util.MixedCollection(false, function(o){
10075             return o.id || (o.id = Roo.id());
10076         });
10077         var iter = function(el) {
10078             if (el.inputEl) {
10079                 r.add(el);
10080             }
10081             if (!el.items) {
10082                 return;
10083             }
10084             Roo.each(el.items,function(e) {
10085                 iter(e);
10086             });
10087         };
10088
10089         iter(this);
10090         return r;
10091     },
10092     
10093     hideFields : function(items)
10094     {
10095         Roo.each(items, function(i){
10096             
10097             var f = this.findField(i);
10098             
10099             if(!f){
10100                 return;
10101             }
10102             
10103             f.hide();
10104             
10105         }, this);
10106     },
10107     
10108     showFields : function(items)
10109     {
10110         Roo.each(items, function(i){
10111             
10112             var f = this.findField(i);
10113             
10114             if(!f){
10115                 return;
10116             }
10117             
10118             f.show();
10119             
10120         }, this);
10121     }
10122
10123 });
10124
10125 Roo.apply(Roo.bootstrap.Form, {
10126     
10127     popover : {
10128         
10129         padding : 5,
10130         
10131         isApplied : false,
10132         
10133         isMasked : false,
10134         
10135         form : false,
10136         
10137         target : false,
10138         
10139         toolTip : false,
10140         
10141         intervalID : false,
10142         
10143         maskEl : false,
10144         
10145         apply : function()
10146         {
10147             if(this.isApplied){
10148                 return;
10149             }
10150             
10151             this.maskEl = {
10152                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10153                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10154                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10155                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10156             };
10157             
10158             this.maskEl.top.enableDisplayMode("block");
10159             this.maskEl.left.enableDisplayMode("block");
10160             this.maskEl.bottom.enableDisplayMode("block");
10161             this.maskEl.right.enableDisplayMode("block");
10162             
10163             this.toolTip = new Roo.bootstrap.Tooltip({
10164                 cls : 'roo-form-error-popover',
10165                 alignment : {
10166                     'left' : ['r-l', [-2,0], 'right'],
10167                     'right' : ['l-r', [2,0], 'left'],
10168                     'bottom' : ['tl-bl', [0,2], 'top'],
10169                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10170                 }
10171             });
10172             
10173             this.toolTip.render(Roo.get(document.body));
10174
10175             this.toolTip.el.enableDisplayMode("block");
10176             
10177             Roo.get(document.body).on('click', function(){
10178                 this.unmask();
10179             }, this);
10180             
10181             Roo.get(document.body).on('touchstart', function(){
10182                 this.unmask();
10183             }, this);
10184             
10185             this.isApplied = true
10186         },
10187         
10188         mask : function(form, target)
10189         {
10190             this.form = form;
10191             
10192             this.target = target;
10193             
10194             if(!this.form.errorMask || !target.el){
10195                 return;
10196             }
10197             
10198             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10199             
10200             Roo.log(scrollable);
10201             
10202             var ot = this.target.el.calcOffsetsTo(scrollable);
10203             
10204             var scrollTo = ot[1] - this.form.maskOffset;
10205             
10206             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10207             
10208             scrollable.scrollTo('top', scrollTo);
10209             
10210             var box = this.target.el.getBox();
10211             Roo.log(box);
10212             var zIndex = Roo.bootstrap.Modal.zIndex++;
10213
10214             
10215             this.maskEl.top.setStyle('position', 'absolute');
10216             this.maskEl.top.setStyle('z-index', zIndex);
10217             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10218             this.maskEl.top.setLeft(0);
10219             this.maskEl.top.setTop(0);
10220             this.maskEl.top.show();
10221             
10222             this.maskEl.left.setStyle('position', 'absolute');
10223             this.maskEl.left.setStyle('z-index', zIndex);
10224             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10225             this.maskEl.left.setLeft(0);
10226             this.maskEl.left.setTop(box.y - this.padding);
10227             this.maskEl.left.show();
10228
10229             this.maskEl.bottom.setStyle('position', 'absolute');
10230             this.maskEl.bottom.setStyle('z-index', zIndex);
10231             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10232             this.maskEl.bottom.setLeft(0);
10233             this.maskEl.bottom.setTop(box.bottom + this.padding);
10234             this.maskEl.bottom.show();
10235
10236             this.maskEl.right.setStyle('position', 'absolute');
10237             this.maskEl.right.setStyle('z-index', zIndex);
10238             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10239             this.maskEl.right.setLeft(box.right + this.padding);
10240             this.maskEl.right.setTop(box.y - this.padding);
10241             this.maskEl.right.show();
10242
10243             this.toolTip.bindEl = this.target.el;
10244
10245             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10246
10247             var tip = this.target.blankText;
10248
10249             if(this.target.getValue() !== '' ) {
10250                 
10251                 if (this.target.invalidText.length) {
10252                     tip = this.target.invalidText;
10253                 } else if (this.target.regexText.length){
10254                     tip = this.target.regexText;
10255                 }
10256             }
10257
10258             this.toolTip.show(tip);
10259
10260             this.intervalID = window.setInterval(function() {
10261                 Roo.bootstrap.Form.popover.unmask();
10262             }, 10000);
10263
10264             window.onwheel = function(){ return false;};
10265             
10266             (function(){ this.isMasked = true; }).defer(500, this);
10267             
10268         },
10269         
10270         unmask : function()
10271         {
10272             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10273                 return;
10274             }
10275             
10276             this.maskEl.top.setStyle('position', 'absolute');
10277             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.top.hide();
10279
10280             this.maskEl.left.setStyle('position', 'absolute');
10281             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.left.hide();
10283
10284             this.maskEl.bottom.setStyle('position', 'absolute');
10285             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10286             this.maskEl.bottom.hide();
10287
10288             this.maskEl.right.setStyle('position', 'absolute');
10289             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10290             this.maskEl.right.hide();
10291             
10292             this.toolTip.hide();
10293             
10294             this.toolTip.el.hide();
10295             
10296             window.onwheel = function(){ return true;};
10297             
10298             if(this.intervalID){
10299                 window.clearInterval(this.intervalID);
10300                 this.intervalID = false;
10301             }
10302             
10303             this.isMasked = false;
10304             
10305         }
10306         
10307     }
10308     
10309 });
10310
10311 /*
10312  * Based on:
10313  * Ext JS Library 1.1.1
10314  * Copyright(c) 2006-2007, Ext JS, LLC.
10315  *
10316  * Originally Released Under LGPL - original licence link has changed is not relivant.
10317  *
10318  * Fork - LGPL
10319  * <script type="text/javascript">
10320  */
10321 /**
10322  * @class Roo.form.VTypes
10323  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10324  * @singleton
10325  */
10326 Roo.form.VTypes = function(){
10327     // closure these in so they are only created once.
10328     var alpha = /^[a-zA-Z_]+$/;
10329     var alphanum = /^[a-zA-Z0-9_]+$/;
10330     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10331     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10332
10333     // All these messages and functions are configurable
10334     return {
10335         /**
10336          * The function used to validate email addresses
10337          * @param {String} value The email address
10338          */
10339         'email' : function(v){
10340             return email.test(v);
10341         },
10342         /**
10343          * The error text to display when the email validation function returns false
10344          * @type String
10345          */
10346         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10347         /**
10348          * The keystroke filter mask to be applied on email input
10349          * @type RegExp
10350          */
10351         'emailMask' : /[a-z0-9_\.\-@]/i,
10352
10353         /**
10354          * The function used to validate URLs
10355          * @param {String} value The URL
10356          */
10357         'url' : function(v){
10358             return url.test(v);
10359         },
10360         /**
10361          * The error text to display when the url validation function returns false
10362          * @type String
10363          */
10364         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10365         
10366         /**
10367          * The function used to validate alpha values
10368          * @param {String} value The value
10369          */
10370         'alpha' : function(v){
10371             return alpha.test(v);
10372         },
10373         /**
10374          * The error text to display when the alpha validation function returns false
10375          * @type String
10376          */
10377         'alphaText' : 'This field should only contain letters and _',
10378         /**
10379          * The keystroke filter mask to be applied on alpha input
10380          * @type RegExp
10381          */
10382         'alphaMask' : /[a-z_]/i,
10383
10384         /**
10385          * The function used to validate alphanumeric values
10386          * @param {String} value The value
10387          */
10388         'alphanum' : function(v){
10389             return alphanum.test(v);
10390         },
10391         /**
10392          * The error text to display when the alphanumeric validation function returns false
10393          * @type String
10394          */
10395         'alphanumText' : 'This field should only contain letters, numbers and _',
10396         /**
10397          * The keystroke filter mask to be applied on alphanumeric input
10398          * @type RegExp
10399          */
10400         'alphanumMask' : /[a-z0-9_]/i
10401     };
10402 }();/*
10403  * - LGPL
10404  *
10405  * Input
10406  * 
10407  */
10408
10409 /**
10410  * @class Roo.bootstrap.Input
10411  * @extends Roo.bootstrap.Component
10412  * Bootstrap Input class
10413  * @cfg {Boolean} disabled is it disabled
10414  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10415  * @cfg {String} name name of the input
10416  * @cfg {string} fieldLabel - the label associated
10417  * @cfg {string} placeholder - placeholder to put in text.
10418  * @cfg {string}  before - input group add on before
10419  * @cfg {string} after - input group add on after
10420  * @cfg {string} size - (lg|sm) or leave empty..
10421  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10422  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10423  * @cfg {Number} md colspan out of 12 for computer-sized screens
10424  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10425  * @cfg {string} value default value of the input
10426  * @cfg {Number} labelWidth set the width of label 
10427  * @cfg {Number} labellg set the width of label (1-12)
10428  * @cfg {Number} labelmd set the width of label (1-12)
10429  * @cfg {Number} labelsm set the width of label (1-12)
10430  * @cfg {Number} labelxs set the width of label (1-12)
10431  * @cfg {String} labelAlign (top|left)
10432  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10433  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10434  * @cfg {String} indicatorpos (left|right) default left
10435  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10436  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10437  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10438
10439  * @cfg {String} align (left|center|right) Default left
10440  * @cfg {Boolean} forceFeedback (true|false) Default false
10441  * 
10442  * @constructor
10443  * Create a new Input
10444  * @param {Object} config The config object
10445  */
10446
10447 Roo.bootstrap.Input = function(config){
10448     
10449     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10450     
10451     this.addEvents({
10452         /**
10453          * @event focus
10454          * Fires when this field receives input focus.
10455          * @param {Roo.form.Field} this
10456          */
10457         focus : true,
10458         /**
10459          * @event blur
10460          * Fires when this field loses input focus.
10461          * @param {Roo.form.Field} this
10462          */
10463         blur : true,
10464         /**
10465          * @event specialkey
10466          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10467          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10468          * @param {Roo.form.Field} this
10469          * @param {Roo.EventObject} e The event object
10470          */
10471         specialkey : true,
10472         /**
10473          * @event change
10474          * Fires just before the field blurs if the field value has changed.
10475          * @param {Roo.form.Field} this
10476          * @param {Mixed} newValue The new value
10477          * @param {Mixed} oldValue The original value
10478          */
10479         change : true,
10480         /**
10481          * @event invalid
10482          * Fires after the field has been marked as invalid.
10483          * @param {Roo.form.Field} this
10484          * @param {String} msg The validation message
10485          */
10486         invalid : true,
10487         /**
10488          * @event valid
10489          * Fires after the field has been validated with no errors.
10490          * @param {Roo.form.Field} this
10491          */
10492         valid : true,
10493          /**
10494          * @event keyup
10495          * Fires after the key up
10496          * @param {Roo.form.Field} this
10497          * @param {Roo.EventObject}  e The event Object
10498          */
10499         keyup : true
10500     });
10501 };
10502
10503 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10504      /**
10505      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10506       automatic validation (defaults to "keyup").
10507      */
10508     validationEvent : "keyup",
10509      /**
10510      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10511      */
10512     validateOnBlur : true,
10513     /**
10514      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10515      */
10516     validationDelay : 250,
10517      /**
10518      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10519      */
10520     focusClass : "x-form-focus",  // not needed???
10521     
10522        
10523     /**
10524      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10525      */
10526     invalidClass : "has-warning",
10527     
10528     /**
10529      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10530      */
10531     validClass : "has-success",
10532     
10533     /**
10534      * @cfg {Boolean} hasFeedback (true|false) default true
10535      */
10536     hasFeedback : true,
10537     
10538     /**
10539      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10540      */
10541     invalidFeedbackClass : "glyphicon-warning-sign",
10542     
10543     /**
10544      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10545      */
10546     validFeedbackClass : "glyphicon-ok",
10547     
10548     /**
10549      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10550      */
10551     selectOnFocus : false,
10552     
10553      /**
10554      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10555      */
10556     maskRe : null,
10557        /**
10558      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10559      */
10560     vtype : null,
10561     
10562       /**
10563      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10564      */
10565     disableKeyFilter : false,
10566     
10567        /**
10568      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10569      */
10570     disabled : false,
10571      /**
10572      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10573      */
10574     allowBlank : true,
10575     /**
10576      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10577      */
10578     blankText : "Please complete this mandatory field",
10579     
10580      /**
10581      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10582      */
10583     minLength : 0,
10584     /**
10585      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10586      */
10587     maxLength : Number.MAX_VALUE,
10588     /**
10589      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10590      */
10591     minLengthText : "The minimum length for this field is {0}",
10592     /**
10593      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10594      */
10595     maxLengthText : "The maximum length for this field is {0}",
10596   
10597     
10598     /**
10599      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10600      * If available, this function will be called only after the basic validators all return true, and will be passed the
10601      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10602      */
10603     validator : null,
10604     /**
10605      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10606      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10607      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10608      */
10609     regex : null,
10610     /**
10611      * @cfg {String} regexText -- Depricated - use Invalid Text
10612      */
10613     regexText : "",
10614     
10615     /**
10616      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10617      */
10618     invalidText : "",
10619     
10620     
10621     
10622     autocomplete: false,
10623     
10624     
10625     fieldLabel : '',
10626     inputType : 'text',
10627     
10628     name : false,
10629     placeholder: false,
10630     before : false,
10631     after : false,
10632     size : false,
10633     hasFocus : false,
10634     preventMark: false,
10635     isFormField : true,
10636     value : '',
10637     labelWidth : 2,
10638     labelAlign : false,
10639     readOnly : false,
10640     align : false,
10641     formatedValue : false,
10642     forceFeedback : false,
10643     
10644     indicatorpos : 'left',
10645     
10646     labellg : 0,
10647     labelmd : 0,
10648     labelsm : 0,
10649     labelxs : 0,
10650     
10651     capture : '',
10652     accept : '',
10653     
10654     parentLabelAlign : function()
10655     {
10656         var parent = this;
10657         while (parent.parent()) {
10658             parent = parent.parent();
10659             if (typeof(parent.labelAlign) !='undefined') {
10660                 return parent.labelAlign;
10661             }
10662         }
10663         return 'left';
10664         
10665     },
10666     
10667     getAutoCreate : function()
10668     {
10669         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10670         
10671         var id = Roo.id();
10672         
10673         var cfg = {};
10674         
10675         if(this.inputType != 'hidden'){
10676             cfg.cls = 'form-group' //input-group
10677         }
10678         
10679         var input =  {
10680             tag: 'input',
10681             id : id,
10682             type : this.inputType,
10683             value : this.value,
10684             cls : 'form-control',
10685             placeholder : this.placeholder || '',
10686             autocomplete : this.autocomplete || 'new-password'
10687         };
10688         if (this.inputType == 'file') {
10689             input.style = 'overflow:hidden'; // why not in CSS?
10690         }
10691         
10692         if(this.capture.length){
10693             input.capture = this.capture;
10694         }
10695         
10696         if(this.accept.length){
10697             input.accept = this.accept + "/*";
10698         }
10699         
10700         if(this.align){
10701             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10702         }
10703         
10704         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10705             input.maxLength = this.maxLength;
10706         }
10707         
10708         if (this.disabled) {
10709             input.disabled=true;
10710         }
10711         
10712         if (this.readOnly) {
10713             input.readonly=true;
10714         }
10715         
10716         if (this.name) {
10717             input.name = this.name;
10718         }
10719         
10720         if (this.size) {
10721             input.cls += ' input-' + this.size;
10722         }
10723         
10724         var settings=this;
10725         ['xs','sm','md','lg'].map(function(size){
10726             if (settings[size]) {
10727                 cfg.cls += ' col-' + size + '-' + settings[size];
10728             }
10729         });
10730         
10731         var inputblock = input;
10732         
10733         var feedback = {
10734             tag: 'span',
10735             cls: 'glyphicon form-control-feedback'
10736         };
10737             
10738         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10739             
10740             inputblock = {
10741                 cls : 'has-feedback',
10742                 cn :  [
10743                     input,
10744                     feedback
10745                 ] 
10746             };  
10747         }
10748         
10749         if (this.before || this.after) {
10750             
10751             inputblock = {
10752                 cls : 'input-group',
10753                 cn :  [] 
10754             };
10755             
10756             if (this.before && typeof(this.before) == 'string') {
10757                 
10758                 inputblock.cn.push({
10759                     tag :'span',
10760                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10761                     html : this.before
10762                 });
10763             }
10764             if (this.before && typeof(this.before) == 'object') {
10765                 this.before = Roo.factory(this.before);
10766                 
10767                 inputblock.cn.push({
10768                     tag :'span',
10769                     cls : 'roo-input-before input-group-prepend   input-group-' +
10770                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10771                 });
10772             }
10773             
10774             inputblock.cn.push(input);
10775             
10776             if (this.after && typeof(this.after) == 'string') {
10777                 inputblock.cn.push({
10778                     tag :'span',
10779                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10780                     html : this.after
10781                 });
10782             }
10783             if (this.after && typeof(this.after) == 'object') {
10784                 this.after = Roo.factory(this.after);
10785                 
10786                 inputblock.cn.push({
10787                     tag :'span',
10788                     cls : 'roo-input-after input-group-append  input-group-' +
10789                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10790                 });
10791             }
10792             
10793             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10794                 inputblock.cls += ' has-feedback';
10795                 inputblock.cn.push(feedback);
10796             }
10797         };
10798         var indicator = {
10799             tag : 'i',
10800             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10801             tooltip : 'This field is required'
10802         };
10803         if (this.allowBlank ) {
10804             indicator.style = this.allowBlank ? ' display:none' : '';
10805         }
10806         if (align ==='left' && this.fieldLabel.length) {
10807             
10808             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10809             
10810             cfg.cn = [
10811                 indicator,
10812                 {
10813                     tag: 'label',
10814                     'for' :  id,
10815                     cls : 'control-label col-form-label',
10816                     html : this.fieldLabel
10817
10818                 },
10819                 {
10820                     cls : "", 
10821                     cn: [
10822                         inputblock
10823                     ]
10824                 }
10825             ];
10826             
10827             var labelCfg = cfg.cn[1];
10828             var contentCfg = cfg.cn[2];
10829             
10830             if(this.indicatorpos == 'right'){
10831                 cfg.cn = [
10832                     {
10833                         tag: 'label',
10834                         'for' :  id,
10835                         cls : 'control-label col-form-label',
10836                         cn : [
10837                             {
10838                                 tag : 'span',
10839                                 html : this.fieldLabel
10840                             },
10841                             indicator
10842                         ]
10843                     },
10844                     {
10845                         cls : "",
10846                         cn: [
10847                             inputblock
10848                         ]
10849                     }
10850
10851                 ];
10852                 
10853                 labelCfg = cfg.cn[0];
10854                 contentCfg = cfg.cn[1];
10855             
10856             }
10857             
10858             if(this.labelWidth > 12){
10859                 labelCfg.style = "width: " + this.labelWidth + 'px';
10860             }
10861             
10862             if(this.labelWidth < 13 && this.labelmd == 0){
10863                 this.labelmd = this.labelWidth;
10864             }
10865             
10866             if(this.labellg > 0){
10867                 labelCfg.cls += ' col-lg-' + this.labellg;
10868                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10869             }
10870             
10871             if(this.labelmd > 0){
10872                 labelCfg.cls += ' col-md-' + this.labelmd;
10873                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10874             }
10875             
10876             if(this.labelsm > 0){
10877                 labelCfg.cls += ' col-sm-' + this.labelsm;
10878                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10879             }
10880             
10881             if(this.labelxs > 0){
10882                 labelCfg.cls += ' col-xs-' + this.labelxs;
10883                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10884             }
10885             
10886             
10887         } else if ( this.fieldLabel.length) {
10888                 
10889             
10890             
10891             cfg.cn = [
10892                 {
10893                     tag : 'i',
10894                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10895                     tooltip : 'This field is required',
10896                     style : this.allowBlank ? ' display:none' : '' 
10897                 },
10898                 {
10899                     tag: 'label',
10900                    //cls : 'input-group-addon',
10901                     html : this.fieldLabel
10902
10903                 },
10904
10905                inputblock
10906
10907            ];
10908            
10909            if(this.indicatorpos == 'right'){
10910        
10911                 cfg.cn = [
10912                     {
10913                         tag: 'label',
10914                        //cls : 'input-group-addon',
10915                         html : this.fieldLabel
10916
10917                     },
10918                     {
10919                         tag : 'i',
10920                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10921                         tooltip : 'This field is required',
10922                         style : this.allowBlank ? ' display:none' : '' 
10923                     },
10924
10925                    inputblock
10926
10927                ];
10928
10929             }
10930
10931         } else {
10932             
10933             cfg.cn = [
10934
10935                     inputblock
10936
10937             ];
10938                 
10939                 
10940         };
10941         
10942         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10943            cfg.cls += ' navbar-form';
10944         }
10945         
10946         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10947             // on BS4 we do this only if not form 
10948             cfg.cls += ' navbar-form';
10949             cfg.tag = 'li';
10950         }
10951         
10952         return cfg;
10953         
10954     },
10955     /**
10956      * return the real input element.
10957      */
10958     inputEl: function ()
10959     {
10960         return this.el.select('input.form-control',true).first();
10961     },
10962     
10963     tooltipEl : function()
10964     {
10965         return this.inputEl();
10966     },
10967     
10968     indicatorEl : function()
10969     {
10970         if (Roo.bootstrap.version == 4) {
10971             return false; // not enabled in v4 yet.
10972         }
10973         
10974         var indicator = this.el.select('i.roo-required-indicator',true).first();
10975         
10976         if(!indicator){
10977             return false;
10978         }
10979         
10980         return indicator;
10981         
10982     },
10983     
10984     setDisabled : function(v)
10985     {
10986         var i  = this.inputEl().dom;
10987         if (!v) {
10988             i.removeAttribute('disabled');
10989             return;
10990             
10991         }
10992         i.setAttribute('disabled','true');
10993     },
10994     initEvents : function()
10995     {
10996           
10997         this.inputEl().on("keydown" , this.fireKey,  this);
10998         this.inputEl().on("focus", this.onFocus,  this);
10999         this.inputEl().on("blur", this.onBlur,  this);
11000         
11001         this.inputEl().relayEvent('keyup', this);
11002         
11003         this.indicator = this.indicatorEl();
11004         
11005         if(this.indicator){
11006             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11007         }
11008  
11009         // reference to original value for reset
11010         this.originalValue = this.getValue();
11011         //Roo.form.TextField.superclass.initEvents.call(this);
11012         if(this.validationEvent == 'keyup'){
11013             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11014             this.inputEl().on('keyup', this.filterValidation, this);
11015         }
11016         else if(this.validationEvent !== false){
11017             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11018         }
11019         
11020         if(this.selectOnFocus){
11021             this.on("focus", this.preFocus, this);
11022             
11023         }
11024         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11025             this.inputEl().on("keypress", this.filterKeys, this);
11026         } else {
11027             this.inputEl().relayEvent('keypress', this);
11028         }
11029        /* if(this.grow){
11030             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11031             this.el.on("click", this.autoSize,  this);
11032         }
11033         */
11034         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11035             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11036         }
11037         
11038         if (typeof(this.before) == 'object') {
11039             this.before.render(this.el.select('.roo-input-before',true).first());
11040         }
11041         if (typeof(this.after) == 'object') {
11042             this.after.render(this.el.select('.roo-input-after',true).first());
11043         }
11044         
11045         this.inputEl().on('change', this.onChange, this);
11046         
11047     },
11048     filterValidation : function(e){
11049         if(!e.isNavKeyPress()){
11050             this.validationTask.delay(this.validationDelay);
11051         }
11052     },
11053      /**
11054      * Validates the field value
11055      * @return {Boolean} True if the value is valid, else false
11056      */
11057     validate : function(){
11058         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11059         if(this.disabled || this.validateValue(this.getRawValue())){
11060             this.markValid();
11061             return true;
11062         }
11063         
11064         this.markInvalid();
11065         return false;
11066     },
11067     
11068     
11069     /**
11070      * Validates a value according to the field's validation rules and marks the field as invalid
11071      * if the validation fails
11072      * @param {Mixed} value The value to validate
11073      * @return {Boolean} True if the value is valid, else false
11074      */
11075     validateValue : function(value)
11076     {
11077         if(this.getVisibilityEl().hasClass('hidden')){
11078             return true;
11079         }
11080         
11081         if(value.length < 1)  { // if it's blank
11082             if(this.allowBlank){
11083                 return true;
11084             }
11085             return false;
11086         }
11087         
11088         if(value.length < this.minLength){
11089             return false;
11090         }
11091         if(value.length > this.maxLength){
11092             return false;
11093         }
11094         if(this.vtype){
11095             var vt = Roo.form.VTypes;
11096             if(!vt[this.vtype](value, this)){
11097                 return false;
11098             }
11099         }
11100         if(typeof this.validator == "function"){
11101             var msg = this.validator(value);
11102             if(msg !== true){
11103                 return false;
11104             }
11105             if (typeof(msg) == 'string') {
11106                 this.invalidText = msg;
11107             }
11108         }
11109         
11110         if(this.regex && !this.regex.test(value)){
11111             return false;
11112         }
11113         
11114         return true;
11115     },
11116     
11117      // private
11118     fireKey : function(e){
11119         //Roo.log('field ' + e.getKey());
11120         if(e.isNavKeyPress()){
11121             this.fireEvent("specialkey", this, e);
11122         }
11123     },
11124     focus : function (selectText){
11125         if(this.rendered){
11126             this.inputEl().focus();
11127             if(selectText === true){
11128                 this.inputEl().dom.select();
11129             }
11130         }
11131         return this;
11132     } ,
11133     
11134     onFocus : function(){
11135         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11136            // this.el.addClass(this.focusClass);
11137         }
11138         if(!this.hasFocus){
11139             this.hasFocus = true;
11140             this.startValue = this.getValue();
11141             this.fireEvent("focus", this);
11142         }
11143     },
11144     
11145     beforeBlur : Roo.emptyFn,
11146
11147     
11148     // private
11149     onBlur : function(){
11150         this.beforeBlur();
11151         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11152             //this.el.removeClass(this.focusClass);
11153         }
11154         this.hasFocus = false;
11155         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11156             this.validate();
11157         }
11158         var v = this.getValue();
11159         if(String(v) !== String(this.startValue)){
11160             this.fireEvent('change', this, v, this.startValue);
11161         }
11162         this.fireEvent("blur", this);
11163     },
11164     
11165     onChange : function(e)
11166     {
11167         var v = this.getValue();
11168         if(String(v) !== String(this.startValue)){
11169             this.fireEvent('change', this, v, this.startValue);
11170         }
11171         
11172     },
11173     
11174     /**
11175      * Resets the current field value to the originally loaded value and clears any validation messages
11176      */
11177     reset : function(){
11178         this.setValue(this.originalValue);
11179         this.validate();
11180     },
11181      /**
11182      * Returns the name of the field
11183      * @return {Mixed} name The name field
11184      */
11185     getName: function(){
11186         return this.name;
11187     },
11188      /**
11189      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11190      * @return {Mixed} value The field value
11191      */
11192     getValue : function(){
11193         
11194         var v = this.inputEl().getValue();
11195         
11196         return v;
11197     },
11198     /**
11199      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11200      * @return {Mixed} value The field value
11201      */
11202     getRawValue : function(){
11203         var v = this.inputEl().getValue();
11204         
11205         return v;
11206     },
11207     
11208     /**
11209      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11210      * @param {Mixed} value The value to set
11211      */
11212     setRawValue : function(v){
11213         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11214     },
11215     
11216     selectText : function(start, end){
11217         var v = this.getRawValue();
11218         if(v.length > 0){
11219             start = start === undefined ? 0 : start;
11220             end = end === undefined ? v.length : end;
11221             var d = this.inputEl().dom;
11222             if(d.setSelectionRange){
11223                 d.setSelectionRange(start, end);
11224             }else if(d.createTextRange){
11225                 var range = d.createTextRange();
11226                 range.moveStart("character", start);
11227                 range.moveEnd("character", v.length-end);
11228                 range.select();
11229             }
11230         }
11231     },
11232     
11233     /**
11234      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11235      * @param {Mixed} value The value to set
11236      */
11237     setValue : function(v){
11238         this.value = v;
11239         if(this.rendered){
11240             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11241             this.validate();
11242         }
11243     },
11244     
11245     /*
11246     processValue : function(value){
11247         if(this.stripCharsRe){
11248             var newValue = value.replace(this.stripCharsRe, '');
11249             if(newValue !== value){
11250                 this.setRawValue(newValue);
11251                 return newValue;
11252             }
11253         }
11254         return value;
11255     },
11256   */
11257     preFocus : function(){
11258         
11259         if(this.selectOnFocus){
11260             this.inputEl().dom.select();
11261         }
11262     },
11263     filterKeys : function(e){
11264         var k = e.getKey();
11265         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11266             return;
11267         }
11268         var c = e.getCharCode(), cc = String.fromCharCode(c);
11269         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11270             return;
11271         }
11272         if(!this.maskRe.test(cc)){
11273             e.stopEvent();
11274         }
11275     },
11276      /**
11277      * Clear any invalid styles/messages for this field
11278      */
11279     clearInvalid : function(){
11280         
11281         if(!this.el || this.preventMark){ // not rendered
11282             return;
11283         }
11284         
11285         
11286         this.el.removeClass([this.invalidClass, 'is-invalid']);
11287         
11288         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11289             
11290             var feedback = this.el.select('.form-control-feedback', true).first();
11291             
11292             if(feedback){
11293                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11294             }
11295             
11296         }
11297         
11298         if(this.indicator){
11299             this.indicator.removeClass('visible');
11300             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11301         }
11302         
11303         this.fireEvent('valid', this);
11304     },
11305     
11306      /**
11307      * Mark this field as valid
11308      */
11309     markValid : function()
11310     {
11311         if(!this.el  || this.preventMark){ // not rendered...
11312             return;
11313         }
11314         
11315         this.el.removeClass([this.invalidClass, this.validClass]);
11316         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11317
11318         var feedback = this.el.select('.form-control-feedback', true).first();
11319             
11320         if(feedback){
11321             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11322         }
11323         
11324         if(this.indicator){
11325             this.indicator.removeClass('visible');
11326             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11327         }
11328         
11329         if(this.disabled){
11330             return;
11331         }
11332         
11333            
11334         if(this.allowBlank && !this.getRawValue().length){
11335             return;
11336         }
11337         if (Roo.bootstrap.version == 3) {
11338             this.el.addClass(this.validClass);
11339         } else {
11340             this.inputEl().addClass('is-valid');
11341         }
11342
11343         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11344             
11345             var feedback = this.el.select('.form-control-feedback', true).first();
11346             
11347             if(feedback){
11348                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11349                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11350             }
11351             
11352         }
11353         
11354         this.fireEvent('valid', this);
11355     },
11356     
11357      /**
11358      * Mark this field as invalid
11359      * @param {String} msg The validation message
11360      */
11361     markInvalid : function(msg)
11362     {
11363         if(!this.el  || this.preventMark){ // not rendered
11364             return;
11365         }
11366         
11367         this.el.removeClass([this.invalidClass, this.validClass]);
11368         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11369         
11370         var feedback = this.el.select('.form-control-feedback', true).first();
11371             
11372         if(feedback){
11373             this.el.select('.form-control-feedback', true).first().removeClass(
11374                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11375         }
11376
11377         if(this.disabled){
11378             return;
11379         }
11380         
11381         if(this.allowBlank && !this.getRawValue().length){
11382             return;
11383         }
11384         
11385         if(this.indicator){
11386             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11387             this.indicator.addClass('visible');
11388         }
11389         if (Roo.bootstrap.version == 3) {
11390             this.el.addClass(this.invalidClass);
11391         } else {
11392             this.inputEl().addClass('is-invalid');
11393         }
11394         
11395         
11396         
11397         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11398             
11399             var feedback = this.el.select('.form-control-feedback', true).first();
11400             
11401             if(feedback){
11402                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11403                 
11404                 if(this.getValue().length || this.forceFeedback){
11405                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11406                 }
11407                 
11408             }
11409             
11410         }
11411         
11412         this.fireEvent('invalid', this, msg);
11413     },
11414     // private
11415     SafariOnKeyDown : function(event)
11416     {
11417         // this is a workaround for a password hang bug on chrome/ webkit.
11418         if (this.inputEl().dom.type != 'password') {
11419             return;
11420         }
11421         
11422         var isSelectAll = false;
11423         
11424         if(this.inputEl().dom.selectionEnd > 0){
11425             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11426         }
11427         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11428             event.preventDefault();
11429             this.setValue('');
11430             return;
11431         }
11432         
11433         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11434             
11435             event.preventDefault();
11436             // this is very hacky as keydown always get's upper case.
11437             //
11438             var cc = String.fromCharCode(event.getCharCode());
11439             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11440             
11441         }
11442     },
11443     adjustWidth : function(tag, w){
11444         tag = tag.toLowerCase();
11445         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11446             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11447                 if(tag == 'input'){
11448                     return w + 2;
11449                 }
11450                 if(tag == 'textarea'){
11451                     return w-2;
11452                 }
11453             }else if(Roo.isOpera){
11454                 if(tag == 'input'){
11455                     return w + 2;
11456                 }
11457                 if(tag == 'textarea'){
11458                     return w-2;
11459                 }
11460             }
11461         }
11462         return w;
11463     },
11464     
11465     setFieldLabel : function(v)
11466     {
11467         if(!this.rendered){
11468             return;
11469         }
11470         
11471         if(this.indicatorEl()){
11472             var ar = this.el.select('label > span',true);
11473             
11474             if (ar.elements.length) {
11475                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             var br = this.el.select('label',true);
11481             
11482             if(br.elements.length) {
11483                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11484                 this.fieldLabel = v;
11485                 return;
11486             }
11487             
11488             Roo.log('Cannot Found any of label > span || label in input');
11489             return;
11490         }
11491         
11492         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11493         this.fieldLabel = v;
11494         
11495         
11496     }
11497 });
11498
11499  
11500 /*
11501  * - LGPL
11502  *
11503  * Input
11504  * 
11505  */
11506
11507 /**
11508  * @class Roo.bootstrap.TextArea
11509  * @extends Roo.bootstrap.Input
11510  * Bootstrap TextArea class
11511  * @cfg {Number} cols Specifies the visible width of a text area
11512  * @cfg {Number} rows Specifies the visible number of lines in a text area
11513  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11514  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11515  * @cfg {string} html text
11516  * 
11517  * @constructor
11518  * Create a new TextArea
11519  * @param {Object} config The config object
11520  */
11521
11522 Roo.bootstrap.TextArea = function(config){
11523     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11524    
11525 };
11526
11527 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11528      
11529     cols : false,
11530     rows : 5,
11531     readOnly : false,
11532     warp : 'soft',
11533     resize : false,
11534     value: false,
11535     html: false,
11536     
11537     getAutoCreate : function(){
11538         
11539         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11540         
11541         var id = Roo.id();
11542         
11543         var cfg = {};
11544         
11545         if(this.inputType != 'hidden'){
11546             cfg.cls = 'form-group' //input-group
11547         }
11548         
11549         var input =  {
11550             tag: 'textarea',
11551             id : id,
11552             warp : this.warp,
11553             rows : this.rows,
11554             value : this.value || '',
11555             html: this.html || '',
11556             cls : 'form-control',
11557             placeholder : this.placeholder || '' 
11558             
11559         };
11560         
11561         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11562             input.maxLength = this.maxLength;
11563         }
11564         
11565         if(this.resize){
11566             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11567         }
11568         
11569         if(this.cols){
11570             input.cols = this.cols;
11571         }
11572         
11573         if (this.readOnly) {
11574             input.readonly = true;
11575         }
11576         
11577         if (this.name) {
11578             input.name = this.name;
11579         }
11580         
11581         if (this.size) {
11582             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11583         }
11584         
11585         var settings=this;
11586         ['xs','sm','md','lg'].map(function(size){
11587             if (settings[size]) {
11588                 cfg.cls += ' col-' + size + '-' + settings[size];
11589             }
11590         });
11591         
11592         var inputblock = input;
11593         
11594         if(this.hasFeedback && !this.allowBlank){
11595             
11596             var feedback = {
11597                 tag: 'span',
11598                 cls: 'glyphicon form-control-feedback'
11599             };
11600
11601             inputblock = {
11602                 cls : 'has-feedback',
11603                 cn :  [
11604                     input,
11605                     feedback
11606                 ] 
11607             };  
11608         }
11609         
11610         
11611         if (this.before || this.after) {
11612             
11613             inputblock = {
11614                 cls : 'input-group',
11615                 cn :  [] 
11616             };
11617             if (this.before) {
11618                 inputblock.cn.push({
11619                     tag :'span',
11620                     cls : 'input-group-addon',
11621                     html : this.before
11622                 });
11623             }
11624             
11625             inputblock.cn.push(input);
11626             
11627             if(this.hasFeedback && !this.allowBlank){
11628                 inputblock.cls += ' has-feedback';
11629                 inputblock.cn.push(feedback);
11630             }
11631             
11632             if (this.after) {
11633                 inputblock.cn.push({
11634                     tag :'span',
11635                     cls : 'input-group-addon',
11636                     html : this.after
11637                 });
11638             }
11639             
11640         }
11641         
11642         if (align ==='left' && this.fieldLabel.length) {
11643             cfg.cn = [
11644                 {
11645                     tag: 'label',
11646                     'for' :  id,
11647                     cls : 'control-label',
11648                     html : this.fieldLabel
11649                 },
11650                 {
11651                     cls : "",
11652                     cn: [
11653                         inputblock
11654                     ]
11655                 }
11656
11657             ];
11658             
11659             if(this.labelWidth > 12){
11660                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11661             }
11662
11663             if(this.labelWidth < 13 && this.labelmd == 0){
11664                 this.labelmd = this.labelWidth;
11665             }
11666
11667             if(this.labellg > 0){
11668                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11669                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11670             }
11671
11672             if(this.labelmd > 0){
11673                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11674                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11675             }
11676
11677             if(this.labelsm > 0){
11678                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11679                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11680             }
11681
11682             if(this.labelxs > 0){
11683                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11684                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11685             }
11686             
11687         } else if ( this.fieldLabel.length) {
11688             cfg.cn = [
11689
11690                {
11691                    tag: 'label',
11692                    //cls : 'input-group-addon',
11693                    html : this.fieldLabel
11694
11695                },
11696
11697                inputblock
11698
11699            ];
11700
11701         } else {
11702
11703             cfg.cn = [
11704
11705                 inputblock
11706
11707             ];
11708                 
11709         }
11710         
11711         if (this.disabled) {
11712             input.disabled=true;
11713         }
11714         
11715         return cfg;
11716         
11717     },
11718     /**
11719      * return the real textarea element.
11720      */
11721     inputEl: function ()
11722     {
11723         return this.el.select('textarea.form-control',true).first();
11724     },
11725     
11726     /**
11727      * Clear any invalid styles/messages for this field
11728      */
11729     clearInvalid : function()
11730     {
11731         
11732         if(!this.el || this.preventMark){ // not rendered
11733             return;
11734         }
11735         
11736         var label = this.el.select('label', true).first();
11737         var icon = this.el.select('i.fa-star', true).first();
11738         
11739         if(label && icon){
11740             icon.remove();
11741         }
11742         this.el.removeClass( this.validClass);
11743         this.inputEl().removeClass('is-invalid');
11744          
11745         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11746             
11747             var feedback = this.el.select('.form-control-feedback', true).first();
11748             
11749             if(feedback){
11750                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11751             }
11752             
11753         }
11754         
11755         this.fireEvent('valid', this);
11756     },
11757     
11758      /**
11759      * Mark this field as valid
11760      */
11761     markValid : function()
11762     {
11763         if(!this.el  || this.preventMark){ // not rendered
11764             return;
11765         }
11766         
11767         this.el.removeClass([this.invalidClass, this.validClass]);
11768         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11769         
11770         var feedback = this.el.select('.form-control-feedback', true).first();
11771             
11772         if(feedback){
11773             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11774         }
11775
11776         if(this.disabled || this.allowBlank){
11777             return;
11778         }
11779         
11780         var label = this.el.select('label', true).first();
11781         var icon = this.el.select('i.fa-star', true).first();
11782         
11783         if(label && icon){
11784             icon.remove();
11785         }
11786         if (Roo.bootstrap.version == 3) {
11787             this.el.addClass(this.validClass);
11788         } else {
11789             this.inputEl().addClass('is-valid');
11790         }
11791         
11792         
11793         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11794             
11795             var feedback = this.el.select('.form-control-feedback', true).first();
11796             
11797             if(feedback){
11798                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11799                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11800             }
11801             
11802         }
11803         
11804         this.fireEvent('valid', this);
11805     },
11806     
11807      /**
11808      * Mark this field as invalid
11809      * @param {String} msg The validation message
11810      */
11811     markInvalid : function(msg)
11812     {
11813         if(!this.el  || this.preventMark){ // not rendered
11814             return;
11815         }
11816         
11817         this.el.removeClass([this.invalidClass, this.validClass]);
11818         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11819         
11820         var feedback = this.el.select('.form-control-feedback', true).first();
11821             
11822         if(feedback){
11823             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11824         }
11825
11826         if(this.disabled || this.allowBlank){
11827             return;
11828         }
11829         
11830         var label = this.el.select('label', true).first();
11831         var icon = this.el.select('i.fa-star', true).first();
11832         
11833         if(!this.getValue().length && label && !icon){
11834             this.el.createChild({
11835                 tag : 'i',
11836                 cls : 'text-danger fa fa-lg fa-star',
11837                 tooltip : 'This field is required',
11838                 style : 'margin-right:5px;'
11839             }, label, true);
11840         }
11841         
11842         if (Roo.bootstrap.version == 3) {
11843             this.el.addClass(this.invalidClass);
11844         } else {
11845             this.inputEl().addClass('is-invalid');
11846         }
11847         
11848         // fixme ... this may be depricated need to test..
11849         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11850             
11851             var feedback = this.el.select('.form-control-feedback', true).first();
11852             
11853             if(feedback){
11854                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11855                 
11856                 if(this.getValue().length || this.forceFeedback){
11857                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11858                 }
11859                 
11860             }
11861             
11862         }
11863         
11864         this.fireEvent('invalid', this, msg);
11865     }
11866 });
11867
11868  
11869 /*
11870  * - LGPL
11871  *
11872  * trigger field - base class for combo..
11873  * 
11874  */
11875  
11876 /**
11877  * @class Roo.bootstrap.TriggerField
11878  * @extends Roo.bootstrap.Input
11879  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11880  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11881  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11882  * for which you can provide a custom implementation.  For example:
11883  * <pre><code>
11884 var trigger = new Roo.bootstrap.TriggerField();
11885 trigger.onTriggerClick = myTriggerFn;
11886 trigger.applyTo('my-field');
11887 </code></pre>
11888  *
11889  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11890  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11891  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11892  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11893  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11894
11895  * @constructor
11896  * Create a new TriggerField.
11897  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11898  * to the base TextField)
11899  */
11900 Roo.bootstrap.TriggerField = function(config){
11901     this.mimicing = false;
11902     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11903 };
11904
11905 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11906     /**
11907      * @cfg {String} triggerClass A CSS class to apply to the trigger
11908      */
11909      /**
11910      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11911      */
11912     hideTrigger:false,
11913
11914     /**
11915      * @cfg {Boolean} removable (true|false) special filter default false
11916      */
11917     removable : false,
11918     
11919     /** @cfg {Boolean} grow @hide */
11920     /** @cfg {Number} growMin @hide */
11921     /** @cfg {Number} growMax @hide */
11922
11923     /**
11924      * @hide 
11925      * @method
11926      */
11927     autoSize: Roo.emptyFn,
11928     // private
11929     monitorTab : true,
11930     // private
11931     deferHeight : true,
11932
11933     
11934     actionMode : 'wrap',
11935     
11936     caret : false,
11937     
11938     
11939     getAutoCreate : function(){
11940        
11941         var align = this.labelAlign || this.parentLabelAlign();
11942         
11943         var id = Roo.id();
11944         
11945         var cfg = {
11946             cls: 'form-group' //input-group
11947         };
11948         
11949         
11950         var input =  {
11951             tag: 'input',
11952             id : id,
11953             type : this.inputType,
11954             cls : 'form-control',
11955             autocomplete: 'new-password',
11956             placeholder : this.placeholder || '' 
11957             
11958         };
11959         if (this.name) {
11960             input.name = this.name;
11961         }
11962         if (this.size) {
11963             input.cls += ' input-' + this.size;
11964         }
11965         
11966         if (this.disabled) {
11967             input.disabled=true;
11968         }
11969         
11970         var inputblock = input;
11971         
11972         if(this.hasFeedback && !this.allowBlank){
11973             
11974             var feedback = {
11975                 tag: 'span',
11976                 cls: 'glyphicon form-control-feedback'
11977             };
11978             
11979             if(this.removable && !this.editable  ){
11980                 inputblock = {
11981                     cls : 'has-feedback',
11982                     cn :  [
11983                         inputblock,
11984                         {
11985                             tag: 'button',
11986                             html : 'x',
11987                             cls : 'roo-combo-removable-btn close'
11988                         },
11989                         feedback
11990                     ] 
11991                 };
11992             } else {
11993                 inputblock = {
11994                     cls : 'has-feedback',
11995                     cn :  [
11996                         inputblock,
11997                         feedback
11998                     ] 
11999                 };
12000             }
12001
12002         } else {
12003             if(this.removable && !this.editable ){
12004                 inputblock = {
12005                     cls : 'roo-removable',
12006                     cn :  [
12007                         inputblock,
12008                         {
12009                             tag: 'button',
12010                             html : 'x',
12011                             cls : 'roo-combo-removable-btn close'
12012                         }
12013                     ] 
12014                 };
12015             }
12016         }
12017         
12018         if (this.before || this.after) {
12019             
12020             inputblock = {
12021                 cls : 'input-group',
12022                 cn :  [] 
12023             };
12024             if (this.before) {
12025                 inputblock.cn.push({
12026                     tag :'span',
12027                     cls : 'input-group-addon input-group-prepend input-group-text',
12028                     html : this.before
12029                 });
12030             }
12031             
12032             inputblock.cn.push(input);
12033             
12034             if(this.hasFeedback && !this.allowBlank){
12035                 inputblock.cls += ' has-feedback';
12036                 inputblock.cn.push(feedback);
12037             }
12038             
12039             if (this.after) {
12040                 inputblock.cn.push({
12041                     tag :'span',
12042                     cls : 'input-group-addon input-group-append input-group-text',
12043                     html : this.after
12044                 });
12045             }
12046             
12047         };
12048         
12049       
12050         
12051         var ibwrap = inputblock;
12052         
12053         if(this.multiple){
12054             ibwrap = {
12055                 tag: 'ul',
12056                 cls: 'roo-select2-choices',
12057                 cn:[
12058                     {
12059                         tag: 'li',
12060                         cls: 'roo-select2-search-field',
12061                         cn: [
12062
12063                             inputblock
12064                         ]
12065                     }
12066                 ]
12067             };
12068                 
12069         }
12070         
12071         var combobox = {
12072             cls: 'roo-select2-container input-group',
12073             cn: [
12074                  {
12075                     tag: 'input',
12076                     type : 'hidden',
12077                     cls: 'form-hidden-field'
12078                 },
12079                 ibwrap
12080             ]
12081         };
12082         
12083         if(!this.multiple && this.showToggleBtn){
12084             
12085             var caret = {
12086                         tag: 'span',
12087                         cls: 'caret'
12088              };
12089             if (this.caret != false) {
12090                 caret = {
12091                      tag: 'i',
12092                      cls: 'fa fa-' + this.caret
12093                 };
12094                 
12095             }
12096             
12097             combobox.cn.push({
12098                 tag :'span',
12099                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12100                 cn : [
12101                     Roo.bootstrap.version == 3 ? caret : '',
12102                     {
12103                         tag: 'span',
12104                         cls: 'combobox-clear',
12105                         cn  : [
12106                             {
12107                                 tag : 'i',
12108                                 cls: 'icon-remove'
12109                             }
12110                         ]
12111                     }
12112                 ]
12113
12114             })
12115         }
12116         
12117         if(this.multiple){
12118             combobox.cls += ' roo-select2-container-multi';
12119         }
12120          var indicator = {
12121             tag : 'i',
12122             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12123             tooltip : 'This field is required'
12124         };
12125         if (Roo.bootstrap.version == 4) {
12126             indicator = {
12127                 tag : 'i',
12128                 style : 'display:none'
12129             };
12130         }
12131         
12132         
12133         if (align ==='left' && this.fieldLabel.length) {
12134             
12135             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12136
12137             cfg.cn = [
12138                 indicator,
12139                 {
12140                     tag: 'label',
12141                     'for' :  id,
12142                     cls : 'control-label',
12143                     html : this.fieldLabel
12144
12145                 },
12146                 {
12147                     cls : "", 
12148                     cn: [
12149                         combobox
12150                     ]
12151                 }
12152
12153             ];
12154             
12155             var labelCfg = cfg.cn[1];
12156             var contentCfg = cfg.cn[2];
12157             
12158             if(this.indicatorpos == 'right'){
12159                 cfg.cn = [
12160                     {
12161                         tag: 'label',
12162                         'for' :  id,
12163                         cls : 'control-label',
12164                         cn : [
12165                             {
12166                                 tag : 'span',
12167                                 html : this.fieldLabel
12168                             },
12169                             indicator
12170                         ]
12171                     },
12172                     {
12173                         cls : "", 
12174                         cn: [
12175                             combobox
12176                         ]
12177                     }
12178
12179                 ];
12180                 
12181                 labelCfg = cfg.cn[0];
12182                 contentCfg = cfg.cn[1];
12183             }
12184             
12185             if(this.labelWidth > 12){
12186                 labelCfg.style = "width: " + this.labelWidth + 'px';
12187             }
12188             
12189             if(this.labelWidth < 13 && this.labelmd == 0){
12190                 this.labelmd = this.labelWidth;
12191             }
12192             
12193             if(this.labellg > 0){
12194                 labelCfg.cls += ' col-lg-' + this.labellg;
12195                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12196             }
12197             
12198             if(this.labelmd > 0){
12199                 labelCfg.cls += ' col-md-' + this.labelmd;
12200                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12201             }
12202             
12203             if(this.labelsm > 0){
12204                 labelCfg.cls += ' col-sm-' + this.labelsm;
12205                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12206             }
12207             
12208             if(this.labelxs > 0){
12209                 labelCfg.cls += ' col-xs-' + this.labelxs;
12210                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12211             }
12212             
12213         } else if ( this.fieldLabel.length) {
12214 //                Roo.log(" label");
12215             cfg.cn = [
12216                 indicator,
12217                {
12218                    tag: 'label',
12219                    //cls : 'input-group-addon',
12220                    html : this.fieldLabel
12221
12222                },
12223
12224                combobox
12225
12226             ];
12227             
12228             if(this.indicatorpos == 'right'){
12229                 
12230                 cfg.cn = [
12231                     {
12232                        tag: 'label',
12233                        cn : [
12234                            {
12235                                tag : 'span',
12236                                html : this.fieldLabel
12237                            },
12238                            indicator
12239                        ]
12240
12241                     },
12242                     combobox
12243
12244                 ];
12245
12246             }
12247
12248         } else {
12249             
12250 //                Roo.log(" no label && no align");
12251                 cfg = combobox
12252                      
12253                 
12254         }
12255         
12256         var settings=this;
12257         ['xs','sm','md','lg'].map(function(size){
12258             if (settings[size]) {
12259                 cfg.cls += ' col-' + size + '-' + settings[size];
12260             }
12261         });
12262         
12263         return cfg;
12264         
12265     },
12266     
12267     
12268     
12269     // private
12270     onResize : function(w, h){
12271 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12272 //        if(typeof w == 'number'){
12273 //            var x = w - this.trigger.getWidth();
12274 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12275 //            this.trigger.setStyle('left', x+'px');
12276 //        }
12277     },
12278
12279     // private
12280     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12281
12282     // private
12283     getResizeEl : function(){
12284         return this.inputEl();
12285     },
12286
12287     // private
12288     getPositionEl : function(){
12289         return this.inputEl();
12290     },
12291
12292     // private
12293     alignErrorIcon : function(){
12294         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12295     },
12296
12297     // private
12298     initEvents : function(){
12299         
12300         this.createList();
12301         
12302         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12303         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12304         if(!this.multiple && this.showToggleBtn){
12305             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12306             if(this.hideTrigger){
12307                 this.trigger.setDisplayed(false);
12308             }
12309             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12310         }
12311         
12312         if(this.multiple){
12313             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12314         }
12315         
12316         if(this.removable && !this.editable && !this.tickable){
12317             var close = this.closeTriggerEl();
12318             
12319             if(close){
12320                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12321                 close.on('click', this.removeBtnClick, this, close);
12322             }
12323         }
12324         
12325         //this.trigger.addClassOnOver('x-form-trigger-over');
12326         //this.trigger.addClassOnClick('x-form-trigger-click');
12327         
12328         //if(!this.width){
12329         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12330         //}
12331     },
12332     
12333     closeTriggerEl : function()
12334     {
12335         var close = this.el.select('.roo-combo-removable-btn', true).first();
12336         return close ? close : false;
12337     },
12338     
12339     removeBtnClick : function(e, h, el)
12340     {
12341         e.preventDefault();
12342         
12343         if(this.fireEvent("remove", this) !== false){
12344             this.reset();
12345             this.fireEvent("afterremove", this)
12346         }
12347     },
12348     
12349     createList : function()
12350     {
12351         this.list = Roo.get(document.body).createChild({
12352             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12353             cls: 'typeahead typeahead-long dropdown-menu shadow',
12354             style: 'display:none'
12355         });
12356         
12357         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12358         
12359     },
12360
12361     // private
12362     initTrigger : function(){
12363        
12364     },
12365
12366     // private
12367     onDestroy : function(){
12368         if(this.trigger){
12369             this.trigger.removeAllListeners();
12370           //  this.trigger.remove();
12371         }
12372         //if(this.wrap){
12373         //    this.wrap.remove();
12374         //}
12375         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12376     },
12377
12378     // private
12379     onFocus : function(){
12380         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12381         /*
12382         if(!this.mimicing){
12383             this.wrap.addClass('x-trigger-wrap-focus');
12384             this.mimicing = true;
12385             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12386             if(this.monitorTab){
12387                 this.el.on("keydown", this.checkTab, this);
12388             }
12389         }
12390         */
12391     },
12392
12393     // private
12394     checkTab : function(e){
12395         if(e.getKey() == e.TAB){
12396             this.triggerBlur();
12397         }
12398     },
12399
12400     // private
12401     onBlur : function(){
12402         // do nothing
12403     },
12404
12405     // private
12406     mimicBlur : function(e, t){
12407         /*
12408         if(!this.wrap.contains(t) && this.validateBlur()){
12409             this.triggerBlur();
12410         }
12411         */
12412     },
12413
12414     // private
12415     triggerBlur : function(){
12416         this.mimicing = false;
12417         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12418         if(this.monitorTab){
12419             this.el.un("keydown", this.checkTab, this);
12420         }
12421         //this.wrap.removeClass('x-trigger-wrap-focus');
12422         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12423     },
12424
12425     // private
12426     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12427     validateBlur : function(e, t){
12428         return true;
12429     },
12430
12431     // private
12432     onDisable : function(){
12433         this.inputEl().dom.disabled = true;
12434         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12435         //if(this.wrap){
12436         //    this.wrap.addClass('x-item-disabled');
12437         //}
12438     },
12439
12440     // private
12441     onEnable : function(){
12442         this.inputEl().dom.disabled = false;
12443         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12444         //if(this.wrap){
12445         //    this.el.removeClass('x-item-disabled');
12446         //}
12447     },
12448
12449     // private
12450     onShow : function(){
12451         var ae = this.getActionEl();
12452         
12453         if(ae){
12454             ae.dom.style.display = '';
12455             ae.dom.style.visibility = 'visible';
12456         }
12457     },
12458
12459     // private
12460     
12461     onHide : function(){
12462         var ae = this.getActionEl();
12463         ae.dom.style.display = 'none';
12464     },
12465
12466     /**
12467      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12468      * by an implementing function.
12469      * @method
12470      * @param {EventObject} e
12471      */
12472     onTriggerClick : Roo.emptyFn
12473 });
12474  
12475 /*
12476 * Licence: LGPL
12477 */
12478
12479 /**
12480  * @class Roo.bootstrap.CardUploader
12481  * @extends Roo.bootstrap.Button
12482  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12483  * @cfg {Number} errorTimeout default 3000
12484  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12485  * @cfg {Array}  html The button text.
12486
12487  *
12488  * @constructor
12489  * Create a new CardUploader
12490  * @param {Object} config The config object
12491  */
12492
12493 Roo.bootstrap.CardUploader = function(config){
12494     
12495  
12496     
12497     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12498     
12499     
12500     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12501         return r.data.id
12502      });
12503     
12504     
12505 };
12506
12507 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12508     
12509      
12510     errorTimeout : 3000,
12511      
12512     images : false,
12513    
12514     fileCollection : false,
12515     allowBlank : true,
12516     
12517     getAutoCreate : function()
12518     {
12519         
12520         var cfg =  {
12521             cls :'form-group' ,
12522             cn : [
12523                
12524                 {
12525                     tag: 'label',
12526                    //cls : 'input-group-addon',
12527                     html : this.fieldLabel
12528
12529                 },
12530
12531                 {
12532                     tag: 'input',
12533                     type : 'hidden',
12534                     name : this.name,
12535                     value : this.value,
12536                     cls : 'd-none  form-control'
12537                 },
12538                 
12539                 {
12540                     tag: 'input',
12541                     multiple : 'multiple',
12542                     type : 'file',
12543                     cls : 'd-none  roo-card-upload-selector'
12544                 },
12545                 
12546                 {
12547                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12548                 },
12549                 {
12550                     cls : 'card-columns roo-card-uploader-container'
12551                 }
12552
12553             ]
12554         };
12555            
12556          
12557         return cfg;
12558     },
12559     
12560     getChildContainer : function() /// what children are added to.
12561     {
12562         return this.containerEl;
12563     },
12564    
12565     getButtonContainer : function() /// what children are added to.
12566     {
12567         return this.el.select(".roo-card-uploader-button-container").first();
12568     },
12569    
12570     initEvents : function()
12571     {
12572         
12573         Roo.bootstrap.Input.prototype.initEvents.call(this);
12574         
12575         var t = this;
12576         this.addxtype({
12577             xns: Roo.bootstrap,
12578
12579             xtype : 'Button',
12580             container_method : 'getButtonContainer' ,            
12581             html :  this.html, // fix changable?
12582             cls : 'w-100 ',
12583             listeners : {
12584                 'click' : function(btn, e) {
12585                     t.onClick(e);
12586                 }
12587             }
12588         });
12589         
12590         
12591         
12592         
12593         this.urlAPI = (window.createObjectURL && window) || 
12594                                 (window.URL && URL.revokeObjectURL && URL) || 
12595                                 (window.webkitURL && webkitURL);
12596                         
12597          
12598          
12599          
12600         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12601         
12602         this.selectorEl.on('change', this.onFileSelected, this);
12603         if (this.images) {
12604             var t = this;
12605             this.images.forEach(function(img) {
12606                 t.addCard(img)
12607             });
12608             this.images = false;
12609         }
12610         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12611          
12612        
12613     },
12614     
12615    
12616     onClick : function(e)
12617     {
12618         e.preventDefault();
12619          
12620         this.selectorEl.dom.click();
12621          
12622     },
12623     
12624     onFileSelected : function(e)
12625     {
12626         e.preventDefault();
12627         
12628         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12629             return;
12630         }
12631         
12632         Roo.each(this.selectorEl.dom.files, function(file){    
12633             this.addFile(file);
12634         }, this);
12635          
12636     },
12637     
12638       
12639     
12640       
12641     
12642     addFile : function(file)
12643     {
12644            
12645         if(typeof(file) === 'string'){
12646             throw "Add file by name?"; // should not happen
12647             return;
12648         }
12649         
12650         if(!file || !this.urlAPI){
12651             return;
12652         }
12653         
12654         // file;
12655         // file.type;
12656         
12657         var _this = this;
12658         
12659         
12660         var url = _this.urlAPI.createObjectURL( file);
12661            
12662         this.addCard({
12663             id : Roo.bootstrap.CardUploader.ID--,
12664             is_uploaded : false,
12665             src : url,
12666             srcfile : file,
12667             title : file.name,
12668             mimetype : file.type,
12669             preview : false,
12670             is_deleted : 0
12671         });
12672         
12673     },
12674     
12675     addCard : function (data)
12676     {
12677         // hidden input element?
12678         // if the file is not an image...
12679         //then we need to use something other that and header_image
12680         var t = this;
12681         //   remove.....
12682         var footer = [
12683             {
12684                 xns : Roo.bootstrap,
12685                 xtype : 'CardFooter',
12686                 items: [
12687                     {
12688                         xns : Roo.bootstrap,
12689                         xtype : 'Element',
12690                         cls : 'd-flex',
12691                         items : [
12692                             
12693                             {
12694                                 xns : Roo.bootstrap,
12695                                 xtype : 'Button',
12696                                 html : String.format("<small>{0}</small>", data.title),
12697                                 cls : 'col-11 text-left',
12698                                 size: 'sm',
12699                                 weight: 'link',
12700                                 fa : 'download',
12701                                 listeners : {
12702                                     click : function() {
12703                                         this.downloadCard(data.id)
12704                                     }
12705                                 }
12706                             },
12707                           
12708                             {
12709                                 xns : Roo.bootstrap,
12710                                 xtype : 'Button',
12711                                 
12712                                 size : 'sm',
12713                                 weight: 'danger',
12714                                 cls : 'col-1',
12715                                 fa : 'times',
12716                                 listeners : {
12717                                     click : function() {
12718                                         t.removeCard(data.id)
12719                                     }
12720                                 }
12721                             }
12722                         ]
12723                     }
12724                     
12725                 ] 
12726             }
12727             
12728         ];
12729         
12730         var cn = this.addxtype(
12731             {
12732                  
12733                 xns : Roo.bootstrap,
12734                 xtype : 'Card',
12735                 closeable : true,
12736                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12737                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12738                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12739                 data : data,
12740                 html : false,
12741                  
12742                 items : footer,
12743                 initEvents : function() {
12744                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12745                     this.imgEl = this.el.select('.card-img-top').first();
12746                     if (this.imgEl) {
12747                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12748                         this.imgEl.set({ 'pointer' : 'cursor' });
12749                                   
12750                     }
12751                     
12752                   
12753                 }
12754                 
12755             }
12756         );
12757         // dont' really need ot update items.
12758         // this.items.push(cn);
12759         this.fileCollection.add(cn);
12760         
12761         if (!data.srcfile) {
12762             this.updateInput();
12763             return;
12764         }
12765             
12766         var _t = this;
12767         var reader = new FileReader();
12768         reader.addEventListener("load", function() {  
12769             data.srcdata =  reader.result;
12770             _t.updateInput();
12771         });
12772         reader.readAsDataURL(data.srcfile);
12773         
12774         
12775         
12776     },
12777     removeCard : function(id)
12778     {
12779         
12780         var card  = this.fileCollection.get(id);
12781         card.data.is_deleted = 1;
12782         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12783         //this.fileCollection.remove(card);
12784         //this.items = this.items.filter(function(e) { return e != card });
12785         // dont' really need ot update items.
12786         card.el.dom.parentNode.removeChild(card.el.dom);
12787         this.updateInput();
12788
12789         
12790     },
12791     reset: function()
12792     {
12793         this.fileCollection.each(function(card) {
12794             if (card.el.dom && card.el.dom.parentNode) {
12795                 card.el.dom.parentNode.removeChild(card.el.dom);
12796             }
12797         });
12798         this.fileCollection.clear();
12799         this.updateInput();
12800     },
12801     
12802     updateInput : function()
12803     {
12804          var data = [];
12805         this.fileCollection.each(function(e) {
12806             data.push(e.data);
12807             
12808         });
12809         this.inputEl().dom.value = JSON.stringify(data);
12810         
12811         
12812         
12813     }
12814     
12815     
12816 });
12817
12818
12819 Roo.bootstrap.CardUploader.ID = -1;/*
12820  * Based on:
12821  * Ext JS Library 1.1.1
12822  * Copyright(c) 2006-2007, Ext JS, LLC.
12823  *
12824  * Originally Released Under LGPL - original licence link has changed is not relivant.
12825  *
12826  * Fork - LGPL
12827  * <script type="text/javascript">
12828  */
12829
12830
12831 /**
12832  * @class Roo.data.SortTypes
12833  * @singleton
12834  * Defines the default sorting (casting?) comparison functions used when sorting data.
12835  */
12836 Roo.data.SortTypes = {
12837     /**
12838      * Default sort that does nothing
12839      * @param {Mixed} s The value being converted
12840      * @return {Mixed} The comparison value
12841      */
12842     none : function(s){
12843         return s;
12844     },
12845     
12846     /**
12847      * The regular expression used to strip tags
12848      * @type {RegExp}
12849      * @property
12850      */
12851     stripTagsRE : /<\/?[^>]+>/gi,
12852     
12853     /**
12854      * Strips all HTML tags to sort on text only
12855      * @param {Mixed} s The value being converted
12856      * @return {String} The comparison value
12857      */
12858     asText : function(s){
12859         return String(s).replace(this.stripTagsRE, "");
12860     },
12861     
12862     /**
12863      * Strips all HTML tags to sort on text only - Case insensitive
12864      * @param {Mixed} s The value being converted
12865      * @return {String} The comparison value
12866      */
12867     asUCText : function(s){
12868         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12869     },
12870     
12871     /**
12872      * Case insensitive string
12873      * @param {Mixed} s The value being converted
12874      * @return {String} The comparison value
12875      */
12876     asUCString : function(s) {
12877         return String(s).toUpperCase();
12878     },
12879     
12880     /**
12881      * Date sorting
12882      * @param {Mixed} s The value being converted
12883      * @return {Number} The comparison value
12884      */
12885     asDate : function(s) {
12886         if(!s){
12887             return 0;
12888         }
12889         if(s instanceof Date){
12890             return s.getTime();
12891         }
12892         return Date.parse(String(s));
12893     },
12894     
12895     /**
12896      * Float sorting
12897      * @param {Mixed} s The value being converted
12898      * @return {Float} The comparison value
12899      */
12900     asFloat : function(s) {
12901         var val = parseFloat(String(s).replace(/,/g, ""));
12902         if(isNaN(val)) {
12903             val = 0;
12904         }
12905         return val;
12906     },
12907     
12908     /**
12909      * Integer sorting
12910      * @param {Mixed} s The value being converted
12911      * @return {Number} The comparison value
12912      */
12913     asInt : function(s) {
12914         var val = parseInt(String(s).replace(/,/g, ""));
12915         if(isNaN(val)) {
12916             val = 0;
12917         }
12918         return val;
12919     }
12920 };/*
12921  * Based on:
12922  * Ext JS Library 1.1.1
12923  * Copyright(c) 2006-2007, Ext JS, LLC.
12924  *
12925  * Originally Released Under LGPL - original licence link has changed is not relivant.
12926  *
12927  * Fork - LGPL
12928  * <script type="text/javascript">
12929  */
12930
12931 /**
12932 * @class Roo.data.Record
12933  * Instances of this class encapsulate both record <em>definition</em> information, and record
12934  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12935  * to access Records cached in an {@link Roo.data.Store} object.<br>
12936  * <p>
12937  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12938  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12939  * objects.<br>
12940  * <p>
12941  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12942  * @constructor
12943  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12944  * {@link #create}. The parameters are the same.
12945  * @param {Array} data An associative Array of data values keyed by the field name.
12946  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12947  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12948  * not specified an integer id is generated.
12949  */
12950 Roo.data.Record = function(data, id){
12951     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12952     this.data = data;
12953 };
12954
12955 /**
12956  * Generate a constructor for a specific record layout.
12957  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12958  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12959  * Each field definition object may contain the following properties: <ul>
12960  * <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,
12961  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12962  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12963  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12964  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12965  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12966  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12967  * this may be omitted.</p></li>
12968  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12969  * <ul><li>auto (Default, implies no conversion)</li>
12970  * <li>string</li>
12971  * <li>int</li>
12972  * <li>float</li>
12973  * <li>boolean</li>
12974  * <li>date</li></ul></p></li>
12975  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12976  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12977  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12978  * by the Reader into an object that will be stored in the Record. It is passed the
12979  * following parameters:<ul>
12980  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12981  * </ul></p></li>
12982  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12983  * </ul>
12984  * <br>usage:<br><pre><code>
12985 var TopicRecord = Roo.data.Record.create(
12986     {name: 'title', mapping: 'topic_title'},
12987     {name: 'author', mapping: 'username'},
12988     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12989     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12990     {name: 'lastPoster', mapping: 'user2'},
12991     {name: 'excerpt', mapping: 'post_text'}
12992 );
12993
12994 var myNewRecord = new TopicRecord({
12995     title: 'Do my job please',
12996     author: 'noobie',
12997     totalPosts: 1,
12998     lastPost: new Date(),
12999     lastPoster: 'Animal',
13000     excerpt: 'No way dude!'
13001 });
13002 myStore.add(myNewRecord);
13003 </code></pre>
13004  * @method create
13005  * @static
13006  */
13007 Roo.data.Record.create = function(o){
13008     var f = function(){
13009         f.superclass.constructor.apply(this, arguments);
13010     };
13011     Roo.extend(f, Roo.data.Record);
13012     var p = f.prototype;
13013     p.fields = new Roo.util.MixedCollection(false, function(field){
13014         return field.name;
13015     });
13016     for(var i = 0, len = o.length; i < len; i++){
13017         p.fields.add(new Roo.data.Field(o[i]));
13018     }
13019     f.getField = function(name){
13020         return p.fields.get(name);  
13021     };
13022     return f;
13023 };
13024
13025 Roo.data.Record.AUTO_ID = 1000;
13026 Roo.data.Record.EDIT = 'edit';
13027 Roo.data.Record.REJECT = 'reject';
13028 Roo.data.Record.COMMIT = 'commit';
13029
13030 Roo.data.Record.prototype = {
13031     /**
13032      * Readonly flag - true if this record has been modified.
13033      * @type Boolean
13034      */
13035     dirty : false,
13036     editing : false,
13037     error: null,
13038     modified: null,
13039
13040     // private
13041     join : function(store){
13042         this.store = store;
13043     },
13044
13045     /**
13046      * Set the named field to the specified value.
13047      * @param {String} name The name of the field to set.
13048      * @param {Object} value The value to set the field to.
13049      */
13050     set : function(name, value){
13051         if(this.data[name] == value){
13052             return;
13053         }
13054         this.dirty = true;
13055         if(!this.modified){
13056             this.modified = {};
13057         }
13058         if(typeof this.modified[name] == 'undefined'){
13059             this.modified[name] = this.data[name];
13060         }
13061         this.data[name] = value;
13062         if(!this.editing && this.store){
13063             this.store.afterEdit(this);
13064         }       
13065     },
13066
13067     /**
13068      * Get the value of the named field.
13069      * @param {String} name The name of the field to get the value of.
13070      * @return {Object} The value of the field.
13071      */
13072     get : function(name){
13073         return this.data[name]; 
13074     },
13075
13076     // private
13077     beginEdit : function(){
13078         this.editing = true;
13079         this.modified = {}; 
13080     },
13081
13082     // private
13083     cancelEdit : function(){
13084         this.editing = false;
13085         delete this.modified;
13086     },
13087
13088     // private
13089     endEdit : function(){
13090         this.editing = false;
13091         if(this.dirty && this.store){
13092             this.store.afterEdit(this);
13093         }
13094     },
13095
13096     /**
13097      * Usually called by the {@link Roo.data.Store} which owns the Record.
13098      * Rejects all changes made to the Record since either creation, or the last commit operation.
13099      * Modified fields are reverted to their original values.
13100      * <p>
13101      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13102      * of reject operations.
13103      */
13104     reject : function(){
13105         var m = this.modified;
13106         for(var n in m){
13107             if(typeof m[n] != "function"){
13108                 this.data[n] = m[n];
13109             }
13110         }
13111         this.dirty = false;
13112         delete this.modified;
13113         this.editing = false;
13114         if(this.store){
13115             this.store.afterReject(this);
13116         }
13117     },
13118
13119     /**
13120      * Usually called by the {@link Roo.data.Store} which owns the Record.
13121      * Commits all changes made to the Record since either creation, or the last commit operation.
13122      * <p>
13123      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13124      * of commit operations.
13125      */
13126     commit : function(){
13127         this.dirty = false;
13128         delete this.modified;
13129         this.editing = false;
13130         if(this.store){
13131             this.store.afterCommit(this);
13132         }
13133     },
13134
13135     // private
13136     hasError : function(){
13137         return this.error != null;
13138     },
13139
13140     // private
13141     clearError : function(){
13142         this.error = null;
13143     },
13144
13145     /**
13146      * Creates a copy of this record.
13147      * @param {String} id (optional) A new record id if you don't want to use this record's id
13148      * @return {Record}
13149      */
13150     copy : function(newId) {
13151         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13152     }
13153 };/*
13154  * Based on:
13155  * Ext JS Library 1.1.1
13156  * Copyright(c) 2006-2007, Ext JS, LLC.
13157  *
13158  * Originally Released Under LGPL - original licence link has changed is not relivant.
13159  *
13160  * Fork - LGPL
13161  * <script type="text/javascript">
13162  */
13163
13164
13165
13166 /**
13167  * @class Roo.data.Store
13168  * @extends Roo.util.Observable
13169  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13170  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13171  * <p>
13172  * 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
13173  * has no knowledge of the format of the data returned by the Proxy.<br>
13174  * <p>
13175  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13176  * instances from the data object. These records are cached and made available through accessor functions.
13177  * @constructor
13178  * Creates a new Store.
13179  * @param {Object} config A config object containing the objects needed for the Store to access data,
13180  * and read the data into Records.
13181  */
13182 Roo.data.Store = function(config){
13183     this.data = new Roo.util.MixedCollection(false);
13184     this.data.getKey = function(o){
13185         return o.id;
13186     };
13187     this.baseParams = {};
13188     // private
13189     this.paramNames = {
13190         "start" : "start",
13191         "limit" : "limit",
13192         "sort" : "sort",
13193         "dir" : "dir",
13194         "multisort" : "_multisort"
13195     };
13196
13197     if(config && config.data){
13198         this.inlineData = config.data;
13199         delete config.data;
13200     }
13201
13202     Roo.apply(this, config);
13203     
13204     if(this.reader){ // reader passed
13205         this.reader = Roo.factory(this.reader, Roo.data);
13206         this.reader.xmodule = this.xmodule || false;
13207         if(!this.recordType){
13208             this.recordType = this.reader.recordType;
13209         }
13210         if(this.reader.onMetaChange){
13211             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13212         }
13213     }
13214
13215     if(this.recordType){
13216         this.fields = this.recordType.prototype.fields;
13217     }
13218     this.modified = [];
13219
13220     this.addEvents({
13221         /**
13222          * @event datachanged
13223          * Fires when the data cache has changed, and a widget which is using this Store
13224          * as a Record cache should refresh its view.
13225          * @param {Store} this
13226          */
13227         datachanged : true,
13228         /**
13229          * @event metachange
13230          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13231          * @param {Store} this
13232          * @param {Object} meta The JSON metadata
13233          */
13234         metachange : true,
13235         /**
13236          * @event add
13237          * Fires when Records have been added to the Store
13238          * @param {Store} this
13239          * @param {Roo.data.Record[]} records The array of Records added
13240          * @param {Number} index The index at which the record(s) were added
13241          */
13242         add : true,
13243         /**
13244          * @event remove
13245          * Fires when a Record has been removed from the Store
13246          * @param {Store} this
13247          * @param {Roo.data.Record} record The Record that was removed
13248          * @param {Number} index The index at which the record was removed
13249          */
13250         remove : true,
13251         /**
13252          * @event update
13253          * Fires when a Record has been updated
13254          * @param {Store} this
13255          * @param {Roo.data.Record} record The Record that was updated
13256          * @param {String} operation The update operation being performed.  Value may be one of:
13257          * <pre><code>
13258  Roo.data.Record.EDIT
13259  Roo.data.Record.REJECT
13260  Roo.data.Record.COMMIT
13261          * </code></pre>
13262          */
13263         update : true,
13264         /**
13265          * @event clear
13266          * Fires when the data cache has been cleared.
13267          * @param {Store} this
13268          */
13269         clear : true,
13270         /**
13271          * @event beforeload
13272          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13273          * the load action will be canceled.
13274          * @param {Store} this
13275          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13276          */
13277         beforeload : true,
13278         /**
13279          * @event beforeloadadd
13280          * Fires after a new set of Records has been loaded.
13281          * @param {Store} this
13282          * @param {Roo.data.Record[]} records The Records that were loaded
13283          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13284          */
13285         beforeloadadd : true,
13286         /**
13287          * @event load
13288          * Fires after a new set of Records has been loaded, before they are added to the store.
13289          * @param {Store} this
13290          * @param {Roo.data.Record[]} records The Records that were loaded
13291          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13292          * @params {Object} return from reader
13293          */
13294         load : true,
13295         /**
13296          * @event loadexception
13297          * Fires if an exception occurs in the Proxy during loading.
13298          * Called with the signature of the Proxy's "loadexception" event.
13299          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13300          * 
13301          * @param {Proxy} 
13302          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13303          * @param {Object} load options 
13304          * @param {Object} jsonData from your request (normally this contains the Exception)
13305          */
13306         loadexception : true
13307     });
13308     
13309     if(this.proxy){
13310         this.proxy = Roo.factory(this.proxy, Roo.data);
13311         this.proxy.xmodule = this.xmodule || false;
13312         this.relayEvents(this.proxy,  ["loadexception"]);
13313     }
13314     this.sortToggle = {};
13315     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13316
13317     Roo.data.Store.superclass.constructor.call(this);
13318
13319     if(this.inlineData){
13320         this.loadData(this.inlineData);
13321         delete this.inlineData;
13322     }
13323 };
13324
13325 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13326      /**
13327     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13328     * without a remote query - used by combo/forms at present.
13329     */
13330     
13331     /**
13332     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13333     */
13334     /**
13335     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13336     */
13337     /**
13338     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13339     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13340     */
13341     /**
13342     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13343     * on any HTTP request
13344     */
13345     /**
13346     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13347     */
13348     /**
13349     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13350     */
13351     multiSort: false,
13352     /**
13353     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13354     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13355     */
13356     remoteSort : false,
13357
13358     /**
13359     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13360      * loaded or when a record is removed. (defaults to false).
13361     */
13362     pruneModifiedRecords : false,
13363
13364     // private
13365     lastOptions : null,
13366
13367     /**
13368      * Add Records to the Store and fires the add event.
13369      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13370      */
13371     add : function(records){
13372         records = [].concat(records);
13373         for(var i = 0, len = records.length; i < len; i++){
13374             records[i].join(this);
13375         }
13376         var index = this.data.length;
13377         this.data.addAll(records);
13378         this.fireEvent("add", this, records, index);
13379     },
13380
13381     /**
13382      * Remove a Record from the Store and fires the remove event.
13383      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13384      */
13385     remove : function(record){
13386         var index = this.data.indexOf(record);
13387         this.data.removeAt(index);
13388  
13389         if(this.pruneModifiedRecords){
13390             this.modified.remove(record);
13391         }
13392         this.fireEvent("remove", this, record, index);
13393     },
13394
13395     /**
13396      * Remove all Records from the Store and fires the clear event.
13397      */
13398     removeAll : function(){
13399         this.data.clear();
13400         if(this.pruneModifiedRecords){
13401             this.modified = [];
13402         }
13403         this.fireEvent("clear", this);
13404     },
13405
13406     /**
13407      * Inserts Records to the Store at the given index and fires the add event.
13408      * @param {Number} index The start index at which to insert the passed Records.
13409      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13410      */
13411     insert : function(index, records){
13412         records = [].concat(records);
13413         for(var i = 0, len = records.length; i < len; i++){
13414             this.data.insert(index, records[i]);
13415             records[i].join(this);
13416         }
13417         this.fireEvent("add", this, records, index);
13418     },
13419
13420     /**
13421      * Get the index within the cache of the passed Record.
13422      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13423      * @return {Number} The index of the passed Record. Returns -1 if not found.
13424      */
13425     indexOf : function(record){
13426         return this.data.indexOf(record);
13427     },
13428
13429     /**
13430      * Get the index within the cache of the Record with the passed id.
13431      * @param {String} id The id of the Record to find.
13432      * @return {Number} The index of the Record. Returns -1 if not found.
13433      */
13434     indexOfId : function(id){
13435         return this.data.indexOfKey(id);
13436     },
13437
13438     /**
13439      * Get the Record with the specified id.
13440      * @param {String} id The id of the Record to find.
13441      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13442      */
13443     getById : function(id){
13444         return this.data.key(id);
13445     },
13446
13447     /**
13448      * Get the Record at the specified index.
13449      * @param {Number} index The index of the Record to find.
13450      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13451      */
13452     getAt : function(index){
13453         return this.data.itemAt(index);
13454     },
13455
13456     /**
13457      * Returns a range of Records between specified indices.
13458      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13459      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13460      * @return {Roo.data.Record[]} An array of Records
13461      */
13462     getRange : function(start, end){
13463         return this.data.getRange(start, end);
13464     },
13465
13466     // private
13467     storeOptions : function(o){
13468         o = Roo.apply({}, o);
13469         delete o.callback;
13470         delete o.scope;
13471         this.lastOptions = o;
13472     },
13473
13474     /**
13475      * Loads the Record cache from the configured Proxy using the configured Reader.
13476      * <p>
13477      * If using remote paging, then the first load call must specify the <em>start</em>
13478      * and <em>limit</em> properties in the options.params property to establish the initial
13479      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13480      * <p>
13481      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13482      * and this call will return before the new data has been loaded. Perform any post-processing
13483      * in a callback function, or in a "load" event handler.</strong>
13484      * <p>
13485      * @param {Object} options An object containing properties which control loading options:<ul>
13486      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13487      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13488      * passed the following arguments:<ul>
13489      * <li>r : Roo.data.Record[]</li>
13490      * <li>options: Options object from the load call</li>
13491      * <li>success: Boolean success indicator</li></ul></li>
13492      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13493      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13494      * </ul>
13495      */
13496     load : function(options){
13497         options = options || {};
13498         if(this.fireEvent("beforeload", this, options) !== false){
13499             this.storeOptions(options);
13500             var p = Roo.apply(options.params || {}, this.baseParams);
13501             // if meta was not loaded from remote source.. try requesting it.
13502             if (!this.reader.metaFromRemote) {
13503                 p._requestMeta = 1;
13504             }
13505             if(this.sortInfo && this.remoteSort){
13506                 var pn = this.paramNames;
13507                 p[pn["sort"]] = this.sortInfo.field;
13508                 p[pn["dir"]] = this.sortInfo.direction;
13509             }
13510             if (this.multiSort) {
13511                 var pn = this.paramNames;
13512                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13513             }
13514             
13515             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13516         }
13517     },
13518
13519     /**
13520      * Reloads the Record cache from the configured Proxy using the configured Reader and
13521      * the options from the last load operation performed.
13522      * @param {Object} options (optional) An object containing properties which may override the options
13523      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13524      * the most recently used options are reused).
13525      */
13526     reload : function(options){
13527         this.load(Roo.applyIf(options||{}, this.lastOptions));
13528     },
13529
13530     // private
13531     // Called as a callback by the Reader during a load operation.
13532     loadRecords : function(o, options, success){
13533         if(!o || success === false){
13534             if(success !== false){
13535                 this.fireEvent("load", this, [], options, o);
13536             }
13537             if(options.callback){
13538                 options.callback.call(options.scope || this, [], options, false);
13539             }
13540             return;
13541         }
13542         // if data returned failure - throw an exception.
13543         if (o.success === false) {
13544             // show a message if no listener is registered.
13545             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13546                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13547             }
13548             // loadmask wil be hooked into this..
13549             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13550             return;
13551         }
13552         var r = o.records, t = o.totalRecords || r.length;
13553         
13554         this.fireEvent("beforeloadadd", this, r, options, o);
13555         
13556         if(!options || options.add !== true){
13557             if(this.pruneModifiedRecords){
13558                 this.modified = [];
13559             }
13560             for(var i = 0, len = r.length; i < len; i++){
13561                 r[i].join(this);
13562             }
13563             if(this.snapshot){
13564                 this.data = this.snapshot;
13565                 delete this.snapshot;
13566             }
13567             this.data.clear();
13568             this.data.addAll(r);
13569             this.totalLength = t;
13570             this.applySort();
13571             this.fireEvent("datachanged", this);
13572         }else{
13573             this.totalLength = Math.max(t, this.data.length+r.length);
13574             this.add(r);
13575         }
13576         
13577         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13578                 
13579             var e = new Roo.data.Record({});
13580
13581             e.set(this.parent.displayField, this.parent.emptyTitle);
13582             e.set(this.parent.valueField, '');
13583
13584             this.insert(0, e);
13585         }
13586             
13587         this.fireEvent("load", this, r, options, o);
13588         if(options.callback){
13589             options.callback.call(options.scope || this, r, options, true);
13590         }
13591     },
13592
13593
13594     /**
13595      * Loads data from a passed data block. A Reader which understands the format of the data
13596      * must have been configured in the constructor.
13597      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13598      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13599      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13600      */
13601     loadData : function(o, append){
13602         var r = this.reader.readRecords(o);
13603         this.loadRecords(r, {add: append}, true);
13604     },
13605     
13606      /**
13607      * using 'cn' the nested child reader read the child array into it's child stores.
13608      * @param {Object} rec The record with a 'children array
13609      */
13610     loadDataFromChildren : function(rec)
13611     {
13612         this.loadData(this.reader.toLoadData(rec));
13613     },
13614     
13615
13616     /**
13617      * Gets the number of cached records.
13618      * <p>
13619      * <em>If using paging, this may not be the total size of the dataset. If the data object
13620      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13621      * the data set size</em>
13622      */
13623     getCount : function(){
13624         return this.data.length || 0;
13625     },
13626
13627     /**
13628      * Gets the total number of records in the dataset as returned by the server.
13629      * <p>
13630      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13631      * the dataset size</em>
13632      */
13633     getTotalCount : function(){
13634         return this.totalLength || 0;
13635     },
13636
13637     /**
13638      * Returns the sort state of the Store as an object with two properties:
13639      * <pre><code>
13640  field {String} The name of the field by which the Records are sorted
13641  direction {String} The sort order, "ASC" or "DESC"
13642      * </code></pre>
13643      */
13644     getSortState : function(){
13645         return this.sortInfo;
13646     },
13647
13648     // private
13649     applySort : function(){
13650         if(this.sortInfo && !this.remoteSort){
13651             var s = this.sortInfo, f = s.field;
13652             var st = this.fields.get(f).sortType;
13653             var fn = function(r1, r2){
13654                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13655                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13656             };
13657             this.data.sort(s.direction, fn);
13658             if(this.snapshot && this.snapshot != this.data){
13659                 this.snapshot.sort(s.direction, fn);
13660             }
13661         }
13662     },
13663
13664     /**
13665      * Sets the default sort column and order to be used by the next load operation.
13666      * @param {String} fieldName The name of the field to sort by.
13667      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13668      */
13669     setDefaultSort : function(field, dir){
13670         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13671     },
13672
13673     /**
13674      * Sort the Records.
13675      * If remote sorting is used, the sort is performed on the server, and the cache is
13676      * reloaded. If local sorting is used, the cache is sorted internally.
13677      * @param {String} fieldName The name of the field to sort by.
13678      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13679      */
13680     sort : function(fieldName, dir){
13681         var f = this.fields.get(fieldName);
13682         if(!dir){
13683             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13684             
13685             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13686                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13687             }else{
13688                 dir = f.sortDir;
13689             }
13690         }
13691         this.sortToggle[f.name] = dir;
13692         this.sortInfo = {field: f.name, direction: dir};
13693         if(!this.remoteSort){
13694             this.applySort();
13695             this.fireEvent("datachanged", this);
13696         }else{
13697             this.load(this.lastOptions);
13698         }
13699     },
13700
13701     /**
13702      * Calls the specified function for each of the Records in the cache.
13703      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13704      * Returning <em>false</em> aborts and exits the iteration.
13705      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13706      */
13707     each : function(fn, scope){
13708         this.data.each(fn, scope);
13709     },
13710
13711     /**
13712      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13713      * (e.g., during paging).
13714      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13715      */
13716     getModifiedRecords : function(){
13717         return this.modified;
13718     },
13719
13720     // private
13721     createFilterFn : function(property, value, anyMatch){
13722         if(!value.exec){ // not a regex
13723             value = String(value);
13724             if(value.length == 0){
13725                 return false;
13726             }
13727             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13728         }
13729         return function(r){
13730             return value.test(r.data[property]);
13731         };
13732     },
13733
13734     /**
13735      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13736      * @param {String} property A field on your records
13737      * @param {Number} start The record index to start at (defaults to 0)
13738      * @param {Number} end The last record index to include (defaults to length - 1)
13739      * @return {Number} The sum
13740      */
13741     sum : function(property, start, end){
13742         var rs = this.data.items, v = 0;
13743         start = start || 0;
13744         end = (end || end === 0) ? end : rs.length-1;
13745
13746         for(var i = start; i <= end; i++){
13747             v += (rs[i].data[property] || 0);
13748         }
13749         return v;
13750     },
13751
13752     /**
13753      * Filter the records by a specified property.
13754      * @param {String} field A field on your records
13755      * @param {String/RegExp} value Either a string that the field
13756      * should start with or a RegExp to test against the field
13757      * @param {Boolean} anyMatch True to match any part not just the beginning
13758      */
13759     filter : function(property, value, anyMatch){
13760         var fn = this.createFilterFn(property, value, anyMatch);
13761         return fn ? this.filterBy(fn) : this.clearFilter();
13762     },
13763
13764     /**
13765      * Filter by a function. The specified function will be called with each
13766      * record in this data source. If the function returns true the record is included,
13767      * otherwise it is filtered.
13768      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769      * @param {Object} scope (optional) The scope of the function (defaults to this)
13770      */
13771     filterBy : function(fn, scope){
13772         this.snapshot = this.snapshot || this.data;
13773         this.data = this.queryBy(fn, scope||this);
13774         this.fireEvent("datachanged", this);
13775     },
13776
13777     /**
13778      * Query the records by a specified property.
13779      * @param {String} field A field on your records
13780      * @param {String/RegExp} value Either a string that the field
13781      * should start with or a RegExp to test against the field
13782      * @param {Boolean} anyMatch True to match any part not just the beginning
13783      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13784      */
13785     query : function(property, value, anyMatch){
13786         var fn = this.createFilterFn(property, value, anyMatch);
13787         return fn ? this.queryBy(fn) : this.data.clone();
13788     },
13789
13790     /**
13791      * Query by a function. The specified function will be called with each
13792      * record in this data source. If the function returns true the record is included
13793      * in the results.
13794      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13795      * @param {Object} scope (optional) The scope of the function (defaults to this)
13796       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13797      **/
13798     queryBy : function(fn, scope){
13799         var data = this.snapshot || this.data;
13800         return data.filterBy(fn, scope||this);
13801     },
13802
13803     /**
13804      * Collects unique values for a particular dataIndex from this store.
13805      * @param {String} dataIndex The property to collect
13806      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13807      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13808      * @return {Array} An array of the unique values
13809      **/
13810     collect : function(dataIndex, allowNull, bypassFilter){
13811         var d = (bypassFilter === true && this.snapshot) ?
13812                 this.snapshot.items : this.data.items;
13813         var v, sv, r = [], l = {};
13814         for(var i = 0, len = d.length; i < len; i++){
13815             v = d[i].data[dataIndex];
13816             sv = String(v);
13817             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13818                 l[sv] = true;
13819                 r[r.length] = v;
13820             }
13821         }
13822         return r;
13823     },
13824
13825     /**
13826      * Revert to a view of the Record cache with no filtering applied.
13827      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13828      */
13829     clearFilter : function(suppressEvent){
13830         if(this.snapshot && this.snapshot != this.data){
13831             this.data = this.snapshot;
13832             delete this.snapshot;
13833             if(suppressEvent !== true){
13834                 this.fireEvent("datachanged", this);
13835             }
13836         }
13837     },
13838
13839     // private
13840     afterEdit : function(record){
13841         if(this.modified.indexOf(record) == -1){
13842             this.modified.push(record);
13843         }
13844         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13845     },
13846     
13847     // private
13848     afterReject : function(record){
13849         this.modified.remove(record);
13850         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13851     },
13852
13853     // private
13854     afterCommit : function(record){
13855         this.modified.remove(record);
13856         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13857     },
13858
13859     /**
13860      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13861      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13862      */
13863     commitChanges : function(){
13864         var m = this.modified.slice(0);
13865         this.modified = [];
13866         for(var i = 0, len = m.length; i < len; i++){
13867             m[i].commit();
13868         }
13869     },
13870
13871     /**
13872      * Cancel outstanding changes on all changed records.
13873      */
13874     rejectChanges : function(){
13875         var m = this.modified.slice(0);
13876         this.modified = [];
13877         for(var i = 0, len = m.length; i < len; i++){
13878             m[i].reject();
13879         }
13880     },
13881
13882     onMetaChange : function(meta, rtype, o){
13883         this.recordType = rtype;
13884         this.fields = rtype.prototype.fields;
13885         delete this.snapshot;
13886         this.sortInfo = meta.sortInfo || this.sortInfo;
13887         this.modified = [];
13888         this.fireEvent('metachange', this, this.reader.meta);
13889     },
13890     
13891     moveIndex : function(data, type)
13892     {
13893         var index = this.indexOf(data);
13894         
13895         var newIndex = index + type;
13896         
13897         this.remove(data);
13898         
13899         this.insert(newIndex, data);
13900         
13901     }
13902 });/*
13903  * Based on:
13904  * Ext JS Library 1.1.1
13905  * Copyright(c) 2006-2007, Ext JS, LLC.
13906  *
13907  * Originally Released Under LGPL - original licence link has changed is not relivant.
13908  *
13909  * Fork - LGPL
13910  * <script type="text/javascript">
13911  */
13912
13913 /**
13914  * @class Roo.data.SimpleStore
13915  * @extends Roo.data.Store
13916  * Small helper class to make creating Stores from Array data easier.
13917  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13918  * @cfg {Array} fields An array of field definition objects, or field name strings.
13919  * @cfg {Object} an existing reader (eg. copied from another store)
13920  * @cfg {Array} data The multi-dimensional array of data
13921  * @constructor
13922  * @param {Object} config
13923  */
13924 Roo.data.SimpleStore = function(config)
13925 {
13926     Roo.data.SimpleStore.superclass.constructor.call(this, {
13927         isLocal : true,
13928         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13929                 id: config.id
13930             },
13931             Roo.data.Record.create(config.fields)
13932         ),
13933         proxy : new Roo.data.MemoryProxy(config.data)
13934     });
13935     this.load();
13936 };
13937 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13938  * Based on:
13939  * Ext JS Library 1.1.1
13940  * Copyright(c) 2006-2007, Ext JS, LLC.
13941  *
13942  * Originally Released Under LGPL - original licence link has changed is not relivant.
13943  *
13944  * Fork - LGPL
13945  * <script type="text/javascript">
13946  */
13947
13948 /**
13949 /**
13950  * @extends Roo.data.Store
13951  * @class Roo.data.JsonStore
13952  * Small helper class to make creating Stores for JSON data easier. <br/>
13953 <pre><code>
13954 var store = new Roo.data.JsonStore({
13955     url: 'get-images.php',
13956     root: 'images',
13957     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13958 });
13959 </code></pre>
13960  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13961  * JsonReader and HttpProxy (unless inline data is provided).</b>
13962  * @cfg {Array} fields An array of field definition objects, or field name strings.
13963  * @constructor
13964  * @param {Object} config
13965  */
13966 Roo.data.JsonStore = function(c){
13967     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13968         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13969         reader: new Roo.data.JsonReader(c, c.fields)
13970     }));
13971 };
13972 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13973  * Based on:
13974  * Ext JS Library 1.1.1
13975  * Copyright(c) 2006-2007, Ext JS, LLC.
13976  *
13977  * Originally Released Under LGPL - original licence link has changed is not relivant.
13978  *
13979  * Fork - LGPL
13980  * <script type="text/javascript">
13981  */
13982
13983  
13984 Roo.data.Field = function(config){
13985     if(typeof config == "string"){
13986         config = {name: config};
13987     }
13988     Roo.apply(this, config);
13989     
13990     if(!this.type){
13991         this.type = "auto";
13992     }
13993     
13994     var st = Roo.data.SortTypes;
13995     // named sortTypes are supported, here we look them up
13996     if(typeof this.sortType == "string"){
13997         this.sortType = st[this.sortType];
13998     }
13999     
14000     // set default sortType for strings and dates
14001     if(!this.sortType){
14002         switch(this.type){
14003             case "string":
14004                 this.sortType = st.asUCString;
14005                 break;
14006             case "date":
14007                 this.sortType = st.asDate;
14008                 break;
14009             default:
14010                 this.sortType = st.none;
14011         }
14012     }
14013
14014     // define once
14015     var stripRe = /[\$,%]/g;
14016
14017     // prebuilt conversion function for this field, instead of
14018     // switching every time we're reading a value
14019     if(!this.convert){
14020         var cv, dateFormat = this.dateFormat;
14021         switch(this.type){
14022             case "":
14023             case "auto":
14024             case undefined:
14025                 cv = function(v){ return v; };
14026                 break;
14027             case "string":
14028                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14029                 break;
14030             case "int":
14031                 cv = function(v){
14032                     return v !== undefined && v !== null && v !== '' ?
14033                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14034                     };
14035                 break;
14036             case "float":
14037                 cv = function(v){
14038                     return v !== undefined && v !== null && v !== '' ?
14039                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14040                     };
14041                 break;
14042             case "bool":
14043             case "boolean":
14044                 cv = function(v){ return v === true || v === "true" || v == 1; };
14045                 break;
14046             case "date":
14047                 cv = function(v){
14048                     if(!v){
14049                         return '';
14050                     }
14051                     if(v instanceof Date){
14052                         return v;
14053                     }
14054                     if(dateFormat){
14055                         if(dateFormat == "timestamp"){
14056                             return new Date(v*1000);
14057                         }
14058                         return Date.parseDate(v, dateFormat);
14059                     }
14060                     var parsed = Date.parse(v);
14061                     return parsed ? new Date(parsed) : null;
14062                 };
14063              break;
14064             
14065         }
14066         this.convert = cv;
14067     }
14068 };
14069
14070 Roo.data.Field.prototype = {
14071     dateFormat: null,
14072     defaultValue: "",
14073     mapping: null,
14074     sortType : null,
14075     sortDir : "ASC"
14076 };/*
14077  * Based on:
14078  * Ext JS Library 1.1.1
14079  * Copyright(c) 2006-2007, Ext JS, LLC.
14080  *
14081  * Originally Released Under LGPL - original licence link has changed is not relivant.
14082  *
14083  * Fork - LGPL
14084  * <script type="text/javascript">
14085  */
14086  
14087 // Base class for reading structured data from a data source.  This class is intended to be
14088 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14089
14090 /**
14091  * @class Roo.data.DataReader
14092  * Base class for reading structured data from a data source.  This class is intended to be
14093  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14094  */
14095
14096 Roo.data.DataReader = function(meta, recordType){
14097     
14098     this.meta = meta;
14099     
14100     this.recordType = recordType instanceof Array ? 
14101         Roo.data.Record.create(recordType) : recordType;
14102 };
14103
14104 Roo.data.DataReader.prototype = {
14105     
14106     
14107     readerType : 'Data',
14108      /**
14109      * Create an empty record
14110      * @param {Object} data (optional) - overlay some values
14111      * @return {Roo.data.Record} record created.
14112      */
14113     newRow :  function(d) {
14114         var da =  {};
14115         this.recordType.prototype.fields.each(function(c) {
14116             switch( c.type) {
14117                 case 'int' : da[c.name] = 0; break;
14118                 case 'date' : da[c.name] = new Date(); break;
14119                 case 'float' : da[c.name] = 0.0; break;
14120                 case 'boolean' : da[c.name] = false; break;
14121                 default : da[c.name] = ""; break;
14122             }
14123             
14124         });
14125         return new this.recordType(Roo.apply(da, d));
14126     }
14127     
14128     
14129 };/*
14130  * Based on:
14131  * Ext JS Library 1.1.1
14132  * Copyright(c) 2006-2007, Ext JS, LLC.
14133  *
14134  * Originally Released Under LGPL - original licence link has changed is not relivant.
14135  *
14136  * Fork - LGPL
14137  * <script type="text/javascript">
14138  */
14139
14140 /**
14141  * @class Roo.data.DataProxy
14142  * @extends Roo.data.Observable
14143  * This class is an abstract base class for implementations which provide retrieval of
14144  * unformatted data objects.<br>
14145  * <p>
14146  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14147  * (of the appropriate type which knows how to parse the data object) to provide a block of
14148  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14149  * <p>
14150  * Custom implementations must implement the load method as described in
14151  * {@link Roo.data.HttpProxy#load}.
14152  */
14153 Roo.data.DataProxy = function(){
14154     this.addEvents({
14155         /**
14156          * @event beforeload
14157          * Fires before a network request is made to retrieve a data object.
14158          * @param {Object} This DataProxy object.
14159          * @param {Object} params The params parameter to the load function.
14160          */
14161         beforeload : true,
14162         /**
14163          * @event load
14164          * Fires before the load method's callback is called.
14165          * @param {Object} This DataProxy object.
14166          * @param {Object} o The data object.
14167          * @param {Object} arg The callback argument object passed to the load function.
14168          */
14169         load : true,
14170         /**
14171          * @event loadexception
14172          * Fires if an Exception occurs during data retrieval.
14173          * @param {Object} This DataProxy object.
14174          * @param {Object} o The data object.
14175          * @param {Object} arg The callback argument object passed to the load function.
14176          * @param {Object} e The Exception.
14177          */
14178         loadexception : true
14179     });
14180     Roo.data.DataProxy.superclass.constructor.call(this);
14181 };
14182
14183 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14184
14185     /**
14186      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14187      */
14188 /*
14189  * Based on:
14190  * Ext JS Library 1.1.1
14191  * Copyright(c) 2006-2007, Ext JS, LLC.
14192  *
14193  * Originally Released Under LGPL - original licence link has changed is not relivant.
14194  *
14195  * Fork - LGPL
14196  * <script type="text/javascript">
14197  */
14198 /**
14199  * @class Roo.data.MemoryProxy
14200  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14201  * to the Reader when its load method is called.
14202  * @constructor
14203  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14204  */
14205 Roo.data.MemoryProxy = function(data){
14206     if (data.data) {
14207         data = data.data;
14208     }
14209     Roo.data.MemoryProxy.superclass.constructor.call(this);
14210     this.data = data;
14211 };
14212
14213 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14214     
14215     /**
14216      * Load data from the requested source (in this case an in-memory
14217      * data object passed to the constructor), read the data object into
14218      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14219      * process that block using the passed callback.
14220      * @param {Object} params This parameter is not used by the MemoryProxy class.
14221      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14222      * object into a block of Roo.data.Records.
14223      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14224      * The function must be passed <ul>
14225      * <li>The Record block object</li>
14226      * <li>The "arg" argument from the load function</li>
14227      * <li>A boolean success indicator</li>
14228      * </ul>
14229      * @param {Object} scope The scope in which to call the callback
14230      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14231      */
14232     load : function(params, reader, callback, scope, arg){
14233         params = params || {};
14234         var result;
14235         try {
14236             result = reader.readRecords(params.data ? params.data :this.data);
14237         }catch(e){
14238             this.fireEvent("loadexception", this, arg, null, e);
14239             callback.call(scope, null, arg, false);
14240             return;
14241         }
14242         callback.call(scope, result, arg, true);
14243     },
14244     
14245     // private
14246     update : function(params, records){
14247         
14248     }
14249 });/*
14250  * Based on:
14251  * Ext JS Library 1.1.1
14252  * Copyright(c) 2006-2007, Ext JS, LLC.
14253  *
14254  * Originally Released Under LGPL - original licence link has changed is not relivant.
14255  *
14256  * Fork - LGPL
14257  * <script type="text/javascript">
14258  */
14259 /**
14260  * @class Roo.data.HttpProxy
14261  * @extends Roo.data.DataProxy
14262  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14263  * configured to reference a certain URL.<br><br>
14264  * <p>
14265  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14266  * from which the running page was served.<br><br>
14267  * <p>
14268  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14269  * <p>
14270  * Be aware that to enable the browser to parse an XML document, the server must set
14271  * the Content-Type header in the HTTP response to "text/xml".
14272  * @constructor
14273  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14274  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14275  * will be used to make the request.
14276  */
14277 Roo.data.HttpProxy = function(conn){
14278     Roo.data.HttpProxy.superclass.constructor.call(this);
14279     // is conn a conn config or a real conn?
14280     this.conn = conn;
14281     this.useAjax = !conn || !conn.events;
14282   
14283 };
14284
14285 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14286     // thse are take from connection...
14287     
14288     /**
14289      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14290      */
14291     /**
14292      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14293      * extra parameters to each request made by this object. (defaults to undefined)
14294      */
14295     /**
14296      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14297      *  to each request made by this object. (defaults to undefined)
14298      */
14299     /**
14300      * @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)
14301      */
14302     /**
14303      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14304      */
14305      /**
14306      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14307      * @type Boolean
14308      */
14309   
14310
14311     /**
14312      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14313      * @type Boolean
14314      */
14315     /**
14316      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14317      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14318      * a finer-grained basis than the DataProxy events.
14319      */
14320     getConnection : function(){
14321         return this.useAjax ? Roo.Ajax : this.conn;
14322     },
14323
14324     /**
14325      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14326      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14327      * process that block using the passed callback.
14328      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14329      * for the request to the remote server.
14330      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14331      * object into a block of Roo.data.Records.
14332      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14333      * The function must be passed <ul>
14334      * <li>The Record block object</li>
14335      * <li>The "arg" argument from the load function</li>
14336      * <li>A boolean success indicator</li>
14337      * </ul>
14338      * @param {Object} scope The scope in which to call the callback
14339      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14340      */
14341     load : function(params, reader, callback, scope, arg){
14342         if(this.fireEvent("beforeload", this, params) !== false){
14343             var  o = {
14344                 params : params || {},
14345                 request: {
14346                     callback : callback,
14347                     scope : scope,
14348                     arg : arg
14349                 },
14350                 reader: reader,
14351                 callback : this.loadResponse,
14352                 scope: this
14353             };
14354             if(this.useAjax){
14355                 Roo.applyIf(o, this.conn);
14356                 if(this.activeRequest){
14357                     Roo.Ajax.abort(this.activeRequest);
14358                 }
14359                 this.activeRequest = Roo.Ajax.request(o);
14360             }else{
14361                 this.conn.request(o);
14362             }
14363         }else{
14364             callback.call(scope||this, null, arg, false);
14365         }
14366     },
14367
14368     // private
14369     loadResponse : function(o, success, response){
14370         delete this.activeRequest;
14371         if(!success){
14372             this.fireEvent("loadexception", this, o, response);
14373             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14374             return;
14375         }
14376         var result;
14377         try {
14378             result = o.reader.read(response);
14379         }catch(e){
14380             this.fireEvent("loadexception", this, o, response, e);
14381             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14382             return;
14383         }
14384         
14385         this.fireEvent("load", this, o, o.request.arg);
14386         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14387     },
14388
14389     // private
14390     update : function(dataSet){
14391
14392     },
14393
14394     // private
14395     updateResponse : function(dataSet){
14396
14397     }
14398 });/*
14399  * Based on:
14400  * Ext JS Library 1.1.1
14401  * Copyright(c) 2006-2007, Ext JS, LLC.
14402  *
14403  * Originally Released Under LGPL - original licence link has changed is not relivant.
14404  *
14405  * Fork - LGPL
14406  * <script type="text/javascript">
14407  */
14408
14409 /**
14410  * @class Roo.data.ScriptTagProxy
14411  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14412  * other than the originating domain of the running page.<br><br>
14413  * <p>
14414  * <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
14415  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14416  * <p>
14417  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14418  * source code that is used as the source inside a &lt;script> tag.<br><br>
14419  * <p>
14420  * In order for the browser to process the returned data, the server must wrap the data object
14421  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14422  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14423  * depending on whether the callback name was passed:
14424  * <p>
14425  * <pre><code>
14426 boolean scriptTag = false;
14427 String cb = request.getParameter("callback");
14428 if (cb != null) {
14429     scriptTag = true;
14430     response.setContentType("text/javascript");
14431 } else {
14432     response.setContentType("application/x-json");
14433 }
14434 Writer out = response.getWriter();
14435 if (scriptTag) {
14436     out.write(cb + "(");
14437 }
14438 out.print(dataBlock.toJsonString());
14439 if (scriptTag) {
14440     out.write(");");
14441 }
14442 </pre></code>
14443  *
14444  * @constructor
14445  * @param {Object} config A configuration object.
14446  */
14447 Roo.data.ScriptTagProxy = function(config){
14448     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14449     Roo.apply(this, config);
14450     this.head = document.getElementsByTagName("head")[0];
14451 };
14452
14453 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14454
14455 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14456     /**
14457      * @cfg {String} url The URL from which to request the data object.
14458      */
14459     /**
14460      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14461      */
14462     timeout : 30000,
14463     /**
14464      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14465      * the server the name of the callback function set up by the load call to process the returned data object.
14466      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14467      * javascript output which calls this named function passing the data object as its only parameter.
14468      */
14469     callbackParam : "callback",
14470     /**
14471      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14472      * name to the request.
14473      */
14474     nocache : true,
14475
14476     /**
14477      * Load data from the configured URL, read the data object into
14478      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14479      * process that block using the passed callback.
14480      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14481      * for the request to the remote server.
14482      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14483      * object into a block of Roo.data.Records.
14484      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14485      * The function must be passed <ul>
14486      * <li>The Record block object</li>
14487      * <li>The "arg" argument from the load function</li>
14488      * <li>A boolean success indicator</li>
14489      * </ul>
14490      * @param {Object} scope The scope in which to call the callback
14491      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14492      */
14493     load : function(params, reader, callback, scope, arg){
14494         if(this.fireEvent("beforeload", this, params) !== false){
14495
14496             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14497
14498             var url = this.url;
14499             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14500             if(this.nocache){
14501                 url += "&_dc=" + (new Date().getTime());
14502             }
14503             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14504             var trans = {
14505                 id : transId,
14506                 cb : "stcCallback"+transId,
14507                 scriptId : "stcScript"+transId,
14508                 params : params,
14509                 arg : arg,
14510                 url : url,
14511                 callback : callback,
14512                 scope : scope,
14513                 reader : reader
14514             };
14515             var conn = this;
14516
14517             window[trans.cb] = function(o){
14518                 conn.handleResponse(o, trans);
14519             };
14520
14521             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14522
14523             if(this.autoAbort !== false){
14524                 this.abort();
14525             }
14526
14527             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14528
14529             var script = document.createElement("script");
14530             script.setAttribute("src", url);
14531             script.setAttribute("type", "text/javascript");
14532             script.setAttribute("id", trans.scriptId);
14533             this.head.appendChild(script);
14534
14535             this.trans = trans;
14536         }else{
14537             callback.call(scope||this, null, arg, false);
14538         }
14539     },
14540
14541     // private
14542     isLoading : function(){
14543         return this.trans ? true : false;
14544     },
14545
14546     /**
14547      * Abort the current server request.
14548      */
14549     abort : function(){
14550         if(this.isLoading()){
14551             this.destroyTrans(this.trans);
14552         }
14553     },
14554
14555     // private
14556     destroyTrans : function(trans, isLoaded){
14557         this.head.removeChild(document.getElementById(trans.scriptId));
14558         clearTimeout(trans.timeoutId);
14559         if(isLoaded){
14560             window[trans.cb] = undefined;
14561             try{
14562                 delete window[trans.cb];
14563             }catch(e){}
14564         }else{
14565             // if hasn't been loaded, wait for load to remove it to prevent script error
14566             window[trans.cb] = function(){
14567                 window[trans.cb] = undefined;
14568                 try{
14569                     delete window[trans.cb];
14570                 }catch(e){}
14571             };
14572         }
14573     },
14574
14575     // private
14576     handleResponse : function(o, trans){
14577         this.trans = false;
14578         this.destroyTrans(trans, true);
14579         var result;
14580         try {
14581             result = trans.reader.readRecords(o);
14582         }catch(e){
14583             this.fireEvent("loadexception", this, o, trans.arg, e);
14584             trans.callback.call(trans.scope||window, null, trans.arg, false);
14585             return;
14586         }
14587         this.fireEvent("load", this, o, trans.arg);
14588         trans.callback.call(trans.scope||window, result, trans.arg, true);
14589     },
14590
14591     // private
14592     handleFailure : function(trans){
14593         this.trans = false;
14594         this.destroyTrans(trans, false);
14595         this.fireEvent("loadexception", this, null, trans.arg);
14596         trans.callback.call(trans.scope||window, null, trans.arg, false);
14597     }
14598 });/*
14599  * Based on:
14600  * Ext JS Library 1.1.1
14601  * Copyright(c) 2006-2007, Ext JS, LLC.
14602  *
14603  * Originally Released Under LGPL - original licence link has changed is not relivant.
14604  *
14605  * Fork - LGPL
14606  * <script type="text/javascript">
14607  */
14608
14609 /**
14610  * @class Roo.data.JsonReader
14611  * @extends Roo.data.DataReader
14612  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14613  * based on mappings in a provided Roo.data.Record constructor.
14614  * 
14615  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14616  * in the reply previously. 
14617  * 
14618  * <p>
14619  * Example code:
14620  * <pre><code>
14621 var RecordDef = Roo.data.Record.create([
14622     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14623     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14624 ]);
14625 var myReader = new Roo.data.JsonReader({
14626     totalProperty: "results",    // The property which contains the total dataset size (optional)
14627     root: "rows",                // The property which contains an Array of row objects
14628     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14629 }, RecordDef);
14630 </code></pre>
14631  * <p>
14632  * This would consume a JSON file like this:
14633  * <pre><code>
14634 { 'results': 2, 'rows': [
14635     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14636     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14637 }
14638 </code></pre>
14639  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14640  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14641  * paged from the remote server.
14642  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14643  * @cfg {String} root name of the property which contains the Array of row objects.
14644  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14645  * @cfg {Array} fields Array of field definition objects
14646  * @constructor
14647  * Create a new JsonReader
14648  * @param {Object} meta Metadata configuration options
14649  * @param {Object} recordType Either an Array of field definition objects,
14650  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14651  */
14652 Roo.data.JsonReader = function(meta, recordType){
14653     
14654     meta = meta || {};
14655     // set some defaults:
14656     Roo.applyIf(meta, {
14657         totalProperty: 'total',
14658         successProperty : 'success',
14659         root : 'data',
14660         id : 'id'
14661     });
14662     
14663     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14664 };
14665 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14666     
14667     readerType : 'Json',
14668     
14669     /**
14670      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14671      * Used by Store query builder to append _requestMeta to params.
14672      * 
14673      */
14674     metaFromRemote : false,
14675     /**
14676      * This method is only used by a DataProxy which has retrieved data from a remote server.
14677      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14678      * @return {Object} data A data block which is used by an Roo.data.Store object as
14679      * a cache of Roo.data.Records.
14680      */
14681     read : function(response){
14682         var json = response.responseText;
14683        
14684         var o = /* eval:var:o */ eval("("+json+")");
14685         if(!o) {
14686             throw {message: "JsonReader.read: Json object not found"};
14687         }
14688         
14689         if(o.metaData){
14690             
14691             delete this.ef;
14692             this.metaFromRemote = true;
14693             this.meta = o.metaData;
14694             this.recordType = Roo.data.Record.create(o.metaData.fields);
14695             this.onMetaChange(this.meta, this.recordType, o);
14696         }
14697         return this.readRecords(o);
14698     },
14699
14700     // private function a store will implement
14701     onMetaChange : function(meta, recordType, o){
14702
14703     },
14704
14705     /**
14706          * @ignore
14707          */
14708     simpleAccess: function(obj, subsc) {
14709         return obj[subsc];
14710     },
14711
14712         /**
14713          * @ignore
14714          */
14715     getJsonAccessor: function(){
14716         var re = /[\[\.]/;
14717         return function(expr) {
14718             try {
14719                 return(re.test(expr))
14720                     ? new Function("obj", "return obj." + expr)
14721                     : function(obj){
14722                         return obj[expr];
14723                     };
14724             } catch(e){}
14725             return Roo.emptyFn;
14726         };
14727     }(),
14728
14729     /**
14730      * Create a data block containing Roo.data.Records from an XML document.
14731      * @param {Object} o An object which contains an Array of row objects in the property specified
14732      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14733      * which contains the total size of the dataset.
14734      * @return {Object} data A data block which is used by an Roo.data.Store object as
14735      * a cache of Roo.data.Records.
14736      */
14737     readRecords : function(o){
14738         /**
14739          * After any data loads, the raw JSON data is available for further custom processing.
14740          * @type Object
14741          */
14742         this.o = o;
14743         var s = this.meta, Record = this.recordType,
14744             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14745
14746 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14747         if (!this.ef) {
14748             if(s.totalProperty) {
14749                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14750                 }
14751                 if(s.successProperty) {
14752                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14753                 }
14754                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14755                 if (s.id) {
14756                         var g = this.getJsonAccessor(s.id);
14757                         this.getId = function(rec) {
14758                                 var r = g(rec);  
14759                                 return (r === undefined || r === "") ? null : r;
14760                         };
14761                 } else {
14762                         this.getId = function(){return null;};
14763                 }
14764             this.ef = [];
14765             for(var jj = 0; jj < fl; jj++){
14766                 f = fi[jj];
14767                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14768                 this.ef[jj] = this.getJsonAccessor(map);
14769             }
14770         }
14771
14772         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14773         if(s.totalProperty){
14774             var vt = parseInt(this.getTotal(o), 10);
14775             if(!isNaN(vt)){
14776                 totalRecords = vt;
14777             }
14778         }
14779         if(s.successProperty){
14780             var vs = this.getSuccess(o);
14781             if(vs === false || vs === 'false'){
14782                 success = false;
14783             }
14784         }
14785         var records = [];
14786         for(var i = 0; i < c; i++){
14787                 var n = root[i];
14788             var values = {};
14789             var id = this.getId(n);
14790             for(var j = 0; j < fl; j++){
14791                 f = fi[j];
14792             var v = this.ef[j](n);
14793             if (!f.convert) {
14794                 Roo.log('missing convert for ' + f.name);
14795                 Roo.log(f);
14796                 continue;
14797             }
14798             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14799             }
14800             var record = new Record(values, id);
14801             record.json = n;
14802             records[i] = record;
14803         }
14804         return {
14805             raw : o,
14806             success : success,
14807             records : records,
14808             totalRecords : totalRecords
14809         };
14810     },
14811     // used when loading children.. @see loadDataFromChildren
14812     toLoadData: function(rec)
14813     {
14814         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14815         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14816         return { data : data, total : data.length };
14817         
14818     }
14819 });/*
14820  * Based on:
14821  * Ext JS Library 1.1.1
14822  * Copyright(c) 2006-2007, Ext JS, LLC.
14823  *
14824  * Originally Released Under LGPL - original licence link has changed is not relivant.
14825  *
14826  * Fork - LGPL
14827  * <script type="text/javascript">
14828  */
14829
14830 /**
14831  * @class Roo.data.ArrayReader
14832  * @extends Roo.data.DataReader
14833  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14834  * Each element of that Array represents a row of data fields. The
14835  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14836  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14837  * <p>
14838  * Example code:.
14839  * <pre><code>
14840 var RecordDef = Roo.data.Record.create([
14841     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14842     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14843 ]);
14844 var myReader = new Roo.data.ArrayReader({
14845     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14846 }, RecordDef);
14847 </code></pre>
14848  * <p>
14849  * This would consume an Array like this:
14850  * <pre><code>
14851 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14852   </code></pre>
14853  
14854  * @constructor
14855  * Create a new JsonReader
14856  * @param {Object} meta Metadata configuration options.
14857  * @param {Object|Array} recordType Either an Array of field definition objects
14858  * 
14859  * @cfg {Array} fields Array of field definition objects
14860  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14861  * as specified to {@link Roo.data.Record#create},
14862  * or an {@link Roo.data.Record} object
14863  *
14864  * 
14865  * created using {@link Roo.data.Record#create}.
14866  */
14867 Roo.data.ArrayReader = function(meta, recordType)
14868 {    
14869     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14870 };
14871
14872 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14873     
14874       /**
14875      * Create a data block containing Roo.data.Records from an XML document.
14876      * @param {Object} o An Array of row objects which represents the dataset.
14877      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14878      * a cache of Roo.data.Records.
14879      */
14880     readRecords : function(o)
14881     {
14882         var sid = this.meta ? this.meta.id : null;
14883         var recordType = this.recordType, fields = recordType.prototype.fields;
14884         var records = [];
14885         var root = o;
14886         for(var i = 0; i < root.length; i++){
14887                 var n = root[i];
14888             var values = {};
14889             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14890             for(var j = 0, jlen = fields.length; j < jlen; j++){
14891                 var f = fields.items[j];
14892                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14893                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14894                 v = f.convert(v);
14895                 values[f.name] = v;
14896             }
14897             var record = new recordType(values, id);
14898             record.json = n;
14899             records[records.length] = record;
14900         }
14901         return {
14902             records : records,
14903             totalRecords : records.length
14904         };
14905     },
14906     // used when loading children.. @see loadDataFromChildren
14907     toLoadData: function(rec)
14908     {
14909         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14910         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14911         
14912     }
14913     
14914     
14915 });/*
14916  * - LGPL
14917  * * 
14918  */
14919
14920 /**
14921  * @class Roo.bootstrap.ComboBox
14922  * @extends Roo.bootstrap.TriggerField
14923  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14924  * @cfg {Boolean} append (true|false) default false
14925  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14926  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14927  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14928  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14929  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14930  * @cfg {Boolean} animate default true
14931  * @cfg {Boolean} emptyResultText only for touch device
14932  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14933  * @cfg {String} emptyTitle default ''
14934  * @cfg {Number} width fixed with? experimental
14935  * @constructor
14936  * Create a new ComboBox.
14937  * @param {Object} config Configuration options
14938  */
14939 Roo.bootstrap.ComboBox = function(config){
14940     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14941     this.addEvents({
14942         /**
14943          * @event expand
14944          * Fires when the dropdown list is expanded
14945         * @param {Roo.bootstrap.ComboBox} combo This combo box
14946         */
14947         'expand' : true,
14948         /**
14949          * @event collapse
14950          * Fires when the dropdown list is collapsed
14951         * @param {Roo.bootstrap.ComboBox} combo This combo box
14952         */
14953         'collapse' : true,
14954         /**
14955          * @event beforeselect
14956          * Fires before a list item is selected. Return false to cancel the selection.
14957         * @param {Roo.bootstrap.ComboBox} combo This combo box
14958         * @param {Roo.data.Record} record The data record returned from the underlying store
14959         * @param {Number} index The index of the selected item in the dropdown list
14960         */
14961         'beforeselect' : true,
14962         /**
14963          * @event select
14964          * Fires when a list item is selected
14965         * @param {Roo.bootstrap.ComboBox} combo This combo box
14966         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14967         * @param {Number} index The index of the selected item in the dropdown list
14968         */
14969         'select' : true,
14970         /**
14971          * @event beforequery
14972          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14973          * The event object passed has these properties:
14974         * @param {Roo.bootstrap.ComboBox} combo This combo box
14975         * @param {String} query The query
14976         * @param {Boolean} forceAll true to force "all" query
14977         * @param {Boolean} cancel true to cancel the query
14978         * @param {Object} e The query event object
14979         */
14980         'beforequery': true,
14981          /**
14982          * @event add
14983          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14984         * @param {Roo.bootstrap.ComboBox} combo This combo box
14985         */
14986         'add' : true,
14987         /**
14988          * @event edit
14989          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14990         * @param {Roo.bootstrap.ComboBox} combo This combo box
14991         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14992         */
14993         'edit' : true,
14994         /**
14995          * @event remove
14996          * Fires when the remove value from the combobox array
14997         * @param {Roo.bootstrap.ComboBox} combo This combo box
14998         */
14999         'remove' : true,
15000         /**
15001          * @event afterremove
15002          * Fires when the remove value from the combobox array
15003         * @param {Roo.bootstrap.ComboBox} combo This combo box
15004         */
15005         'afterremove' : true,
15006         /**
15007          * @event specialfilter
15008          * Fires when specialfilter
15009             * @param {Roo.bootstrap.ComboBox} combo This combo box
15010             */
15011         'specialfilter' : true,
15012         /**
15013          * @event tick
15014          * Fires when tick the element
15015             * @param {Roo.bootstrap.ComboBox} combo This combo box
15016             */
15017         'tick' : true,
15018         /**
15019          * @event touchviewdisplay
15020          * Fires when touch view require special display (default is using displayField)
15021             * @param {Roo.bootstrap.ComboBox} combo This combo box
15022             * @param {Object} cfg set html .
15023             */
15024         'touchviewdisplay' : true
15025         
15026     });
15027     
15028     this.item = [];
15029     this.tickItems = [];
15030     
15031     this.selectedIndex = -1;
15032     if(this.mode == 'local'){
15033         if(config.queryDelay === undefined){
15034             this.queryDelay = 10;
15035         }
15036         if(config.minChars === undefined){
15037             this.minChars = 0;
15038         }
15039     }
15040 };
15041
15042 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15043      
15044     /**
15045      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15046      * rendering into an Roo.Editor, defaults to false)
15047      */
15048     /**
15049      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15050      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15051      */
15052     /**
15053      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15054      */
15055     /**
15056      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15057      * the dropdown list (defaults to undefined, with no header element)
15058      */
15059
15060      /**
15061      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15062      */
15063      
15064      /**
15065      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15066      */
15067     listWidth: undefined,
15068     /**
15069      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15070      * mode = 'remote' or 'text' if mode = 'local')
15071      */
15072     displayField: undefined,
15073     
15074     /**
15075      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15076      * mode = 'remote' or 'value' if mode = 'local'). 
15077      * Note: use of a valueField requires the user make a selection
15078      * in order for a value to be mapped.
15079      */
15080     valueField: undefined,
15081     /**
15082      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15083      */
15084     modalTitle : '',
15085     
15086     /**
15087      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15088      * field's data value (defaults to the underlying DOM element's name)
15089      */
15090     hiddenName: undefined,
15091     /**
15092      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15093      */
15094     listClass: '',
15095     /**
15096      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15097      */
15098     selectedClass: 'active',
15099     
15100     /**
15101      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15102      */
15103     shadow:'sides',
15104     /**
15105      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15106      * anchor positions (defaults to 'tl-bl')
15107      */
15108     listAlign: 'tl-bl?',
15109     /**
15110      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15111      */
15112     maxHeight: 300,
15113     /**
15114      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15115      * query specified by the allQuery config option (defaults to 'query')
15116      */
15117     triggerAction: 'query',
15118     /**
15119      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15120      * (defaults to 4, does not apply if editable = false)
15121      */
15122     minChars : 4,
15123     /**
15124      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15125      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15126      */
15127     typeAhead: false,
15128     /**
15129      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15130      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15131      */
15132     queryDelay: 500,
15133     /**
15134      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15135      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15136      */
15137     pageSize: 0,
15138     /**
15139      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15140      * when editable = true (defaults to false)
15141      */
15142     selectOnFocus:false,
15143     /**
15144      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15145      */
15146     queryParam: 'query',
15147     /**
15148      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15149      * when mode = 'remote' (defaults to 'Loading...')
15150      */
15151     loadingText: 'Loading...',
15152     /**
15153      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15154      */
15155     resizable: false,
15156     /**
15157      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15158      */
15159     handleHeight : 8,
15160     /**
15161      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15162      * traditional select (defaults to true)
15163      */
15164     editable: true,
15165     /**
15166      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15167      */
15168     allQuery: '',
15169     /**
15170      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15171      */
15172     mode: 'remote',
15173     /**
15174      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15175      * listWidth has a higher value)
15176      */
15177     minListWidth : 70,
15178     /**
15179      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15180      * allow the user to set arbitrary text into the field (defaults to false)
15181      */
15182     forceSelection:false,
15183     /**
15184      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15185      * if typeAhead = true (defaults to 250)
15186      */
15187     typeAheadDelay : 250,
15188     /**
15189      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15190      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15191      */
15192     valueNotFoundText : undefined,
15193     /**
15194      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15195      */
15196     blockFocus : false,
15197     
15198     /**
15199      * @cfg {Boolean} disableClear Disable showing of clear button.
15200      */
15201     disableClear : false,
15202     /**
15203      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15204      */
15205     alwaysQuery : false,
15206     
15207     /**
15208      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15209      */
15210     multiple : false,
15211     
15212     /**
15213      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15214      */
15215     invalidClass : "has-warning",
15216     
15217     /**
15218      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15219      */
15220     validClass : "has-success",
15221     
15222     /**
15223      * @cfg {Boolean} specialFilter (true|false) special filter default false
15224      */
15225     specialFilter : false,
15226     
15227     /**
15228      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15229      */
15230     mobileTouchView : true,
15231     
15232     /**
15233      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15234      */
15235     useNativeIOS : false,
15236     
15237     /**
15238      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15239      */
15240     mobile_restrict_height : false,
15241     
15242     ios_options : false,
15243     
15244     //private
15245     addicon : false,
15246     editicon: false,
15247     
15248     page: 0,
15249     hasQuery: false,
15250     append: false,
15251     loadNext: false,
15252     autoFocus : true,
15253     tickable : false,
15254     btnPosition : 'right',
15255     triggerList : true,
15256     showToggleBtn : true,
15257     animate : true,
15258     emptyResultText: 'Empty',
15259     triggerText : 'Select',
15260     emptyTitle : '',
15261     width : false,
15262     
15263     // element that contains real text value.. (when hidden is used..)
15264     
15265     getAutoCreate : function()
15266     {   
15267         var cfg = false;
15268         //render
15269         /*
15270          * Render classic select for iso
15271          */
15272         
15273         if(Roo.isIOS && this.useNativeIOS){
15274             cfg = this.getAutoCreateNativeIOS();
15275             return cfg;
15276         }
15277         
15278         /*
15279          * Touch Devices
15280          */
15281         
15282         if(Roo.isTouch && this.mobileTouchView){
15283             cfg = this.getAutoCreateTouchView();
15284             return cfg;;
15285         }
15286         
15287         /*
15288          *  Normal ComboBox
15289          */
15290         if(!this.tickable){
15291             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15292             return cfg;
15293         }
15294         
15295         /*
15296          *  ComboBox with tickable selections
15297          */
15298              
15299         var align = this.labelAlign || this.parentLabelAlign();
15300         
15301         cfg = {
15302             cls : 'form-group roo-combobox-tickable' //input-group
15303         };
15304         
15305         var btn_text_select = '';
15306         var btn_text_done = '';
15307         var btn_text_cancel = '';
15308         
15309         if (this.btn_text_show) {
15310             btn_text_select = 'Select';
15311             btn_text_done = 'Done';
15312             btn_text_cancel = 'Cancel'; 
15313         }
15314         
15315         var buttons = {
15316             tag : 'div',
15317             cls : 'tickable-buttons',
15318             cn : [
15319                 {
15320                     tag : 'button',
15321                     type : 'button',
15322                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15323                     //html : this.triggerText
15324                     html: btn_text_select
15325                 },
15326                 {
15327                     tag : 'button',
15328                     type : 'button',
15329                     name : 'ok',
15330                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15331                     //html : 'Done'
15332                     html: btn_text_done
15333                 },
15334                 {
15335                     tag : 'button',
15336                     type : 'button',
15337                     name : 'cancel',
15338                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15339                     //html : 'Cancel'
15340                     html: btn_text_cancel
15341                 }
15342             ]
15343         };
15344         
15345         if(this.editable){
15346             buttons.cn.unshift({
15347                 tag: 'input',
15348                 cls: 'roo-select2-search-field-input'
15349             });
15350         }
15351         
15352         var _this = this;
15353         
15354         Roo.each(buttons.cn, function(c){
15355             if (_this.size) {
15356                 c.cls += ' btn-' + _this.size;
15357             }
15358
15359             if (_this.disabled) {
15360                 c.disabled = true;
15361             }
15362         });
15363         
15364         var box = {
15365             tag: 'div',
15366             style : 'display: contents',
15367             cn: [
15368                 {
15369                     tag: 'input',
15370                     type : 'hidden',
15371                     cls: 'form-hidden-field'
15372                 },
15373                 {
15374                     tag: 'ul',
15375                     cls: 'roo-select2-choices',
15376                     cn:[
15377                         {
15378                             tag: 'li',
15379                             cls: 'roo-select2-search-field',
15380                             cn: [
15381                                 buttons
15382                             ]
15383                         }
15384                     ]
15385                 }
15386             ]
15387         };
15388         
15389         var combobox = {
15390             cls: 'roo-select2-container input-group roo-select2-container-multi',
15391             cn: [
15392                 
15393                 box
15394 //                {
15395 //                    tag: 'ul',
15396 //                    cls: 'typeahead typeahead-long dropdown-menu',
15397 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15398 //                }
15399             ]
15400         };
15401         
15402         if(this.hasFeedback && !this.allowBlank){
15403             
15404             var feedback = {
15405                 tag: 'span',
15406                 cls: 'glyphicon form-control-feedback'
15407             };
15408
15409             combobox.cn.push(feedback);
15410         }
15411         
15412         
15413         
15414         var indicator = {
15415             tag : 'i',
15416             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15417             tooltip : 'This field is required'
15418         };
15419         if (Roo.bootstrap.version == 4) {
15420             indicator = {
15421                 tag : 'i',
15422                 style : 'display:none'
15423             };
15424         }
15425         if (align ==='left' && this.fieldLabel.length) {
15426             
15427             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15428             
15429             cfg.cn = [
15430                 indicator,
15431                 {
15432                     tag: 'label',
15433                     'for' :  id,
15434                     cls : 'control-label col-form-label',
15435                     html : this.fieldLabel
15436
15437                 },
15438                 {
15439                     cls : "", 
15440                     cn: [
15441                         combobox
15442                     ]
15443                 }
15444
15445             ];
15446             
15447             var labelCfg = cfg.cn[1];
15448             var contentCfg = cfg.cn[2];
15449             
15450
15451             if(this.indicatorpos == 'right'){
15452                 
15453                 cfg.cn = [
15454                     {
15455                         tag: 'label',
15456                         'for' :  id,
15457                         cls : 'control-label col-form-label',
15458                         cn : [
15459                             {
15460                                 tag : 'span',
15461                                 html : this.fieldLabel
15462                             },
15463                             indicator
15464                         ]
15465                     },
15466                     {
15467                         cls : "",
15468                         cn: [
15469                             combobox
15470                         ]
15471                     }
15472
15473                 ];
15474                 
15475                 
15476                 
15477                 labelCfg = cfg.cn[0];
15478                 contentCfg = cfg.cn[1];
15479             
15480             }
15481             
15482             if(this.labelWidth > 12){
15483                 labelCfg.style = "width: " + this.labelWidth + 'px';
15484             }
15485             if(this.width * 1 > 0){
15486                 contentCfg.style = "width: " + this.width + 'px';
15487             }
15488             if(this.labelWidth < 13 && this.labelmd == 0){
15489                 this.labelmd = this.labelWidth;
15490             }
15491             
15492             if(this.labellg > 0){
15493                 labelCfg.cls += ' col-lg-' + this.labellg;
15494                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15495             }
15496             
15497             if(this.labelmd > 0){
15498                 labelCfg.cls += ' col-md-' + this.labelmd;
15499                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15500             }
15501             
15502             if(this.labelsm > 0){
15503                 labelCfg.cls += ' col-sm-' + this.labelsm;
15504                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15505             }
15506             
15507             if(this.labelxs > 0){
15508                 labelCfg.cls += ' col-xs-' + this.labelxs;
15509                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15510             }
15511                 
15512                 
15513         } else if ( this.fieldLabel.length) {
15514 //                Roo.log(" label");
15515                  cfg.cn = [
15516                    indicator,
15517                     {
15518                         tag: 'label',
15519                         //cls : 'input-group-addon',
15520                         html : this.fieldLabel
15521                     },
15522                     combobox
15523                 ];
15524                 
15525                 if(this.indicatorpos == 'right'){
15526                     cfg.cn = [
15527                         {
15528                             tag: 'label',
15529                             //cls : 'input-group-addon',
15530                             html : this.fieldLabel
15531                         },
15532                         indicator,
15533                         combobox
15534                     ];
15535                     
15536                 }
15537
15538         } else {
15539             
15540 //                Roo.log(" no label && no align");
15541                 cfg = combobox
15542                      
15543                 
15544         }
15545          
15546         var settings=this;
15547         ['xs','sm','md','lg'].map(function(size){
15548             if (settings[size]) {
15549                 cfg.cls += ' col-' + size + '-' + settings[size];
15550             }
15551         });
15552         
15553         return cfg;
15554         
15555     },
15556     
15557     _initEventsCalled : false,
15558     
15559     // private
15560     initEvents: function()
15561     {   
15562         if (this._initEventsCalled) { // as we call render... prevent looping...
15563             return;
15564         }
15565         this._initEventsCalled = true;
15566         
15567         if (!this.store) {
15568             throw "can not find store for combo";
15569         }
15570         
15571         this.indicator = this.indicatorEl();
15572         
15573         this.store = Roo.factory(this.store, Roo.data);
15574         this.store.parent = this;
15575         
15576         // if we are building from html. then this element is so complex, that we can not really
15577         // use the rendered HTML.
15578         // so we have to trash and replace the previous code.
15579         if (Roo.XComponent.build_from_html) {
15580             // remove this element....
15581             var e = this.el.dom, k=0;
15582             while (e ) { e = e.previousSibling;  ++k;}
15583
15584             this.el.remove();
15585             
15586             this.el=false;
15587             this.rendered = false;
15588             
15589             this.render(this.parent().getChildContainer(true), k);
15590         }
15591         
15592         if(Roo.isIOS && this.useNativeIOS){
15593             this.initIOSView();
15594             return;
15595         }
15596         
15597         /*
15598          * Touch Devices
15599          */
15600         
15601         if(Roo.isTouch && this.mobileTouchView){
15602             this.initTouchView();
15603             return;
15604         }
15605         
15606         if(this.tickable){
15607             this.initTickableEvents();
15608             return;
15609         }
15610         
15611         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15612         
15613         if(this.hiddenName){
15614             
15615             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15616             
15617             this.hiddenField.dom.value =
15618                 this.hiddenValue !== undefined ? this.hiddenValue :
15619                 this.value !== undefined ? this.value : '';
15620
15621             // prevent input submission
15622             this.el.dom.removeAttribute('name');
15623             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15624              
15625              
15626         }
15627         //if(Roo.isGecko){
15628         //    this.el.dom.setAttribute('autocomplete', 'off');
15629         //}
15630         
15631         var cls = 'x-combo-list';
15632         
15633         //this.list = new Roo.Layer({
15634         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15635         //});
15636         
15637         var _this = this;
15638         
15639         (function(){
15640             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15641             _this.list.setWidth(lw);
15642         }).defer(100);
15643         
15644         this.list.on('mouseover', this.onViewOver, this);
15645         this.list.on('mousemove', this.onViewMove, this);
15646         this.list.on('scroll', this.onViewScroll, this);
15647         
15648         /*
15649         this.list.swallowEvent('mousewheel');
15650         this.assetHeight = 0;
15651
15652         if(this.title){
15653             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15654             this.assetHeight += this.header.getHeight();
15655         }
15656
15657         this.innerList = this.list.createChild({cls:cls+'-inner'});
15658         this.innerList.on('mouseover', this.onViewOver, this);
15659         this.innerList.on('mousemove', this.onViewMove, this);
15660         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15661         
15662         if(this.allowBlank && !this.pageSize && !this.disableClear){
15663             this.footer = this.list.createChild({cls:cls+'-ft'});
15664             this.pageTb = new Roo.Toolbar(this.footer);
15665            
15666         }
15667         if(this.pageSize){
15668             this.footer = this.list.createChild({cls:cls+'-ft'});
15669             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15670                     {pageSize: this.pageSize});
15671             
15672         }
15673         
15674         if (this.pageTb && this.allowBlank && !this.disableClear) {
15675             var _this = this;
15676             this.pageTb.add(new Roo.Toolbar.Fill(), {
15677                 cls: 'x-btn-icon x-btn-clear',
15678                 text: '&#160;',
15679                 handler: function()
15680                 {
15681                     _this.collapse();
15682                     _this.clearValue();
15683                     _this.onSelect(false, -1);
15684                 }
15685             });
15686         }
15687         if (this.footer) {
15688             this.assetHeight += this.footer.getHeight();
15689         }
15690         */
15691             
15692         if(!this.tpl){
15693             this.tpl = Roo.bootstrap.version == 4 ?
15694                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15695                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15696         }
15697
15698         this.view = new Roo.View(this.list, this.tpl, {
15699             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15700         });
15701         //this.view.wrapEl.setDisplayed(false);
15702         this.view.on('click', this.onViewClick, this);
15703         
15704         
15705         this.store.on('beforeload', this.onBeforeLoad, this);
15706         this.store.on('load', this.onLoad, this);
15707         this.store.on('loadexception', this.onLoadException, this);
15708         /*
15709         if(this.resizable){
15710             this.resizer = new Roo.Resizable(this.list,  {
15711                pinned:true, handles:'se'
15712             });
15713             this.resizer.on('resize', function(r, w, h){
15714                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15715                 this.listWidth = w;
15716                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15717                 this.restrictHeight();
15718             }, this);
15719             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15720         }
15721         */
15722         if(!this.editable){
15723             this.editable = true;
15724             this.setEditable(false);
15725         }
15726         
15727         /*
15728         
15729         if (typeof(this.events.add.listeners) != 'undefined') {
15730             
15731             this.addicon = this.wrap.createChild(
15732                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15733        
15734             this.addicon.on('click', function(e) {
15735                 this.fireEvent('add', this);
15736             }, this);
15737         }
15738         if (typeof(this.events.edit.listeners) != 'undefined') {
15739             
15740             this.editicon = this.wrap.createChild(
15741                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15742             if (this.addicon) {
15743                 this.editicon.setStyle('margin-left', '40px');
15744             }
15745             this.editicon.on('click', function(e) {
15746                 
15747                 // we fire even  if inothing is selected..
15748                 this.fireEvent('edit', this, this.lastData );
15749                 
15750             }, this);
15751         }
15752         */
15753         
15754         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15755             "up" : function(e){
15756                 this.inKeyMode = true;
15757                 this.selectPrev();
15758             },
15759
15760             "down" : function(e){
15761                 if(!this.isExpanded()){
15762                     this.onTriggerClick();
15763                 }else{
15764                     this.inKeyMode = true;
15765                     this.selectNext();
15766                 }
15767             },
15768
15769             "enter" : function(e){
15770 //                this.onViewClick();
15771                 //return true;
15772                 this.collapse();
15773                 
15774                 if(this.fireEvent("specialkey", this, e)){
15775                     this.onViewClick(false);
15776                 }
15777                 
15778                 return true;
15779             },
15780
15781             "esc" : function(e){
15782                 this.collapse();
15783             },
15784
15785             "tab" : function(e){
15786                 this.collapse();
15787                 
15788                 if(this.fireEvent("specialkey", this, e)){
15789                     this.onViewClick(false);
15790                 }
15791                 
15792                 return true;
15793             },
15794
15795             scope : this,
15796
15797             doRelay : function(foo, bar, hname){
15798                 if(hname == 'down' || this.scope.isExpanded()){
15799                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15800                 }
15801                 return true;
15802             },
15803
15804             forceKeyDown: true
15805         });
15806         
15807         
15808         this.queryDelay = Math.max(this.queryDelay || 10,
15809                 this.mode == 'local' ? 10 : 250);
15810         
15811         
15812         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15813         
15814         if(this.typeAhead){
15815             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15816         }
15817         if(this.editable !== false){
15818             this.inputEl().on("keyup", this.onKeyUp, this);
15819         }
15820         if(this.forceSelection){
15821             this.inputEl().on('blur', this.doForce, this);
15822         }
15823         
15824         if(this.multiple){
15825             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827         }
15828     },
15829     
15830     initTickableEvents: function()
15831     {   
15832         this.createList();
15833         
15834         if(this.hiddenName){
15835             
15836             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15837             
15838             this.hiddenField.dom.value =
15839                 this.hiddenValue !== undefined ? this.hiddenValue :
15840                 this.value !== undefined ? this.value : '';
15841
15842             // prevent input submission
15843             this.el.dom.removeAttribute('name');
15844             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15845              
15846              
15847         }
15848         
15849 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15850         
15851         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15852         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15853         if(this.triggerList){
15854             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15855         }
15856          
15857         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15858         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15859         
15860         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15861         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15862         
15863         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15864         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15865         
15866         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15867         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15868         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15869         
15870         this.okBtn.hide();
15871         this.cancelBtn.hide();
15872         
15873         var _this = this;
15874         
15875         (function(){
15876             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15877             _this.list.setWidth(lw);
15878         }).defer(100);
15879         
15880         this.list.on('mouseover', this.onViewOver, this);
15881         this.list.on('mousemove', this.onViewMove, this);
15882         
15883         this.list.on('scroll', this.onViewScroll, this);
15884         
15885         if(!this.tpl){
15886             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15887                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15888         }
15889
15890         this.view = new Roo.View(this.list, this.tpl, {
15891             singleSelect:true,
15892             tickable:true,
15893             parent:this,
15894             store: this.store,
15895             selectedClass: this.selectedClass
15896         });
15897         
15898         //this.view.wrapEl.setDisplayed(false);
15899         this.view.on('click', this.onViewClick, this);
15900         
15901         
15902         
15903         this.store.on('beforeload', this.onBeforeLoad, this);
15904         this.store.on('load', this.onLoad, this);
15905         this.store.on('loadexception', this.onLoadException, this);
15906         
15907         if(this.editable){
15908             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15909                 "up" : function(e){
15910                     this.inKeyMode = true;
15911                     this.selectPrev();
15912                 },
15913
15914                 "down" : function(e){
15915                     this.inKeyMode = true;
15916                     this.selectNext();
15917                 },
15918
15919                 "enter" : function(e){
15920                     if(this.fireEvent("specialkey", this, e)){
15921                         this.onViewClick(false);
15922                     }
15923                     
15924                     return true;
15925                 },
15926
15927                 "esc" : function(e){
15928                     this.onTickableFooterButtonClick(e, false, false);
15929                 },
15930
15931                 "tab" : function(e){
15932                     this.fireEvent("specialkey", this, e);
15933                     
15934                     this.onTickableFooterButtonClick(e, false, false);
15935                     
15936                     return true;
15937                 },
15938
15939                 scope : this,
15940
15941                 doRelay : function(e, fn, key){
15942                     if(this.scope.isExpanded()){
15943                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15944                     }
15945                     return true;
15946                 },
15947
15948                 forceKeyDown: true
15949             });
15950         }
15951         
15952         this.queryDelay = Math.max(this.queryDelay || 10,
15953                 this.mode == 'local' ? 10 : 250);
15954         
15955         
15956         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15957         
15958         if(this.typeAhead){
15959             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15960         }
15961         
15962         if(this.editable !== false){
15963             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15964         }
15965         
15966         this.indicator = this.indicatorEl();
15967         
15968         if(this.indicator){
15969             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15970             this.indicator.hide();
15971         }
15972         
15973     },
15974
15975     onDestroy : function(){
15976         if(this.view){
15977             this.view.setStore(null);
15978             this.view.el.removeAllListeners();
15979             this.view.el.remove();
15980             this.view.purgeListeners();
15981         }
15982         if(this.list){
15983             this.list.dom.innerHTML  = '';
15984         }
15985         
15986         if(this.store){
15987             this.store.un('beforeload', this.onBeforeLoad, this);
15988             this.store.un('load', this.onLoad, this);
15989             this.store.un('loadexception', this.onLoadException, this);
15990         }
15991         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15992     },
15993
15994     // private
15995     fireKey : function(e){
15996         if(e.isNavKeyPress() && !this.list.isVisible()){
15997             this.fireEvent("specialkey", this, e);
15998         }
15999     },
16000
16001     // private
16002     onResize: function(w, h)
16003     {
16004         
16005         
16006 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16007 //        
16008 //        if(typeof w != 'number'){
16009 //            // we do not handle it!?!?
16010 //            return;
16011 //        }
16012 //        var tw = this.trigger.getWidth();
16013 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16014 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16015 //        var x = w - tw;
16016 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16017 //            
16018 //        //this.trigger.setStyle('left', x+'px');
16019 //        
16020 //        if(this.list && this.listWidth === undefined){
16021 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16022 //            this.list.setWidth(lw);
16023 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16024 //        }
16025         
16026     
16027         
16028     },
16029
16030     /**
16031      * Allow or prevent the user from directly editing the field text.  If false is passed,
16032      * the user will only be able to select from the items defined in the dropdown list.  This method
16033      * is the runtime equivalent of setting the 'editable' config option at config time.
16034      * @param {Boolean} value True to allow the user to directly edit the field text
16035      */
16036     setEditable : function(value){
16037         if(value == this.editable){
16038             return;
16039         }
16040         this.editable = value;
16041         if(!value){
16042             this.inputEl().dom.setAttribute('readOnly', true);
16043             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16044             this.inputEl().addClass('x-combo-noedit');
16045         }else{
16046             this.inputEl().dom.setAttribute('readOnly', false);
16047             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16048             this.inputEl().removeClass('x-combo-noedit');
16049         }
16050     },
16051
16052     // private
16053     
16054     onBeforeLoad : function(combo,opts){
16055         if(!this.hasFocus){
16056             return;
16057         }
16058          if (!opts.add) {
16059             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16060          }
16061         this.restrictHeight();
16062         this.selectedIndex = -1;
16063     },
16064
16065     // private
16066     onLoad : function(){
16067         
16068         this.hasQuery = false;
16069         
16070         if(!this.hasFocus){
16071             return;
16072         }
16073         
16074         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16075             this.loading.hide();
16076         }
16077         
16078         if(this.store.getCount() > 0){
16079             
16080             this.expand();
16081             this.restrictHeight();
16082             if(this.lastQuery == this.allQuery){
16083                 if(this.editable && !this.tickable){
16084                     this.inputEl().dom.select();
16085                 }
16086                 
16087                 if(
16088                     !this.selectByValue(this.value, true) &&
16089                     this.autoFocus && 
16090                     (
16091                         !this.store.lastOptions ||
16092                         typeof(this.store.lastOptions.add) == 'undefined' || 
16093                         this.store.lastOptions.add != true
16094                     )
16095                 ){
16096                     this.select(0, true);
16097                 }
16098             }else{
16099                 if(this.autoFocus){
16100                     this.selectNext();
16101                 }
16102                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16103                     this.taTask.delay(this.typeAheadDelay);
16104                 }
16105             }
16106         }else{
16107             this.onEmptyResults();
16108         }
16109         
16110         //this.el.focus();
16111     },
16112     // private
16113     onLoadException : function()
16114     {
16115         this.hasQuery = false;
16116         
16117         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16118             this.loading.hide();
16119         }
16120         
16121         if(this.tickable && this.editable){
16122             return;
16123         }
16124         
16125         this.collapse();
16126         // only causes errors at present
16127         //Roo.log(this.store.reader.jsonData);
16128         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16129             // fixme
16130             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16131         //}
16132         
16133         
16134     },
16135     // private
16136     onTypeAhead : function(){
16137         if(this.store.getCount() > 0){
16138             var r = this.store.getAt(0);
16139             var newValue = r.data[this.displayField];
16140             var len = newValue.length;
16141             var selStart = this.getRawValue().length;
16142             
16143             if(selStart != len){
16144                 this.setRawValue(newValue);
16145                 this.selectText(selStart, newValue.length);
16146             }
16147         }
16148     },
16149
16150     // private
16151     onSelect : function(record, index){
16152         
16153         if(this.fireEvent('beforeselect', this, record, index) !== false){
16154         
16155             this.setFromData(index > -1 ? record.data : false);
16156             
16157             this.collapse();
16158             this.fireEvent('select', this, record, index);
16159         }
16160     },
16161
16162     /**
16163      * Returns the currently selected field value or empty string if no value is set.
16164      * @return {String} value The selected value
16165      */
16166     getValue : function()
16167     {
16168         if(Roo.isIOS && this.useNativeIOS){
16169             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16170         }
16171         
16172         if(this.multiple){
16173             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16174         }
16175         
16176         if(this.valueField){
16177             return typeof this.value != 'undefined' ? this.value : '';
16178         }else{
16179             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16180         }
16181     },
16182     
16183     getRawValue : function()
16184     {
16185         if(Roo.isIOS && this.useNativeIOS){
16186             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16187         }
16188         
16189         var v = this.inputEl().getValue();
16190         
16191         return v;
16192     },
16193
16194     /**
16195      * Clears any text/value currently set in the field
16196      */
16197     clearValue : function(){
16198         
16199         if(this.hiddenField){
16200             this.hiddenField.dom.value = '';
16201         }
16202         this.value = '';
16203         this.setRawValue('');
16204         this.lastSelectionText = '';
16205         this.lastData = false;
16206         
16207         var close = this.closeTriggerEl();
16208         
16209         if(close){
16210             close.hide();
16211         }
16212         
16213         this.validate();
16214         
16215     },
16216
16217     /**
16218      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16219      * will be displayed in the field.  If the value does not match the data value of an existing item,
16220      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16221      * Otherwise the field will be blank (although the value will still be set).
16222      * @param {String} value The value to match
16223      */
16224     setValue : function(v)
16225     {
16226         if(Roo.isIOS && this.useNativeIOS){
16227             this.setIOSValue(v);
16228             return;
16229         }
16230         
16231         if(this.multiple){
16232             this.syncValue();
16233             return;
16234         }
16235         
16236         var text = v;
16237         if(this.valueField){
16238             var r = this.findRecord(this.valueField, v);
16239             if(r){
16240                 text = r.data[this.displayField];
16241             }else if(this.valueNotFoundText !== undefined){
16242                 text = this.valueNotFoundText;
16243             }
16244         }
16245         this.lastSelectionText = text;
16246         if(this.hiddenField){
16247             this.hiddenField.dom.value = v;
16248         }
16249         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16250         this.value = v;
16251         
16252         var close = this.closeTriggerEl();
16253         
16254         if(close){
16255             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16256         }
16257         
16258         this.validate();
16259     },
16260     /**
16261      * @property {Object} the last set data for the element
16262      */
16263     
16264     lastData : false,
16265     /**
16266      * Sets the value of the field based on a object which is related to the record format for the store.
16267      * @param {Object} value the value to set as. or false on reset?
16268      */
16269     setFromData : function(o){
16270         
16271         if(this.multiple){
16272             this.addItem(o);
16273             return;
16274         }
16275             
16276         var dv = ''; // display value
16277         var vv = ''; // value value..
16278         this.lastData = o;
16279         if (this.displayField) {
16280             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16281         } else {
16282             // this is an error condition!!!
16283             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16284         }
16285         
16286         if(this.valueField){
16287             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16288         }
16289         
16290         var close = this.closeTriggerEl();
16291         
16292         if(close){
16293             if(dv.length || vv * 1 > 0){
16294                 close.show() ;
16295                 this.blockFocus=true;
16296             } else {
16297                 close.hide();
16298             }             
16299         }
16300         
16301         if(this.hiddenField){
16302             this.hiddenField.dom.value = vv;
16303             
16304             this.lastSelectionText = dv;
16305             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16306             this.value = vv;
16307             return;
16308         }
16309         // no hidden field.. - we store the value in 'value', but still display
16310         // display field!!!!
16311         this.lastSelectionText = dv;
16312         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16313         this.value = vv;
16314         
16315         
16316         
16317     },
16318     // private
16319     reset : function(){
16320         // overridden so that last data is reset..
16321         
16322         if(this.multiple){
16323             this.clearItem();
16324             return;
16325         }
16326         
16327         this.setValue(this.originalValue);
16328         //this.clearInvalid();
16329         this.lastData = false;
16330         if (this.view) {
16331             this.view.clearSelections();
16332         }
16333         
16334         this.validate();
16335     },
16336     // private
16337     findRecord : function(prop, value){
16338         var record;
16339         if(this.store.getCount() > 0){
16340             this.store.each(function(r){
16341                 if(r.data[prop] == value){
16342                     record = r;
16343                     return false;
16344                 }
16345                 return true;
16346             });
16347         }
16348         return record;
16349     },
16350     
16351     getName: function()
16352     {
16353         // returns hidden if it's set..
16354         if (!this.rendered) {return ''};
16355         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16356         
16357     },
16358     // private
16359     onViewMove : function(e, t){
16360         this.inKeyMode = false;
16361     },
16362
16363     // private
16364     onViewOver : function(e, t){
16365         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16366             return;
16367         }
16368         var item = this.view.findItemFromChild(t);
16369         
16370         if(item){
16371             var index = this.view.indexOf(item);
16372             this.select(index, false);
16373         }
16374     },
16375
16376     // private
16377     onViewClick : function(view, doFocus, el, e)
16378     {
16379         var index = this.view.getSelectedIndexes()[0];
16380         
16381         var r = this.store.getAt(index);
16382         
16383         if(this.tickable){
16384             
16385             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16386                 return;
16387             }
16388             
16389             var rm = false;
16390             var _this = this;
16391             
16392             Roo.each(this.tickItems, function(v,k){
16393                 
16394                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16395                     Roo.log(v);
16396                     _this.tickItems.splice(k, 1);
16397                     
16398                     if(typeof(e) == 'undefined' && view == false){
16399                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16400                     }
16401                     
16402                     rm = true;
16403                     return;
16404                 }
16405             });
16406             
16407             if(rm){
16408                 return;
16409             }
16410             
16411             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16412                 this.tickItems.push(r.data);
16413             }
16414             
16415             if(typeof(e) == 'undefined' && view == false){
16416                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16417             }
16418                     
16419             return;
16420         }
16421         
16422         if(r){
16423             this.onSelect(r, index);
16424         }
16425         if(doFocus !== false && !this.blockFocus){
16426             this.inputEl().focus();
16427         }
16428     },
16429
16430     // private
16431     restrictHeight : function(){
16432         //this.innerList.dom.style.height = '';
16433         //var inner = this.innerList.dom;
16434         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16435         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16436         //this.list.beginUpdate();
16437         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16438         this.list.alignTo(this.inputEl(), this.listAlign);
16439         this.list.alignTo(this.inputEl(), this.listAlign);
16440         //this.list.endUpdate();
16441     },
16442
16443     // private
16444     onEmptyResults : function(){
16445         
16446         if(this.tickable && this.editable){
16447             this.hasFocus = false;
16448             this.restrictHeight();
16449             return;
16450         }
16451         
16452         this.collapse();
16453     },
16454
16455     /**
16456      * Returns true if the dropdown list is expanded, else false.
16457      */
16458     isExpanded : function(){
16459         return this.list.isVisible();
16460     },
16461
16462     /**
16463      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16464      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16465      * @param {String} value The data value of the item to select
16466      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16467      * selected item if it is not currently in view (defaults to true)
16468      * @return {Boolean} True if the value matched an item in the list, else false
16469      */
16470     selectByValue : function(v, scrollIntoView){
16471         if(v !== undefined && v !== null){
16472             var r = this.findRecord(this.valueField || this.displayField, v);
16473             if(r){
16474                 this.select(this.store.indexOf(r), scrollIntoView);
16475                 return true;
16476             }
16477         }
16478         return false;
16479     },
16480
16481     /**
16482      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16483      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16484      * @param {Number} index The zero-based index of the list item to select
16485      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16486      * selected item if it is not currently in view (defaults to true)
16487      */
16488     select : function(index, scrollIntoView){
16489         this.selectedIndex = index;
16490         this.view.select(index);
16491         if(scrollIntoView !== false){
16492             var el = this.view.getNode(index);
16493             /*
16494              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16495              */
16496             if(el){
16497                 this.list.scrollChildIntoView(el, false);
16498             }
16499         }
16500     },
16501
16502     // private
16503     selectNext : function(){
16504         var ct = this.store.getCount();
16505         if(ct > 0){
16506             if(this.selectedIndex == -1){
16507                 this.select(0);
16508             }else if(this.selectedIndex < ct-1){
16509                 this.select(this.selectedIndex+1);
16510             }
16511         }
16512     },
16513
16514     // private
16515     selectPrev : function(){
16516         var ct = this.store.getCount();
16517         if(ct > 0){
16518             if(this.selectedIndex == -1){
16519                 this.select(0);
16520             }else if(this.selectedIndex != 0){
16521                 this.select(this.selectedIndex-1);
16522             }
16523         }
16524     },
16525
16526     // private
16527     onKeyUp : function(e){
16528         if(this.editable !== false && !e.isSpecialKey()){
16529             this.lastKey = e.getKey();
16530             this.dqTask.delay(this.queryDelay);
16531         }
16532     },
16533
16534     // private
16535     validateBlur : function(){
16536         return !this.list || !this.list.isVisible();   
16537     },
16538
16539     // private
16540     initQuery : function(){
16541         
16542         var v = this.getRawValue();
16543         
16544         if(this.tickable && this.editable){
16545             v = this.tickableInputEl().getValue();
16546         }
16547         
16548         this.doQuery(v);
16549     },
16550
16551     // private
16552     doForce : function(){
16553         if(this.inputEl().dom.value.length > 0){
16554             this.inputEl().dom.value =
16555                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16556              
16557         }
16558     },
16559
16560     /**
16561      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16562      * query allowing the query action to be canceled if needed.
16563      * @param {String} query The SQL query to execute
16564      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16565      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16566      * saved in the current store (defaults to false)
16567      */
16568     doQuery : function(q, forceAll){
16569         
16570         if(q === undefined || q === null){
16571             q = '';
16572         }
16573         var qe = {
16574             query: q,
16575             forceAll: forceAll,
16576             combo: this,
16577             cancel:false
16578         };
16579         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16580             return false;
16581         }
16582         q = qe.query;
16583         
16584         forceAll = qe.forceAll;
16585         if(forceAll === true || (q.length >= this.minChars)){
16586             
16587             this.hasQuery = true;
16588             
16589             if(this.lastQuery != q || this.alwaysQuery){
16590                 this.lastQuery = q;
16591                 if(this.mode == 'local'){
16592                     this.selectedIndex = -1;
16593                     if(forceAll){
16594                         this.store.clearFilter();
16595                     }else{
16596                         
16597                         if(this.specialFilter){
16598                             this.fireEvent('specialfilter', this);
16599                             this.onLoad();
16600                             return;
16601                         }
16602                         
16603                         this.store.filter(this.displayField, q);
16604                     }
16605                     
16606                     this.store.fireEvent("datachanged", this.store);
16607                     
16608                     this.onLoad();
16609                     
16610                     
16611                 }else{
16612                     
16613                     this.store.baseParams[this.queryParam] = q;
16614                     
16615                     var options = {params : this.getParams(q)};
16616                     
16617                     if(this.loadNext){
16618                         options.add = true;
16619                         options.params.start = this.page * this.pageSize;
16620                     }
16621                     
16622                     this.store.load(options);
16623                     
16624                     /*
16625                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16626                      *  we should expand the list on onLoad
16627                      *  so command out it
16628                      */
16629 //                    this.expand();
16630                 }
16631             }else{
16632                 this.selectedIndex = -1;
16633                 this.onLoad();   
16634             }
16635         }
16636         
16637         this.loadNext = false;
16638     },
16639     
16640     // private
16641     getParams : function(q){
16642         var p = {};
16643         //p[this.queryParam] = q;
16644         
16645         if(this.pageSize){
16646             p.start = 0;
16647             p.limit = this.pageSize;
16648         }
16649         return p;
16650     },
16651
16652     /**
16653      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16654      */
16655     collapse : function(){
16656         if(!this.isExpanded()){
16657             return;
16658         }
16659         
16660         this.list.hide();
16661         
16662         this.hasFocus = false;
16663         
16664         if(this.tickable){
16665             this.okBtn.hide();
16666             this.cancelBtn.hide();
16667             this.trigger.show();
16668             
16669             if(this.editable){
16670                 this.tickableInputEl().dom.value = '';
16671                 this.tickableInputEl().blur();
16672             }
16673             
16674         }
16675         
16676         Roo.get(document).un('mousedown', this.collapseIf, this);
16677         Roo.get(document).un('mousewheel', this.collapseIf, this);
16678         if (!this.editable) {
16679             Roo.get(document).un('keydown', this.listKeyPress, this);
16680         }
16681         this.fireEvent('collapse', this);
16682         
16683         this.validate();
16684     },
16685
16686     // private
16687     collapseIf : function(e){
16688         var in_combo  = e.within(this.el);
16689         var in_list =  e.within(this.list);
16690         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16691         
16692         if (in_combo || in_list || is_list) {
16693             //e.stopPropagation();
16694             return;
16695         }
16696         
16697         if(this.tickable){
16698             this.onTickableFooterButtonClick(e, false, false);
16699         }
16700
16701         this.collapse();
16702         
16703     },
16704
16705     /**
16706      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16707      */
16708     expand : function(){
16709        
16710         if(this.isExpanded() || !this.hasFocus){
16711             return;
16712         }
16713         
16714         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16715         this.list.setWidth(lw);
16716         
16717         Roo.log('expand');
16718         
16719         this.list.show();
16720         
16721         this.restrictHeight();
16722         
16723         if(this.tickable){
16724             
16725             this.tickItems = Roo.apply([], this.item);
16726             
16727             this.okBtn.show();
16728             this.cancelBtn.show();
16729             this.trigger.hide();
16730             
16731             if(this.editable){
16732                 this.tickableInputEl().focus();
16733             }
16734             
16735         }
16736         
16737         Roo.get(document).on('mousedown', this.collapseIf, this);
16738         Roo.get(document).on('mousewheel', this.collapseIf, this);
16739         if (!this.editable) {
16740             Roo.get(document).on('keydown', this.listKeyPress, this);
16741         }
16742         
16743         this.fireEvent('expand', this);
16744     },
16745
16746     // private
16747     // Implements the default empty TriggerField.onTriggerClick function
16748     onTriggerClick : function(e)
16749     {
16750         Roo.log('trigger click');
16751         
16752         if(this.disabled || !this.triggerList){
16753             return;
16754         }
16755         
16756         this.page = 0;
16757         this.loadNext = false;
16758         
16759         if(this.isExpanded()){
16760             this.collapse();
16761             if (!this.blockFocus) {
16762                 this.inputEl().focus();
16763             }
16764             
16765         }else {
16766             this.hasFocus = true;
16767             if(this.triggerAction == 'all') {
16768                 this.doQuery(this.allQuery, true);
16769             } else {
16770                 this.doQuery(this.getRawValue());
16771             }
16772             if (!this.blockFocus) {
16773                 this.inputEl().focus();
16774             }
16775         }
16776     },
16777     
16778     onTickableTriggerClick : function(e)
16779     {
16780         if(this.disabled){
16781             return;
16782         }
16783         
16784         this.page = 0;
16785         this.loadNext = false;
16786         this.hasFocus = true;
16787         
16788         if(this.triggerAction == 'all') {
16789             this.doQuery(this.allQuery, true);
16790         } else {
16791             this.doQuery(this.getRawValue());
16792         }
16793     },
16794     
16795     onSearchFieldClick : function(e)
16796     {
16797         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16798             this.onTickableFooterButtonClick(e, false, false);
16799             return;
16800         }
16801         
16802         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16803             return;
16804         }
16805         
16806         this.page = 0;
16807         this.loadNext = false;
16808         this.hasFocus = true;
16809         
16810         if(this.triggerAction == 'all') {
16811             this.doQuery(this.allQuery, true);
16812         } else {
16813             this.doQuery(this.getRawValue());
16814         }
16815     },
16816     
16817     listKeyPress : function(e)
16818     {
16819         //Roo.log('listkeypress');
16820         // scroll to first matching element based on key pres..
16821         if (e.isSpecialKey()) {
16822             return false;
16823         }
16824         var k = String.fromCharCode(e.getKey()).toUpperCase();
16825         //Roo.log(k);
16826         var match  = false;
16827         var csel = this.view.getSelectedNodes();
16828         var cselitem = false;
16829         if (csel.length) {
16830             var ix = this.view.indexOf(csel[0]);
16831             cselitem  = this.store.getAt(ix);
16832             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16833                 cselitem = false;
16834             }
16835             
16836         }
16837         
16838         this.store.each(function(v) { 
16839             if (cselitem) {
16840                 // start at existing selection.
16841                 if (cselitem.id == v.id) {
16842                     cselitem = false;
16843                 }
16844                 return true;
16845             }
16846                 
16847             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16848                 match = this.store.indexOf(v);
16849                 return false;
16850             }
16851             return true;
16852         }, this);
16853         
16854         if (match === false) {
16855             return true; // no more action?
16856         }
16857         // scroll to?
16858         this.view.select(match);
16859         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16860         sn.scrollIntoView(sn.dom.parentNode, false);
16861     },
16862     
16863     onViewScroll : function(e, t){
16864         
16865         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){
16866             return;
16867         }
16868         
16869         this.hasQuery = true;
16870         
16871         this.loading = this.list.select('.loading', true).first();
16872         
16873         if(this.loading === null){
16874             this.list.createChild({
16875                 tag: 'div',
16876                 cls: 'loading roo-select2-more-results roo-select2-active',
16877                 html: 'Loading more results...'
16878             });
16879             
16880             this.loading = this.list.select('.loading', true).first();
16881             
16882             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16883             
16884             this.loading.hide();
16885         }
16886         
16887         this.loading.show();
16888         
16889         var _combo = this;
16890         
16891         this.page++;
16892         this.loadNext = true;
16893         
16894         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16895         
16896         return;
16897     },
16898     
16899     addItem : function(o)
16900     {   
16901         var dv = ''; // display value
16902         
16903         if (this.displayField) {
16904             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16905         } else {
16906             // this is an error condition!!!
16907             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16908         }
16909         
16910         if(!dv.length){
16911             return;
16912         }
16913         
16914         var choice = this.choices.createChild({
16915             tag: 'li',
16916             cls: 'roo-select2-search-choice',
16917             cn: [
16918                 {
16919                     tag: 'div',
16920                     html: dv
16921                 },
16922                 {
16923                     tag: 'a',
16924                     href: '#',
16925                     cls: 'roo-select2-search-choice-close fa fa-times',
16926                     tabindex: '-1'
16927                 }
16928             ]
16929             
16930         }, this.searchField);
16931         
16932         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16933         
16934         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16935         
16936         this.item.push(o);
16937         
16938         this.lastData = o;
16939         
16940         this.syncValue();
16941         
16942         this.inputEl().dom.value = '';
16943         
16944         this.validate();
16945     },
16946     
16947     onRemoveItem : function(e, _self, o)
16948     {
16949         e.preventDefault();
16950         
16951         this.lastItem = Roo.apply([], this.item);
16952         
16953         var index = this.item.indexOf(o.data) * 1;
16954         
16955         if( index < 0){
16956             Roo.log('not this item?!');
16957             return;
16958         }
16959         
16960         this.item.splice(index, 1);
16961         o.item.remove();
16962         
16963         this.syncValue();
16964         
16965         this.fireEvent('remove', this, e);
16966         
16967         this.validate();
16968         
16969     },
16970     
16971     syncValue : function()
16972     {
16973         if(!this.item.length){
16974             this.clearValue();
16975             return;
16976         }
16977             
16978         var value = [];
16979         var _this = this;
16980         Roo.each(this.item, function(i){
16981             if(_this.valueField){
16982                 value.push(i[_this.valueField]);
16983                 return;
16984             }
16985
16986             value.push(i);
16987         });
16988
16989         this.value = value.join(',');
16990
16991         if(this.hiddenField){
16992             this.hiddenField.dom.value = this.value;
16993         }
16994         
16995         this.store.fireEvent("datachanged", this.store);
16996         
16997         this.validate();
16998     },
16999     
17000     clearItem : function()
17001     {
17002         if(!this.multiple){
17003             return;
17004         }
17005         
17006         this.item = [];
17007         
17008         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17009            c.remove();
17010         });
17011         
17012         this.syncValue();
17013         
17014         this.validate();
17015         
17016         if(this.tickable && !Roo.isTouch){
17017             this.view.refresh();
17018         }
17019     },
17020     
17021     inputEl: function ()
17022     {
17023         if(Roo.isIOS && this.useNativeIOS){
17024             return this.el.select('select.roo-ios-select', true).first();
17025         }
17026         
17027         if(Roo.isTouch && this.mobileTouchView){
17028             return this.el.select('input.form-control',true).first();
17029         }
17030         
17031         if(this.tickable){
17032             return this.searchField;
17033         }
17034         
17035         return this.el.select('input.form-control',true).first();
17036     },
17037     
17038     onTickableFooterButtonClick : function(e, btn, el)
17039     {
17040         e.preventDefault();
17041         
17042         this.lastItem = Roo.apply([], this.item);
17043         
17044         if(btn && btn.name == 'cancel'){
17045             this.tickItems = Roo.apply([], this.item);
17046             this.collapse();
17047             return;
17048         }
17049         
17050         this.clearItem();
17051         
17052         var _this = this;
17053         
17054         Roo.each(this.tickItems, function(o){
17055             _this.addItem(o);
17056         });
17057         
17058         this.collapse();
17059         
17060     },
17061     
17062     validate : function()
17063     {
17064         if(this.getVisibilityEl().hasClass('hidden')){
17065             return true;
17066         }
17067         
17068         var v = this.getRawValue();
17069         
17070         if(this.multiple){
17071             v = this.getValue();
17072         }
17073         
17074         if(this.disabled || this.allowBlank || v.length){
17075             this.markValid();
17076             return true;
17077         }
17078         
17079         this.markInvalid();
17080         return false;
17081     },
17082     
17083     tickableInputEl : function()
17084     {
17085         if(!this.tickable || !this.editable){
17086             return this.inputEl();
17087         }
17088         
17089         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17090     },
17091     
17092     
17093     getAutoCreateTouchView : function()
17094     {
17095         var id = Roo.id();
17096         
17097         var cfg = {
17098             cls: 'form-group' //input-group
17099         };
17100         
17101         var input =  {
17102             tag: 'input',
17103             id : id,
17104             type : this.inputType,
17105             cls : 'form-control x-combo-noedit',
17106             autocomplete: 'new-password',
17107             placeholder : this.placeholder || '',
17108             readonly : true
17109         };
17110         
17111         if (this.name) {
17112             input.name = this.name;
17113         }
17114         
17115         if (this.size) {
17116             input.cls += ' input-' + this.size;
17117         }
17118         
17119         if (this.disabled) {
17120             input.disabled = true;
17121         }
17122         
17123         var inputblock = {
17124             cls : 'roo-combobox-wrap',
17125             cn : [
17126                 input
17127             ]
17128         };
17129         
17130         if(this.before){
17131             inputblock.cls += ' input-group';
17132             
17133             inputblock.cn.unshift({
17134                 tag :'span',
17135                 cls : 'input-group-addon input-group-prepend input-group-text',
17136                 html : this.before
17137             });
17138         }
17139         
17140         if(this.removable && !this.multiple){
17141             inputblock.cls += ' roo-removable';
17142             
17143             inputblock.cn.push({
17144                 tag: 'button',
17145                 html : 'x',
17146                 cls : 'roo-combo-removable-btn close'
17147             });
17148         }
17149
17150         if(this.hasFeedback && !this.allowBlank){
17151             
17152             inputblock.cls += ' has-feedback';
17153             
17154             inputblock.cn.push({
17155                 tag: 'span',
17156                 cls: 'glyphicon form-control-feedback'
17157             });
17158             
17159         }
17160         
17161         if (this.after) {
17162             
17163             inputblock.cls += (this.before) ? '' : ' input-group';
17164             
17165             inputblock.cn.push({
17166                 tag :'span',
17167                 cls : 'input-group-addon input-group-append input-group-text',
17168                 html : this.after
17169             });
17170         }
17171
17172         
17173         var ibwrap = inputblock;
17174         
17175         if(this.multiple){
17176             ibwrap = {
17177                 tag: 'ul',
17178                 cls: 'roo-select2-choices',
17179                 cn:[
17180                     {
17181                         tag: 'li',
17182                         cls: 'roo-select2-search-field',
17183                         cn: [
17184
17185                             inputblock
17186                         ]
17187                     }
17188                 ]
17189             };
17190         
17191             
17192         }
17193         
17194         var combobox = {
17195             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17196             cn: [
17197                 {
17198                     tag: 'input',
17199                     type : 'hidden',
17200                     cls: 'form-hidden-field'
17201                 },
17202                 ibwrap
17203             ]
17204         };
17205         
17206         if(!this.multiple && this.showToggleBtn){
17207             
17208             var caret = {
17209                 cls: 'caret'
17210             };
17211             
17212             if (this.caret != false) {
17213                 caret = {
17214                      tag: 'i',
17215                      cls: 'fa fa-' + this.caret
17216                 };
17217                 
17218             }
17219             
17220             combobox.cn.push({
17221                 tag :'span',
17222                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17223                 cn : [
17224                     Roo.bootstrap.version == 3 ? caret : '',
17225                     {
17226                         tag: 'span',
17227                         cls: 'combobox-clear',
17228                         cn  : [
17229                             {
17230                                 tag : 'i',
17231                                 cls: 'icon-remove'
17232                             }
17233                         ]
17234                     }
17235                 ]
17236
17237             })
17238         }
17239         
17240         if(this.multiple){
17241             combobox.cls += ' roo-select2-container-multi';
17242         }
17243         
17244         var align = this.labelAlign || this.parentLabelAlign();
17245         
17246         if (align ==='left' && this.fieldLabel.length) {
17247
17248             cfg.cn = [
17249                 {
17250                    tag : 'i',
17251                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17252                    tooltip : 'This field is required'
17253                 },
17254                 {
17255                     tag: 'label',
17256                     cls : 'control-label col-form-label',
17257                     html : this.fieldLabel
17258
17259                 },
17260                 {
17261                     cls : 'roo-combobox-wrap ', 
17262                     cn: [
17263                         combobox
17264                     ]
17265                 }
17266             ];
17267             
17268             var labelCfg = cfg.cn[1];
17269             var contentCfg = cfg.cn[2];
17270             
17271
17272             if(this.indicatorpos == 'right'){
17273                 cfg.cn = [
17274                     {
17275                         tag: 'label',
17276                         'for' :  id,
17277                         cls : 'control-label col-form-label',
17278                         cn : [
17279                             {
17280                                 tag : 'span',
17281                                 html : this.fieldLabel
17282                             },
17283                             {
17284                                 tag : 'i',
17285                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17286                                 tooltip : 'This field is required'
17287                             }
17288                         ]
17289                     },
17290                     {
17291                         cls : "roo-combobox-wrap ",
17292                         cn: [
17293                             combobox
17294                         ]
17295                     }
17296
17297                 ];
17298                 
17299                 labelCfg = cfg.cn[0];
17300                 contentCfg = cfg.cn[1];
17301             }
17302             
17303            
17304             
17305             if(this.labelWidth > 12){
17306                 labelCfg.style = "width: " + this.labelWidth + 'px';
17307             }
17308            
17309             if(this.labelWidth < 13 && this.labelmd == 0){
17310                 this.labelmd = this.labelWidth;
17311             }
17312             
17313             if(this.labellg > 0){
17314                 labelCfg.cls += ' col-lg-' + this.labellg;
17315                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17316             }
17317             
17318             if(this.labelmd > 0){
17319                 labelCfg.cls += ' col-md-' + this.labelmd;
17320                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17321             }
17322             
17323             if(this.labelsm > 0){
17324                 labelCfg.cls += ' col-sm-' + this.labelsm;
17325                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17326             }
17327             
17328             if(this.labelxs > 0){
17329                 labelCfg.cls += ' col-xs-' + this.labelxs;
17330                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17331             }
17332                 
17333                 
17334         } else if ( this.fieldLabel.length) {
17335             cfg.cn = [
17336                 {
17337                    tag : 'i',
17338                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17339                    tooltip : 'This field is required'
17340                 },
17341                 {
17342                     tag: 'label',
17343                     cls : 'control-label',
17344                     html : this.fieldLabel
17345
17346                 },
17347                 {
17348                     cls : '', 
17349                     cn: [
17350                         combobox
17351                     ]
17352                 }
17353             ];
17354             
17355             if(this.indicatorpos == 'right'){
17356                 cfg.cn = [
17357                     {
17358                         tag: 'label',
17359                         cls : 'control-label',
17360                         html : this.fieldLabel,
17361                         cn : [
17362                             {
17363                                tag : 'i',
17364                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17365                                tooltip : 'This field is required'
17366                             }
17367                         ]
17368                     },
17369                     {
17370                         cls : '', 
17371                         cn: [
17372                             combobox
17373                         ]
17374                     }
17375                 ];
17376             }
17377         } else {
17378             cfg.cn = combobox;    
17379         }
17380         
17381         
17382         var settings = this;
17383         
17384         ['xs','sm','md','lg'].map(function(size){
17385             if (settings[size]) {
17386                 cfg.cls += ' col-' + size + '-' + settings[size];
17387             }
17388         });
17389         
17390         return cfg;
17391     },
17392     
17393     initTouchView : function()
17394     {
17395         this.renderTouchView();
17396         
17397         this.touchViewEl.on('scroll', function(){
17398             this.el.dom.scrollTop = 0;
17399         }, this);
17400         
17401         this.originalValue = this.getValue();
17402         
17403         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17404         
17405         this.inputEl().on("click", this.showTouchView, this);
17406         if (this.triggerEl) {
17407             this.triggerEl.on("click", this.showTouchView, this);
17408         }
17409         
17410         
17411         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17412         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17413         
17414         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17415         
17416         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17417         this.store.on('load', this.onTouchViewLoad, this);
17418         this.store.on('loadexception', this.onTouchViewLoadException, this);
17419         
17420         if(this.hiddenName){
17421             
17422             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17423             
17424             this.hiddenField.dom.value =
17425                 this.hiddenValue !== undefined ? this.hiddenValue :
17426                 this.value !== undefined ? this.value : '';
17427         
17428             this.el.dom.removeAttribute('name');
17429             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17430         }
17431         
17432         if(this.multiple){
17433             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17434             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17435         }
17436         
17437         if(this.removable && !this.multiple){
17438             var close = this.closeTriggerEl();
17439             if(close){
17440                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17441                 close.on('click', this.removeBtnClick, this, close);
17442             }
17443         }
17444         /*
17445          * fix the bug in Safari iOS8
17446          */
17447         this.inputEl().on("focus", function(e){
17448             document.activeElement.blur();
17449         }, this);
17450         
17451         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17452         
17453         return;
17454         
17455         
17456     },
17457     
17458     renderTouchView : function()
17459     {
17460         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17461         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17462         
17463         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17464         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17465         
17466         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17467         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17468         this.touchViewBodyEl.setStyle('overflow', 'auto');
17469         
17470         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17471         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17472         
17473         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17474         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17475         
17476     },
17477     
17478     showTouchView : function()
17479     {
17480         if(this.disabled){
17481             return;
17482         }
17483         
17484         this.touchViewHeaderEl.hide();
17485
17486         if(this.modalTitle.length){
17487             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17488             this.touchViewHeaderEl.show();
17489         }
17490
17491         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17492         this.touchViewEl.show();
17493
17494         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17495         
17496         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17497         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17498
17499         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17500
17501         if(this.modalTitle.length){
17502             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17503         }
17504         
17505         this.touchViewBodyEl.setHeight(bodyHeight);
17506
17507         if(this.animate){
17508             var _this = this;
17509             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17510         }else{
17511             this.touchViewEl.addClass(['in','show']);
17512         }
17513         
17514         if(this._touchViewMask){
17515             Roo.get(document.body).addClass("x-body-masked");
17516             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17517             this._touchViewMask.setStyle('z-index', 10000);
17518             this._touchViewMask.addClass('show');
17519         }
17520         
17521         this.doTouchViewQuery();
17522         
17523     },
17524     
17525     hideTouchView : function()
17526     {
17527         this.touchViewEl.removeClass(['in','show']);
17528
17529         if(this.animate){
17530             var _this = this;
17531             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17532         }else{
17533             this.touchViewEl.setStyle('display', 'none');
17534         }
17535         
17536         if(this._touchViewMask){
17537             this._touchViewMask.removeClass('show');
17538             Roo.get(document.body).removeClass("x-body-masked");
17539         }
17540     },
17541     
17542     setTouchViewValue : function()
17543     {
17544         if(this.multiple){
17545             this.clearItem();
17546         
17547             var _this = this;
17548
17549             Roo.each(this.tickItems, function(o){
17550                 this.addItem(o);
17551             }, this);
17552         }
17553         
17554         this.hideTouchView();
17555     },
17556     
17557     doTouchViewQuery : function()
17558     {
17559         var qe = {
17560             query: '',
17561             forceAll: true,
17562             combo: this,
17563             cancel:false
17564         };
17565         
17566         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17567             return false;
17568         }
17569         
17570         if(!this.alwaysQuery || this.mode == 'local'){
17571             this.onTouchViewLoad();
17572             return;
17573         }
17574         
17575         this.store.load();
17576     },
17577     
17578     onTouchViewBeforeLoad : function(combo,opts)
17579     {
17580         return;
17581     },
17582
17583     // private
17584     onTouchViewLoad : function()
17585     {
17586         if(this.store.getCount() < 1){
17587             this.onTouchViewEmptyResults();
17588             return;
17589         }
17590         
17591         this.clearTouchView();
17592         
17593         var rawValue = this.getRawValue();
17594         
17595         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17596         
17597         this.tickItems = [];
17598         
17599         this.store.data.each(function(d, rowIndex){
17600             var row = this.touchViewListGroup.createChild(template);
17601             
17602             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17603                 row.addClass(d.data.cls);
17604             }
17605             
17606             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17607                 var cfg = {
17608                     data : d.data,
17609                     html : d.data[this.displayField]
17610                 };
17611                 
17612                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17613                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17614                 }
17615             }
17616             row.removeClass('selected');
17617             if(!this.multiple && this.valueField &&
17618                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17619             {
17620                 // radio buttons..
17621                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17622                 row.addClass('selected');
17623             }
17624             
17625             if(this.multiple && this.valueField &&
17626                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17627             {
17628                 
17629                 // checkboxes...
17630                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17631                 this.tickItems.push(d.data);
17632             }
17633             
17634             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17635             
17636         }, this);
17637         
17638         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17639         
17640         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17641
17642         if(this.modalTitle.length){
17643             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17644         }
17645
17646         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17647         
17648         if(this.mobile_restrict_height && listHeight < bodyHeight){
17649             this.touchViewBodyEl.setHeight(listHeight);
17650         }
17651         
17652         var _this = this;
17653         
17654         if(firstChecked && listHeight > bodyHeight){
17655             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17656         }
17657         
17658     },
17659     
17660     onTouchViewLoadException : function()
17661     {
17662         this.hideTouchView();
17663     },
17664     
17665     onTouchViewEmptyResults : function()
17666     {
17667         this.clearTouchView();
17668         
17669         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17670         
17671         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17672         
17673     },
17674     
17675     clearTouchView : function()
17676     {
17677         this.touchViewListGroup.dom.innerHTML = '';
17678     },
17679     
17680     onTouchViewClick : function(e, el, o)
17681     {
17682         e.preventDefault();
17683         
17684         var row = o.row;
17685         var rowIndex = o.rowIndex;
17686         
17687         var r = this.store.getAt(rowIndex);
17688         
17689         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17690             
17691             if(!this.multiple){
17692                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17693                     c.dom.removeAttribute('checked');
17694                 }, this);
17695
17696                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17697
17698                 this.setFromData(r.data);
17699
17700                 var close = this.closeTriggerEl();
17701
17702                 if(close){
17703                     close.show();
17704                 }
17705
17706                 this.hideTouchView();
17707
17708                 this.fireEvent('select', this, r, rowIndex);
17709
17710                 return;
17711             }
17712
17713             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17714                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17715                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17716                 return;
17717             }
17718
17719             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17720             this.addItem(r.data);
17721             this.tickItems.push(r.data);
17722         }
17723     },
17724     
17725     getAutoCreateNativeIOS : function()
17726     {
17727         var cfg = {
17728             cls: 'form-group' //input-group,
17729         };
17730         
17731         var combobox =  {
17732             tag: 'select',
17733             cls : 'roo-ios-select'
17734         };
17735         
17736         if (this.name) {
17737             combobox.name = this.name;
17738         }
17739         
17740         if (this.disabled) {
17741             combobox.disabled = true;
17742         }
17743         
17744         var settings = this;
17745         
17746         ['xs','sm','md','lg'].map(function(size){
17747             if (settings[size]) {
17748                 cfg.cls += ' col-' + size + '-' + settings[size];
17749             }
17750         });
17751         
17752         cfg.cn = combobox;
17753         
17754         return cfg;
17755         
17756     },
17757     
17758     initIOSView : function()
17759     {
17760         this.store.on('load', this.onIOSViewLoad, this);
17761         
17762         return;
17763     },
17764     
17765     onIOSViewLoad : function()
17766     {
17767         if(this.store.getCount() < 1){
17768             return;
17769         }
17770         
17771         this.clearIOSView();
17772         
17773         if(this.allowBlank) {
17774             
17775             var default_text = '-- SELECT --';
17776             
17777             if(this.placeholder.length){
17778                 default_text = this.placeholder;
17779             }
17780             
17781             if(this.emptyTitle.length){
17782                 default_text += ' - ' + this.emptyTitle + ' -';
17783             }
17784             
17785             var opt = this.inputEl().createChild({
17786                 tag: 'option',
17787                 value : 0,
17788                 html : default_text
17789             });
17790             
17791             var o = {};
17792             o[this.valueField] = 0;
17793             o[this.displayField] = default_text;
17794             
17795             this.ios_options.push({
17796                 data : o,
17797                 el : opt
17798             });
17799             
17800         }
17801         
17802         this.store.data.each(function(d, rowIndex){
17803             
17804             var html = '';
17805             
17806             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17807                 html = d.data[this.displayField];
17808             }
17809             
17810             var value = '';
17811             
17812             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17813                 value = d.data[this.valueField];
17814             }
17815             
17816             var option = {
17817                 tag: 'option',
17818                 value : value,
17819                 html : html
17820             };
17821             
17822             if(this.value == d.data[this.valueField]){
17823                 option['selected'] = true;
17824             }
17825             
17826             var opt = this.inputEl().createChild(option);
17827             
17828             this.ios_options.push({
17829                 data : d.data,
17830                 el : opt
17831             });
17832             
17833         }, this);
17834         
17835         this.inputEl().on('change', function(){
17836            this.fireEvent('select', this);
17837         }, this);
17838         
17839     },
17840     
17841     clearIOSView: function()
17842     {
17843         this.inputEl().dom.innerHTML = '';
17844         
17845         this.ios_options = [];
17846     },
17847     
17848     setIOSValue: function(v)
17849     {
17850         this.value = v;
17851         
17852         if(!this.ios_options){
17853             return;
17854         }
17855         
17856         Roo.each(this.ios_options, function(opts){
17857            
17858            opts.el.dom.removeAttribute('selected');
17859            
17860            if(opts.data[this.valueField] != v){
17861                return;
17862            }
17863            
17864            opts.el.dom.setAttribute('selected', true);
17865            
17866         }, this);
17867     }
17868
17869     /** 
17870     * @cfg {Boolean} grow 
17871     * @hide 
17872     */
17873     /** 
17874     * @cfg {Number} growMin 
17875     * @hide 
17876     */
17877     /** 
17878     * @cfg {Number} growMax 
17879     * @hide 
17880     */
17881     /**
17882      * @hide
17883      * @method autoSize
17884      */
17885 });
17886
17887 Roo.apply(Roo.bootstrap.ComboBox,  {
17888     
17889     header : {
17890         tag: 'div',
17891         cls: 'modal-header',
17892         cn: [
17893             {
17894                 tag: 'h4',
17895                 cls: 'modal-title'
17896             }
17897         ]
17898     },
17899     
17900     body : {
17901         tag: 'div',
17902         cls: 'modal-body',
17903         cn: [
17904             {
17905                 tag: 'ul',
17906                 cls: 'list-group'
17907             }
17908         ]
17909     },
17910     
17911     listItemRadio : {
17912         tag: 'li',
17913         cls: 'list-group-item',
17914         cn: [
17915             {
17916                 tag: 'span',
17917                 cls: 'roo-combobox-list-group-item-value'
17918             },
17919             {
17920                 tag: 'div',
17921                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17922                 cn: [
17923                     {
17924                         tag: 'input',
17925                         type: 'radio'
17926                     },
17927                     {
17928                         tag: 'label'
17929                     }
17930                 ]
17931             }
17932         ]
17933     },
17934     
17935     listItemCheckbox : {
17936         tag: 'li',
17937         cls: 'list-group-item',
17938         cn: [
17939             {
17940                 tag: 'span',
17941                 cls: 'roo-combobox-list-group-item-value'
17942             },
17943             {
17944                 tag: 'div',
17945                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17946                 cn: [
17947                     {
17948                         tag: 'input',
17949                         type: 'checkbox'
17950                     },
17951                     {
17952                         tag: 'label'
17953                     }
17954                 ]
17955             }
17956         ]
17957     },
17958     
17959     emptyResult : {
17960         tag: 'div',
17961         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17962     },
17963     
17964     footer : {
17965         tag: 'div',
17966         cls: 'modal-footer',
17967         cn: [
17968             {
17969                 tag: 'div',
17970                 cls: 'row',
17971                 cn: [
17972                     {
17973                         tag: 'div',
17974                         cls: 'col-xs-6 text-left',
17975                         cn: {
17976                             tag: 'button',
17977                             cls: 'btn btn-danger roo-touch-view-cancel',
17978                             html: 'Cancel'
17979                         }
17980                     },
17981                     {
17982                         tag: 'div',
17983                         cls: 'col-xs-6 text-right',
17984                         cn: {
17985                             tag: 'button',
17986                             cls: 'btn btn-success roo-touch-view-ok',
17987                             html: 'OK'
17988                         }
17989                     }
17990                 ]
17991             }
17992         ]
17993         
17994     }
17995 });
17996
17997 Roo.apply(Roo.bootstrap.ComboBox,  {
17998     
17999     touchViewTemplate : {
18000         tag: 'div',
18001         cls: 'modal fade roo-combobox-touch-view',
18002         cn: [
18003             {
18004                 tag: 'div',
18005                 cls: 'modal-dialog',
18006                 style : 'position:fixed', // we have to fix position....
18007                 cn: [
18008                     {
18009                         tag: 'div',
18010                         cls: 'modal-content',
18011                         cn: [
18012                             Roo.bootstrap.ComboBox.header,
18013                             Roo.bootstrap.ComboBox.body,
18014                             Roo.bootstrap.ComboBox.footer
18015                         ]
18016                     }
18017                 ]
18018             }
18019         ]
18020     }
18021 });/*
18022  * Based on:
18023  * Ext JS Library 1.1.1
18024  * Copyright(c) 2006-2007, Ext JS, LLC.
18025  *
18026  * Originally Released Under LGPL - original licence link has changed is not relivant.
18027  *
18028  * Fork - LGPL
18029  * <script type="text/javascript">
18030  */
18031
18032 /**
18033  * @class Roo.View
18034  * @extends Roo.util.Observable
18035  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18036  * This class also supports single and multi selection modes. <br>
18037  * Create a data model bound view:
18038  <pre><code>
18039  var store = new Roo.data.Store(...);
18040
18041  var view = new Roo.View({
18042     el : "my-element",
18043     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18044  
18045     singleSelect: true,
18046     selectedClass: "ydataview-selected",
18047     store: store
18048  });
18049
18050  // listen for node click?
18051  view.on("click", function(vw, index, node, e){
18052  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18053  });
18054
18055  // load XML data
18056  dataModel.load("foobar.xml");
18057  </code></pre>
18058  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18059  * <br><br>
18060  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18061  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18062  * 
18063  * Note: old style constructor is still suported (container, template, config)
18064  * 
18065  * @constructor
18066  * Create a new View
18067  * @param {Object} config The config object
18068  * 
18069  */
18070 Roo.View = function(config, depreciated_tpl, depreciated_config){
18071     
18072     this.parent = false;
18073     
18074     if (typeof(depreciated_tpl) == 'undefined') {
18075         // new way.. - universal constructor.
18076         Roo.apply(this, config);
18077         this.el  = Roo.get(this.el);
18078     } else {
18079         // old format..
18080         this.el  = Roo.get(config);
18081         this.tpl = depreciated_tpl;
18082         Roo.apply(this, depreciated_config);
18083     }
18084     this.wrapEl  = this.el.wrap().wrap();
18085     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18086     
18087     
18088     if(typeof(this.tpl) == "string"){
18089         this.tpl = new Roo.Template(this.tpl);
18090     } else {
18091         // support xtype ctors..
18092         this.tpl = new Roo.factory(this.tpl, Roo);
18093     }
18094     
18095     
18096     this.tpl.compile();
18097     
18098     /** @private */
18099     this.addEvents({
18100         /**
18101          * @event beforeclick
18102          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
18109         /**
18110          * @event click
18111          * Fires when a template node is 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             "click" : true,
18118         /**
18119          * @event dblclick
18120          * Fires when a template node is double 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             "dblclick" : true,
18127         /**
18128          * @event contextmenu
18129          * Fires when a template node is right clicked.
18130          * @param {Roo.View} this
18131          * @param {Number} index The index of the target node
18132          * @param {HTMLElement} node The target node
18133          * @param {Roo.EventObject} e The raw event object
18134          */
18135             "contextmenu" : true,
18136         /**
18137          * @event selectionchange
18138          * Fires when the selected nodes change.
18139          * @param {Roo.View} this
18140          * @param {Array} selections Array of the selected nodes
18141          */
18142             "selectionchange" : true,
18143     
18144         /**
18145          * @event beforeselect
18146          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18147          * @param {Roo.View} this
18148          * @param {HTMLElement} node The node to be selected
18149          * @param {Array} selections Array of currently selected nodes
18150          */
18151             "beforeselect" : true,
18152         /**
18153          * @event preparedata
18154          * Fires on every row to render, to allow you to change the data.
18155          * @param {Roo.View} this
18156          * @param {Object} data to be rendered (change this)
18157          */
18158           "preparedata" : true
18159           
18160           
18161         });
18162
18163
18164
18165     this.el.on({
18166         "click": this.onClick,
18167         "dblclick": this.onDblClick,
18168         "contextmenu": this.onContextMenu,
18169         scope:this
18170     });
18171
18172     this.selections = [];
18173     this.nodes = [];
18174     this.cmp = new Roo.CompositeElementLite([]);
18175     if(this.store){
18176         this.store = Roo.factory(this.store, Roo.data);
18177         this.setStore(this.store, true);
18178     }
18179     
18180     if ( this.footer && this.footer.xtype) {
18181            
18182          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18183         
18184         this.footer.dataSource = this.store;
18185         this.footer.container = fctr;
18186         this.footer = Roo.factory(this.footer, Roo);
18187         fctr.insertFirst(this.el);
18188         
18189         // this is a bit insane - as the paging toolbar seems to detach the el..
18190 //        dom.parentNode.parentNode.parentNode
18191          // they get detached?
18192     }
18193     
18194     
18195     Roo.View.superclass.constructor.call(this);
18196     
18197     
18198 };
18199
18200 Roo.extend(Roo.View, Roo.util.Observable, {
18201     
18202      /**
18203      * @cfg {Roo.data.Store} store Data store to load data from.
18204      */
18205     store : false,
18206     
18207     /**
18208      * @cfg {String|Roo.Element} el The container element.
18209      */
18210     el : '',
18211     
18212     /**
18213      * @cfg {String|Roo.Template} tpl The template used by this View 
18214      */
18215     tpl : false,
18216     /**
18217      * @cfg {String} dataName the named area of the template to use as the data area
18218      *                          Works with domtemplates roo-name="name"
18219      */
18220     dataName: false,
18221     /**
18222      * @cfg {String} selectedClass The css class to add to selected nodes
18223      */
18224     selectedClass : "x-view-selected",
18225      /**
18226      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18227      */
18228     emptyText : "",
18229     
18230     /**
18231      * @cfg {String} text to display on mask (default Loading)
18232      */
18233     mask : false,
18234     /**
18235      * @cfg {Boolean} multiSelect Allow multiple selection
18236      */
18237     multiSelect : false,
18238     /**
18239      * @cfg {Boolean} singleSelect Allow single selection
18240      */
18241     singleSelect:  false,
18242     
18243     /**
18244      * @cfg {Boolean} toggleSelect - selecting 
18245      */
18246     toggleSelect : false,
18247     
18248     /**
18249      * @cfg {Boolean} tickable - selecting 
18250      */
18251     tickable : false,
18252     
18253     /**
18254      * Returns the element this view is bound to.
18255      * @return {Roo.Element}
18256      */
18257     getEl : function(){
18258         return this.wrapEl;
18259     },
18260     
18261     
18262
18263     /**
18264      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18265      */
18266     refresh : function(){
18267         //Roo.log('refresh');
18268         var t = this.tpl;
18269         
18270         // if we are using something like 'domtemplate', then
18271         // the what gets used is:
18272         // t.applySubtemplate(NAME, data, wrapping data..)
18273         // the outer template then get' applied with
18274         //     the store 'extra data'
18275         // and the body get's added to the
18276         //      roo-name="data" node?
18277         //      <span class='roo-tpl-{name}'></span> ?????
18278         
18279         
18280         
18281         this.clearSelections();
18282         this.el.update("");
18283         var html = [];
18284         var records = this.store.getRange();
18285         if(records.length < 1) {
18286             
18287             // is this valid??  = should it render a template??
18288             
18289             this.el.update(this.emptyText);
18290             return;
18291         }
18292         var el = this.el;
18293         if (this.dataName) {
18294             this.el.update(t.apply(this.store.meta)); //????
18295             el = this.el.child('.roo-tpl-' + this.dataName);
18296         }
18297         
18298         for(var i = 0, len = records.length; i < len; i++){
18299             var data = this.prepareData(records[i].data, i, records[i]);
18300             this.fireEvent("preparedata", this, data, i, records[i]);
18301             
18302             var d = Roo.apply({}, data);
18303             
18304             if(this.tickable){
18305                 Roo.apply(d, {'roo-id' : Roo.id()});
18306                 
18307                 var _this = this;
18308             
18309                 Roo.each(this.parent.item, function(item){
18310                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18311                         return;
18312                     }
18313                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18314                 });
18315             }
18316             
18317             html[html.length] = Roo.util.Format.trim(
18318                 this.dataName ?
18319                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18320                     t.apply(d)
18321             );
18322         }
18323         
18324         
18325         
18326         el.update(html.join(""));
18327         this.nodes = el.dom.childNodes;
18328         this.updateIndexes(0);
18329     },
18330     
18331
18332     /**
18333      * Function to override to reformat the data that is sent to
18334      * the template for each node.
18335      * DEPRICATED - use the preparedata event handler.
18336      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18337      * a JSON object for an UpdateManager bound view).
18338      */
18339     prepareData : function(data, index, record)
18340     {
18341         this.fireEvent("preparedata", this, data, index, record);
18342         return data;
18343     },
18344
18345     onUpdate : function(ds, record){
18346         // Roo.log('on update');   
18347         this.clearSelections();
18348         var index = this.store.indexOf(record);
18349         var n = this.nodes[index];
18350         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18351         n.parentNode.removeChild(n);
18352         this.updateIndexes(index, index);
18353     },
18354
18355     
18356     
18357 // --------- FIXME     
18358     onAdd : function(ds, records, index)
18359     {
18360         //Roo.log(['on Add', ds, records, index] );        
18361         this.clearSelections();
18362         if(this.nodes.length == 0){
18363             this.refresh();
18364             return;
18365         }
18366         var n = this.nodes[index];
18367         for(var i = 0, len = records.length; i < len; i++){
18368             var d = this.prepareData(records[i].data, i, records[i]);
18369             if(n){
18370                 this.tpl.insertBefore(n, d);
18371             }else{
18372                 
18373                 this.tpl.append(this.el, d);
18374             }
18375         }
18376         this.updateIndexes(index);
18377     },
18378
18379     onRemove : function(ds, record, index){
18380        // Roo.log('onRemove');
18381         this.clearSelections();
18382         var el = this.dataName  ?
18383             this.el.child('.roo-tpl-' + this.dataName) :
18384             this.el; 
18385         
18386         el.dom.removeChild(this.nodes[index]);
18387         this.updateIndexes(index);
18388     },
18389
18390     /**
18391      * Refresh an individual node.
18392      * @param {Number} index
18393      */
18394     refreshNode : function(index){
18395         this.onUpdate(this.store, this.store.getAt(index));
18396     },
18397
18398     updateIndexes : function(startIndex, endIndex){
18399         var ns = this.nodes;
18400         startIndex = startIndex || 0;
18401         endIndex = endIndex || ns.length - 1;
18402         for(var i = startIndex; i <= endIndex; i++){
18403             ns[i].nodeIndex = i;
18404         }
18405     },
18406
18407     /**
18408      * Changes the data store this view uses and refresh the view.
18409      * @param {Store} store
18410      */
18411     setStore : function(store, initial){
18412         if(!initial && this.store){
18413             this.store.un("datachanged", this.refresh);
18414             this.store.un("add", this.onAdd);
18415             this.store.un("remove", this.onRemove);
18416             this.store.un("update", this.onUpdate);
18417             this.store.un("clear", this.refresh);
18418             this.store.un("beforeload", this.onBeforeLoad);
18419             this.store.un("load", this.onLoad);
18420             this.store.un("loadexception", this.onLoad);
18421         }
18422         if(store){
18423           
18424             store.on("datachanged", this.refresh, this);
18425             store.on("add", this.onAdd, this);
18426             store.on("remove", this.onRemove, this);
18427             store.on("update", this.onUpdate, this);
18428             store.on("clear", this.refresh, this);
18429             store.on("beforeload", this.onBeforeLoad, this);
18430             store.on("load", this.onLoad, this);
18431             store.on("loadexception", this.onLoad, this);
18432         }
18433         
18434         if(store){
18435             this.refresh();
18436         }
18437     },
18438     /**
18439      * onbeforeLoad - masks the loading area.
18440      *
18441      */
18442     onBeforeLoad : function(store,opts)
18443     {
18444          //Roo.log('onBeforeLoad');   
18445         if (!opts.add) {
18446             this.el.update("");
18447         }
18448         this.el.mask(this.mask ? this.mask : "Loading" ); 
18449     },
18450     onLoad : function ()
18451     {
18452         this.el.unmask();
18453     },
18454     
18455
18456     /**
18457      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18458      * @param {HTMLElement} node
18459      * @return {HTMLElement} The template node
18460      */
18461     findItemFromChild : function(node){
18462         var el = this.dataName  ?
18463             this.el.child('.roo-tpl-' + this.dataName,true) :
18464             this.el.dom; 
18465         
18466         if(!node || node.parentNode == el){
18467                     return node;
18468             }
18469             var p = node.parentNode;
18470             while(p && p != el){
18471             if(p.parentNode == el){
18472                 return p;
18473             }
18474             p = p.parentNode;
18475         }
18476             return null;
18477     },
18478
18479     /** @ignore */
18480     onClick : function(e){
18481         var item = this.findItemFromChild(e.getTarget());
18482         if(item){
18483             var index = this.indexOf(item);
18484             if(this.onItemClick(item, index, e) !== false){
18485                 this.fireEvent("click", this, index, item, e);
18486             }
18487         }else{
18488             this.clearSelections();
18489         }
18490     },
18491
18492     /** @ignore */
18493     onContextMenu : function(e){
18494         var item = this.findItemFromChild(e.getTarget());
18495         if(item){
18496             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18497         }
18498     },
18499
18500     /** @ignore */
18501     onDblClick : function(e){
18502         var item = this.findItemFromChild(e.getTarget());
18503         if(item){
18504             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18505         }
18506     },
18507
18508     onItemClick : function(item, index, e)
18509     {
18510         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18511             return false;
18512         }
18513         if (this.toggleSelect) {
18514             var m = this.isSelected(item) ? 'unselect' : 'select';
18515             //Roo.log(m);
18516             var _t = this;
18517             _t[m](item, true, false);
18518             return true;
18519         }
18520         if(this.multiSelect || this.singleSelect){
18521             if(this.multiSelect && e.shiftKey && this.lastSelection){
18522                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18523             }else{
18524                 this.select(item, this.multiSelect && e.ctrlKey);
18525                 this.lastSelection = item;
18526             }
18527             
18528             if(!this.tickable){
18529                 e.preventDefault();
18530             }
18531             
18532         }
18533         return true;
18534     },
18535
18536     /**
18537      * Get the number of selected nodes.
18538      * @return {Number}
18539      */
18540     getSelectionCount : function(){
18541         return this.selections.length;
18542     },
18543
18544     /**
18545      * Get the currently selected nodes.
18546      * @return {Array} An array of HTMLElements
18547      */
18548     getSelectedNodes : function(){
18549         return this.selections;
18550     },
18551
18552     /**
18553      * Get the indexes of the selected nodes.
18554      * @return {Array}
18555      */
18556     getSelectedIndexes : function(){
18557         var indexes = [], s = this.selections;
18558         for(var i = 0, len = s.length; i < len; i++){
18559             indexes.push(s[i].nodeIndex);
18560         }
18561         return indexes;
18562     },
18563
18564     /**
18565      * Clear all selections
18566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18567      */
18568     clearSelections : function(suppressEvent){
18569         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18570             this.cmp.elements = this.selections;
18571             this.cmp.removeClass(this.selectedClass);
18572             this.selections = [];
18573             if(!suppressEvent){
18574                 this.fireEvent("selectionchange", this, this.selections);
18575             }
18576         }
18577     },
18578
18579     /**
18580      * Returns true if the passed node is selected
18581      * @param {HTMLElement/Number} node The node or node index
18582      * @return {Boolean}
18583      */
18584     isSelected : function(node){
18585         var s = this.selections;
18586         if(s.length < 1){
18587             return false;
18588         }
18589         node = this.getNode(node);
18590         return s.indexOf(node) !== -1;
18591     },
18592
18593     /**
18594      * Selects nodes.
18595      * @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
18596      * @param {Boolean} keepExisting (optional) true to keep existing selections
18597      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18598      */
18599     select : function(nodeInfo, keepExisting, suppressEvent){
18600         if(nodeInfo instanceof Array){
18601             if(!keepExisting){
18602                 this.clearSelections(true);
18603             }
18604             for(var i = 0, len = nodeInfo.length; i < len; i++){
18605                 this.select(nodeInfo[i], true, true);
18606             }
18607             return;
18608         } 
18609         var node = this.getNode(nodeInfo);
18610         if(!node || this.isSelected(node)){
18611             return; // already selected.
18612         }
18613         if(!keepExisting){
18614             this.clearSelections(true);
18615         }
18616         
18617         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18618             Roo.fly(node).addClass(this.selectedClass);
18619             this.selections.push(node);
18620             if(!suppressEvent){
18621                 this.fireEvent("selectionchange", this, this.selections);
18622             }
18623         }
18624         
18625         
18626     },
18627       /**
18628      * Unselects nodes.
18629      * @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
18630      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18631      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18632      */
18633     unselect : function(nodeInfo, keepExisting, suppressEvent)
18634     {
18635         if(nodeInfo instanceof Array){
18636             Roo.each(this.selections, function(s) {
18637                 this.unselect(s, nodeInfo);
18638             }, this);
18639             return;
18640         }
18641         var node = this.getNode(nodeInfo);
18642         if(!node || !this.isSelected(node)){
18643             //Roo.log("not selected");
18644             return; // not selected.
18645         }
18646         // fireevent???
18647         var ns = [];
18648         Roo.each(this.selections, function(s) {
18649             if (s == node ) {
18650                 Roo.fly(node).removeClass(this.selectedClass);
18651
18652                 return;
18653             }
18654             ns.push(s);
18655         },this);
18656         
18657         this.selections= ns;
18658         this.fireEvent("selectionchange", this, this.selections);
18659     },
18660
18661     /**
18662      * Gets a template node.
18663      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18664      * @return {HTMLElement} The node or null if it wasn't found
18665      */
18666     getNode : function(nodeInfo){
18667         if(typeof nodeInfo == "string"){
18668             return document.getElementById(nodeInfo);
18669         }else if(typeof nodeInfo == "number"){
18670             return this.nodes[nodeInfo];
18671         }
18672         return nodeInfo;
18673     },
18674
18675     /**
18676      * Gets a range template nodes.
18677      * @param {Number} startIndex
18678      * @param {Number} endIndex
18679      * @return {Array} An array of nodes
18680      */
18681     getNodes : function(start, end){
18682         var ns = this.nodes;
18683         start = start || 0;
18684         end = typeof end == "undefined" ? ns.length - 1 : end;
18685         var nodes = [];
18686         if(start <= end){
18687             for(var i = start; i <= end; i++){
18688                 nodes.push(ns[i]);
18689             }
18690         } else{
18691             for(var i = start; i >= end; i--){
18692                 nodes.push(ns[i]);
18693             }
18694         }
18695         return nodes;
18696     },
18697
18698     /**
18699      * Finds the index of the passed node
18700      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18701      * @return {Number} The index of the node or -1
18702      */
18703     indexOf : function(node){
18704         node = this.getNode(node);
18705         if(typeof node.nodeIndex == "number"){
18706             return node.nodeIndex;
18707         }
18708         var ns = this.nodes;
18709         for(var i = 0, len = ns.length; i < len; i++){
18710             if(ns[i] == node){
18711                 return i;
18712             }
18713         }
18714         return -1;
18715     }
18716 });
18717 /*
18718  * - LGPL
18719  *
18720  * based on jquery fullcalendar
18721  * 
18722  */
18723
18724 Roo.bootstrap = Roo.bootstrap || {};
18725 /**
18726  * @class Roo.bootstrap.Calendar
18727  * @extends Roo.bootstrap.Component
18728  * Bootstrap Calendar class
18729  * @cfg {Boolean} loadMask (true|false) default false
18730  * @cfg {Object} header generate the user specific header of the calendar, default false
18731
18732  * @constructor
18733  * Create a new Container
18734  * @param {Object} config The config object
18735  */
18736
18737
18738
18739 Roo.bootstrap.Calendar = function(config){
18740     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18741      this.addEvents({
18742         /**
18743              * @event select
18744              * Fires when a date is selected
18745              * @param {DatePicker} this
18746              * @param {Date} date The selected date
18747              */
18748         'select': true,
18749         /**
18750              * @event monthchange
18751              * Fires when the displayed month changes 
18752              * @param {DatePicker} this
18753              * @param {Date} date The selected month
18754              */
18755         'monthchange': true,
18756         /**
18757              * @event evententer
18758              * Fires when mouse over an event
18759              * @param {Calendar} this
18760              * @param {event} Event
18761              */
18762         'evententer': true,
18763         /**
18764              * @event eventleave
18765              * Fires when the mouse leaves an
18766              * @param {Calendar} this
18767              * @param {event}
18768              */
18769         'eventleave': true,
18770         /**
18771              * @event eventclick
18772              * Fires when the mouse click an
18773              * @param {Calendar} this
18774              * @param {event}
18775              */
18776         'eventclick': true
18777         
18778     });
18779
18780 };
18781
18782 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18783     
18784      /**
18785      * @cfg {Number} startDay
18786      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18787      */
18788     startDay : 0,
18789     
18790     loadMask : false,
18791     
18792     header : false,
18793       
18794     getAutoCreate : function(){
18795         
18796         
18797         var fc_button = function(name, corner, style, content ) {
18798             return Roo.apply({},{
18799                 tag : 'span',
18800                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18801                          (corner.length ?
18802                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18803                             ''
18804                         ),
18805                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18806                 unselectable: 'on'
18807             });
18808         };
18809         
18810         var header = {};
18811         
18812         if(!this.header){
18813             header = {
18814                 tag : 'table',
18815                 cls : 'fc-header',
18816                 style : 'width:100%',
18817                 cn : [
18818                     {
18819                         tag: 'tr',
18820                         cn : [
18821                             {
18822                                 tag : 'td',
18823                                 cls : 'fc-header-left',
18824                                 cn : [
18825                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18826                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18827                                     { tag: 'span', cls: 'fc-header-space' },
18828                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18829
18830
18831                                 ]
18832                             },
18833
18834                             {
18835                                 tag : 'td',
18836                                 cls : 'fc-header-center',
18837                                 cn : [
18838                                     {
18839                                         tag: 'span',
18840                                         cls: 'fc-header-title',
18841                                         cn : {
18842                                             tag: 'H2',
18843                                             html : 'month / year'
18844                                         }
18845                                     }
18846
18847                                 ]
18848                             },
18849                             {
18850                                 tag : 'td',
18851                                 cls : 'fc-header-right',
18852                                 cn : [
18853                               /*      fc_button('month', 'left', '', 'month' ),
18854                                     fc_button('week', '', '', 'week' ),
18855                                     fc_button('day', 'right', '', 'day' )
18856                                 */    
18857
18858                                 ]
18859                             }
18860
18861                         ]
18862                     }
18863                 ]
18864             };
18865         }
18866         
18867         header = this.header;
18868         
18869        
18870         var cal_heads = function() {
18871             var ret = [];
18872             // fixme - handle this.
18873             
18874             for (var i =0; i < Date.dayNames.length; i++) {
18875                 var d = Date.dayNames[i];
18876                 ret.push({
18877                     tag: 'th',
18878                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18879                     html : d.substring(0,3)
18880                 });
18881                 
18882             }
18883             ret[0].cls += ' fc-first';
18884             ret[6].cls += ' fc-last';
18885             return ret;
18886         };
18887         var cal_cell = function(n) {
18888             return  {
18889                 tag: 'td',
18890                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18891                 cn : [
18892                     {
18893                         cn : [
18894                             {
18895                                 cls: 'fc-day-number',
18896                                 html: 'D'
18897                             },
18898                             {
18899                                 cls: 'fc-day-content',
18900                              
18901                                 cn : [
18902                                      {
18903                                         style: 'position: relative;' // height: 17px;
18904                                     }
18905                                 ]
18906                             }
18907                             
18908                             
18909                         ]
18910                     }
18911                 ]
18912                 
18913             }
18914         };
18915         var cal_rows = function() {
18916             
18917             var ret = [];
18918             for (var r = 0; r < 6; r++) {
18919                 var row= {
18920                     tag : 'tr',
18921                     cls : 'fc-week',
18922                     cn : []
18923                 };
18924                 
18925                 for (var i =0; i < Date.dayNames.length; i++) {
18926                     var d = Date.dayNames[i];
18927                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18928
18929                 }
18930                 row.cn[0].cls+=' fc-first';
18931                 row.cn[0].cn[0].style = 'min-height:90px';
18932                 row.cn[6].cls+=' fc-last';
18933                 ret.push(row);
18934                 
18935             }
18936             ret[0].cls += ' fc-first';
18937             ret[4].cls += ' fc-prev-last';
18938             ret[5].cls += ' fc-last';
18939             return ret;
18940             
18941         };
18942         
18943         var cal_table = {
18944             tag: 'table',
18945             cls: 'fc-border-separate',
18946             style : 'width:100%',
18947             cellspacing  : 0,
18948             cn : [
18949                 { 
18950                     tag: 'thead',
18951                     cn : [
18952                         { 
18953                             tag: 'tr',
18954                             cls : 'fc-first fc-last',
18955                             cn : cal_heads()
18956                         }
18957                     ]
18958                 },
18959                 { 
18960                     tag: 'tbody',
18961                     cn : cal_rows()
18962                 }
18963                   
18964             ]
18965         };
18966          
18967          var cfg = {
18968             cls : 'fc fc-ltr',
18969             cn : [
18970                 header,
18971                 {
18972                     cls : 'fc-content',
18973                     style : "position: relative;",
18974                     cn : [
18975                         {
18976                             cls : 'fc-view fc-view-month fc-grid',
18977                             style : 'position: relative',
18978                             unselectable : 'on',
18979                             cn : [
18980                                 {
18981                                     cls : 'fc-event-container',
18982                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18983                                 },
18984                                 cal_table
18985                             ]
18986                         }
18987                     ]
18988     
18989                 }
18990            ] 
18991             
18992         };
18993         
18994          
18995         
18996         return cfg;
18997     },
18998     
18999     
19000     initEvents : function()
19001     {
19002         if(!this.store){
19003             throw "can not find store for calendar";
19004         }
19005         
19006         var mark = {
19007             tag: "div",
19008             cls:"x-dlg-mask",
19009             style: "text-align:center",
19010             cn: [
19011                 {
19012                     tag: "div",
19013                     style: "background-color:white;width:50%;margin:250 auto",
19014                     cn: [
19015                         {
19016                             tag: "img",
19017                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19018                         },
19019                         {
19020                             tag: "span",
19021                             html: "Loading"
19022                         }
19023                         
19024                     ]
19025                 }
19026             ]
19027         };
19028         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19029         
19030         var size = this.el.select('.fc-content', true).first().getSize();
19031         this.maskEl.setSize(size.width, size.height);
19032         this.maskEl.enableDisplayMode("block");
19033         if(!this.loadMask){
19034             this.maskEl.hide();
19035         }
19036         
19037         this.store = Roo.factory(this.store, Roo.data);
19038         this.store.on('load', this.onLoad, this);
19039         this.store.on('beforeload', this.onBeforeLoad, this);
19040         
19041         this.resize();
19042         
19043         this.cells = this.el.select('.fc-day',true);
19044         //Roo.log(this.cells);
19045         this.textNodes = this.el.query('.fc-day-number');
19046         this.cells.addClassOnOver('fc-state-hover');
19047         
19048         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19049         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19050         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19051         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19052         
19053         this.on('monthchange', this.onMonthChange, this);
19054         
19055         this.update(new Date().clearTime());
19056     },
19057     
19058     resize : function() {
19059         var sz  = this.el.getSize();
19060         
19061         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19062         this.el.select('.fc-day-content div',true).setHeight(34);
19063     },
19064     
19065     
19066     // private
19067     showPrevMonth : function(e){
19068         this.update(this.activeDate.add("mo", -1));
19069     },
19070     showToday : function(e){
19071         this.update(new Date().clearTime());
19072     },
19073     // private
19074     showNextMonth : function(e){
19075         this.update(this.activeDate.add("mo", 1));
19076     },
19077
19078     // private
19079     showPrevYear : function(){
19080         this.update(this.activeDate.add("y", -1));
19081     },
19082
19083     // private
19084     showNextYear : function(){
19085         this.update(this.activeDate.add("y", 1));
19086     },
19087
19088     
19089    // private
19090     update : function(date)
19091     {
19092         var vd = this.activeDate;
19093         this.activeDate = date;
19094 //        if(vd && this.el){
19095 //            var t = date.getTime();
19096 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19097 //                Roo.log('using add remove');
19098 //                
19099 //                this.fireEvent('monthchange', this, date);
19100 //                
19101 //                this.cells.removeClass("fc-state-highlight");
19102 //                this.cells.each(function(c){
19103 //                   if(c.dateValue == t){
19104 //                       c.addClass("fc-state-highlight");
19105 //                       setTimeout(function(){
19106 //                            try{c.dom.firstChild.focus();}catch(e){}
19107 //                       }, 50);
19108 //                       return false;
19109 //                   }
19110 //                   return true;
19111 //                });
19112 //                return;
19113 //            }
19114 //        }
19115         
19116         var days = date.getDaysInMonth();
19117         
19118         var firstOfMonth = date.getFirstDateOfMonth();
19119         var startingPos = firstOfMonth.getDay()-this.startDay;
19120         
19121         if(startingPos < this.startDay){
19122             startingPos += 7;
19123         }
19124         
19125         var pm = date.add(Date.MONTH, -1);
19126         var prevStart = pm.getDaysInMonth()-startingPos;
19127 //        
19128         this.cells = this.el.select('.fc-day',true);
19129         this.textNodes = this.el.query('.fc-day-number');
19130         this.cells.addClassOnOver('fc-state-hover');
19131         
19132         var cells = this.cells.elements;
19133         var textEls = this.textNodes;
19134         
19135         Roo.each(cells, function(cell){
19136             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19137         });
19138         
19139         days += startingPos;
19140
19141         // convert everything to numbers so it's fast
19142         var day = 86400000;
19143         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19144         //Roo.log(d);
19145         //Roo.log(pm);
19146         //Roo.log(prevStart);
19147         
19148         var today = new Date().clearTime().getTime();
19149         var sel = date.clearTime().getTime();
19150         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19151         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19152         var ddMatch = this.disabledDatesRE;
19153         var ddText = this.disabledDatesText;
19154         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19155         var ddaysText = this.disabledDaysText;
19156         var format = this.format;
19157         
19158         var setCellClass = function(cal, cell){
19159             cell.row = 0;
19160             cell.events = [];
19161             cell.more = [];
19162             //Roo.log('set Cell Class');
19163             cell.title = "";
19164             var t = d.getTime();
19165             
19166             //Roo.log(d);
19167             
19168             cell.dateValue = t;
19169             if(t == today){
19170                 cell.className += " fc-today";
19171                 cell.className += " fc-state-highlight";
19172                 cell.title = cal.todayText;
19173             }
19174             if(t == sel){
19175                 // disable highlight in other month..
19176                 //cell.className += " fc-state-highlight";
19177                 
19178             }
19179             // disabling
19180             if(t < min) {
19181                 cell.className = " fc-state-disabled";
19182                 cell.title = cal.minText;
19183                 return;
19184             }
19185             if(t > max) {
19186                 cell.className = " fc-state-disabled";
19187                 cell.title = cal.maxText;
19188                 return;
19189             }
19190             if(ddays){
19191                 if(ddays.indexOf(d.getDay()) != -1){
19192                     cell.title = ddaysText;
19193                     cell.className = " fc-state-disabled";
19194                 }
19195             }
19196             if(ddMatch && format){
19197                 var fvalue = d.dateFormat(format);
19198                 if(ddMatch.test(fvalue)){
19199                     cell.title = ddText.replace("%0", fvalue);
19200                     cell.className = " fc-state-disabled";
19201                 }
19202             }
19203             
19204             if (!cell.initialClassName) {
19205                 cell.initialClassName = cell.dom.className;
19206             }
19207             
19208             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19209         };
19210
19211         var i = 0;
19212         
19213         for(; i < startingPos; i++) {
19214             textEls[i].innerHTML = (++prevStart);
19215             d.setDate(d.getDate()+1);
19216             
19217             cells[i].className = "fc-past fc-other-month";
19218             setCellClass(this, cells[i]);
19219         }
19220         
19221         var intDay = 0;
19222         
19223         for(; i < days; i++){
19224             intDay = i - startingPos + 1;
19225             textEls[i].innerHTML = (intDay);
19226             d.setDate(d.getDate()+1);
19227             
19228             cells[i].className = ''; // "x-date-active";
19229             setCellClass(this, cells[i]);
19230         }
19231         var extraDays = 0;
19232         
19233         for(; i < 42; i++) {
19234             textEls[i].innerHTML = (++extraDays);
19235             d.setDate(d.getDate()+1);
19236             
19237             cells[i].className = "fc-future fc-other-month";
19238             setCellClass(this, cells[i]);
19239         }
19240         
19241         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19242         
19243         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19244         
19245         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19246         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19247         
19248         if(totalRows != 6){
19249             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19250             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19251         }
19252         
19253         this.fireEvent('monthchange', this, date);
19254         
19255         
19256         /*
19257         if(!this.internalRender){
19258             var main = this.el.dom.firstChild;
19259             var w = main.offsetWidth;
19260             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19261             Roo.fly(main).setWidth(w);
19262             this.internalRender = true;
19263             // opera does not respect the auto grow header center column
19264             // then, after it gets a width opera refuses to recalculate
19265             // without a second pass
19266             if(Roo.isOpera && !this.secondPass){
19267                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19268                 this.secondPass = true;
19269                 this.update.defer(10, this, [date]);
19270             }
19271         }
19272         */
19273         
19274     },
19275     
19276     findCell : function(dt) {
19277         dt = dt.clearTime().getTime();
19278         var ret = false;
19279         this.cells.each(function(c){
19280             //Roo.log("check " +c.dateValue + '?=' + dt);
19281             if(c.dateValue == dt){
19282                 ret = c;
19283                 return false;
19284             }
19285             return true;
19286         });
19287         
19288         return ret;
19289     },
19290     
19291     findCells : function(ev) {
19292         var s = ev.start.clone().clearTime().getTime();
19293        // Roo.log(s);
19294         var e= ev.end.clone().clearTime().getTime();
19295        // Roo.log(e);
19296         var ret = [];
19297         this.cells.each(function(c){
19298              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19299             
19300             if(c.dateValue > e){
19301                 return ;
19302             }
19303             if(c.dateValue < s){
19304                 return ;
19305             }
19306             ret.push(c);
19307         });
19308         
19309         return ret;    
19310     },
19311     
19312 //    findBestRow: function(cells)
19313 //    {
19314 //        var ret = 0;
19315 //        
19316 //        for (var i =0 ; i < cells.length;i++) {
19317 //            ret  = Math.max(cells[i].rows || 0,ret);
19318 //        }
19319 //        return ret;
19320 //        
19321 //    },
19322     
19323     
19324     addItem : function(ev)
19325     {
19326         // look for vertical location slot in
19327         var cells = this.findCells(ev);
19328         
19329 //        ev.row = this.findBestRow(cells);
19330         
19331         // work out the location.
19332         
19333         var crow = false;
19334         var rows = [];
19335         for(var i =0; i < cells.length; i++) {
19336             
19337             cells[i].row = cells[0].row;
19338             
19339             if(i == 0){
19340                 cells[i].row = cells[i].row + 1;
19341             }
19342             
19343             if (!crow) {
19344                 crow = {
19345                     start : cells[i],
19346                     end :  cells[i]
19347                 };
19348                 continue;
19349             }
19350             if (crow.start.getY() == cells[i].getY()) {
19351                 // on same row.
19352                 crow.end = cells[i];
19353                 continue;
19354             }
19355             // different row.
19356             rows.push(crow);
19357             crow = {
19358                 start: cells[i],
19359                 end : cells[i]
19360             };
19361             
19362         }
19363         
19364         rows.push(crow);
19365         ev.els = [];
19366         ev.rows = rows;
19367         ev.cells = cells;
19368         
19369         cells[0].events.push(ev);
19370         
19371         this.calevents.push(ev);
19372     },
19373     
19374     clearEvents: function() {
19375         
19376         if(!this.calevents){
19377             return;
19378         }
19379         
19380         Roo.each(this.cells.elements, function(c){
19381             c.row = 0;
19382             c.events = [];
19383             c.more = [];
19384         });
19385         
19386         Roo.each(this.calevents, function(e) {
19387             Roo.each(e.els, function(el) {
19388                 el.un('mouseenter' ,this.onEventEnter, this);
19389                 el.un('mouseleave' ,this.onEventLeave, this);
19390                 el.remove();
19391             },this);
19392         },this);
19393         
19394         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19395             e.remove();
19396         });
19397         
19398     },
19399     
19400     renderEvents: function()
19401     {   
19402         var _this = this;
19403         
19404         this.cells.each(function(c) {
19405             
19406             if(c.row < 5){
19407                 return;
19408             }
19409             
19410             var ev = c.events;
19411             
19412             var r = 4;
19413             if(c.row != c.events.length){
19414                 r = 4 - (4 - (c.row - c.events.length));
19415             }
19416             
19417             c.events = ev.slice(0, r);
19418             c.more = ev.slice(r);
19419             
19420             if(c.more.length && c.more.length == 1){
19421                 c.events.push(c.more.pop());
19422             }
19423             
19424             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19425             
19426         });
19427             
19428         this.cells.each(function(c) {
19429             
19430             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19431             
19432             
19433             for (var e = 0; e < c.events.length; e++){
19434                 var ev = c.events[e];
19435                 var rows = ev.rows;
19436                 
19437                 for(var i = 0; i < rows.length; i++) {
19438                 
19439                     // how many rows should it span..
19440
19441                     var  cfg = {
19442                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19443                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19444
19445                         unselectable : "on",
19446                         cn : [
19447                             {
19448                                 cls: 'fc-event-inner',
19449                                 cn : [
19450     //                                {
19451     //                                  tag:'span',
19452     //                                  cls: 'fc-event-time',
19453     //                                  html : cells.length > 1 ? '' : ev.time
19454     //                                },
19455                                     {
19456                                       tag:'span',
19457                                       cls: 'fc-event-title',
19458                                       html : String.format('{0}', ev.title)
19459                                     }
19460
19461
19462                                 ]
19463                             },
19464                             {
19465                                 cls: 'ui-resizable-handle ui-resizable-e',
19466                                 html : '&nbsp;&nbsp;&nbsp'
19467                             }
19468
19469                         ]
19470                     };
19471
19472                     if (i == 0) {
19473                         cfg.cls += ' fc-event-start';
19474                     }
19475                     if ((i+1) == rows.length) {
19476                         cfg.cls += ' fc-event-end';
19477                     }
19478
19479                     var ctr = _this.el.select('.fc-event-container',true).first();
19480                     var cg = ctr.createChild(cfg);
19481
19482                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19483                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19484
19485                     var r = (c.more.length) ? 1 : 0;
19486                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19487                     cg.setWidth(ebox.right - sbox.x -2);
19488
19489                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19490                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19491                     cg.on('click', _this.onEventClick, _this, ev);
19492
19493                     ev.els.push(cg);
19494                     
19495                 }
19496                 
19497             }
19498             
19499             
19500             if(c.more.length){
19501                 var  cfg = {
19502                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19503                     style : 'position: absolute',
19504                     unselectable : "on",
19505                     cn : [
19506                         {
19507                             cls: 'fc-event-inner',
19508                             cn : [
19509                                 {
19510                                   tag:'span',
19511                                   cls: 'fc-event-title',
19512                                   html : 'More'
19513                                 }
19514
19515
19516                             ]
19517                         },
19518                         {
19519                             cls: 'ui-resizable-handle ui-resizable-e',
19520                             html : '&nbsp;&nbsp;&nbsp'
19521                         }
19522
19523                     ]
19524                 };
19525
19526                 var ctr = _this.el.select('.fc-event-container',true).first();
19527                 var cg = ctr.createChild(cfg);
19528
19529                 var sbox = c.select('.fc-day-content',true).first().getBox();
19530                 var ebox = c.select('.fc-day-content',true).first().getBox();
19531                 //Roo.log(cg);
19532                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19533                 cg.setWidth(ebox.right - sbox.x -2);
19534
19535                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19536                 
19537             }
19538             
19539         });
19540         
19541         
19542         
19543     },
19544     
19545     onEventEnter: function (e, el,event,d) {
19546         this.fireEvent('evententer', this, el, event);
19547     },
19548     
19549     onEventLeave: function (e, el,event,d) {
19550         this.fireEvent('eventleave', this, el, event);
19551     },
19552     
19553     onEventClick: function (e, el,event,d) {
19554         this.fireEvent('eventclick', this, el, event);
19555     },
19556     
19557     onMonthChange: function () {
19558         this.store.load();
19559     },
19560     
19561     onMoreEventClick: function(e, el, more)
19562     {
19563         var _this = this;
19564         
19565         this.calpopover.placement = 'right';
19566         this.calpopover.setTitle('More');
19567         
19568         this.calpopover.setContent('');
19569         
19570         var ctr = this.calpopover.el.select('.popover-content', true).first();
19571         
19572         Roo.each(more, function(m){
19573             var cfg = {
19574                 cls : 'fc-event-hori fc-event-draggable',
19575                 html : m.title
19576             };
19577             var cg = ctr.createChild(cfg);
19578             
19579             cg.on('click', _this.onEventClick, _this, m);
19580         });
19581         
19582         this.calpopover.show(el);
19583         
19584         
19585     },
19586     
19587     onLoad: function () 
19588     {   
19589         this.calevents = [];
19590         var cal = this;
19591         
19592         if(this.store.getCount() > 0){
19593             this.store.data.each(function(d){
19594                cal.addItem({
19595                     id : d.data.id,
19596                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19597                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19598                     time : d.data.start_time,
19599                     title : d.data.title,
19600                     description : d.data.description,
19601                     venue : d.data.venue
19602                 });
19603             });
19604         }
19605         
19606         this.renderEvents();
19607         
19608         if(this.calevents.length && this.loadMask){
19609             this.maskEl.hide();
19610         }
19611     },
19612     
19613     onBeforeLoad: function()
19614     {
19615         this.clearEvents();
19616         if(this.loadMask){
19617             this.maskEl.show();
19618         }
19619     }
19620 });
19621
19622  
19623  /*
19624  * - LGPL
19625  *
19626  * element
19627  * 
19628  */
19629
19630 /**
19631  * @class Roo.bootstrap.Popover
19632  * @extends Roo.bootstrap.Component
19633  * Bootstrap Popover class
19634  * @cfg {String} html contents of the popover   (or false to use children..)
19635  * @cfg {String} title of popover (or false to hide)
19636  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19637  * @cfg {String} trigger click || hover (or false to trigger manually)
19638  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19639  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19640  *      - if false and it has a 'parent' then it will be automatically added to that element
19641  *      - if string - Roo.get  will be called 
19642  * @cfg {Number} delay - delay before showing
19643  
19644  * @constructor
19645  * Create a new Popover
19646  * @param {Object} config The config object
19647  */
19648
19649 Roo.bootstrap.Popover = function(config){
19650     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19651     
19652     this.addEvents({
19653         // raw events
19654          /**
19655          * @event show
19656          * After the popover show
19657          * 
19658          * @param {Roo.bootstrap.Popover} this
19659          */
19660         "show" : true,
19661         /**
19662          * @event hide
19663          * After the popover hide
19664          * 
19665          * @param {Roo.bootstrap.Popover} this
19666          */
19667         "hide" : true
19668     });
19669 };
19670
19671 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19672     
19673     title: false,
19674     html: false,
19675     
19676     placement : 'right',
19677     trigger : 'hover', // hover
19678     modal : false,
19679     delay : 0,
19680     
19681     over: false,
19682     
19683     can_build_overlaid : false,
19684     
19685     maskEl : false, // the mask element
19686     headerEl : false,
19687     contentEl : false,
19688     alignEl : false, // when show is called with an element - this get's stored.
19689     
19690     getChildContainer : function()
19691     {
19692         return this.contentEl;
19693         
19694     },
19695     getPopoverHeader : function()
19696     {
19697         this.title = true; // flag not to hide it..
19698         this.headerEl.addClass('p-0');
19699         return this.headerEl
19700     },
19701     
19702     
19703     getAutoCreate : function(){
19704          
19705         var cfg = {
19706            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19707            style: 'display:block',
19708            cn : [
19709                 {
19710                     cls : 'arrow'
19711                 },
19712                 {
19713                     cls : 'popover-inner ',
19714                     cn : [
19715                         {
19716                             tag: 'h3',
19717                             cls: 'popover-title popover-header',
19718                             html : this.title === false ? '' : this.title
19719                         },
19720                         {
19721                             cls : 'popover-content popover-body '  + (this.cls || ''),
19722                             html : this.html || ''
19723                         }
19724                     ]
19725                     
19726                 }
19727            ]
19728         };
19729         
19730         return cfg;
19731     },
19732     /**
19733      * @param {string} the title
19734      */
19735     setTitle: function(str)
19736     {
19737         this.title = str;
19738         if (this.el) {
19739             this.headerEl.dom.innerHTML = str;
19740         }
19741         
19742     },
19743     /**
19744      * @param {string} the body content
19745      */
19746     setContent: function(str)
19747     {
19748         this.html = str;
19749         if (this.contentEl) {
19750             this.contentEl.dom.innerHTML = str;
19751         }
19752         
19753     },
19754     // as it get's added to the bottom of the page.
19755     onRender : function(ct, position)
19756     {
19757         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19758         
19759         
19760         
19761         if(!this.el){
19762             var cfg = Roo.apply({},  this.getAutoCreate());
19763             cfg.id = Roo.id();
19764             
19765             if (this.cls) {
19766                 cfg.cls += ' ' + this.cls;
19767             }
19768             if (this.style) {
19769                 cfg.style = this.style;
19770             }
19771             //Roo.log("adding to ");
19772             this.el = Roo.get(document.body).createChild(cfg, position);
19773 //            Roo.log(this.el);
19774         }
19775         
19776         this.contentEl = this.el.select('.popover-content',true).first();
19777         this.headerEl =  this.el.select('.popover-title',true).first();
19778         
19779         var nitems = [];
19780         if(typeof(this.items) != 'undefined'){
19781             var items = this.items;
19782             delete this.items;
19783
19784             for(var i =0;i < items.length;i++) {
19785                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19786             }
19787         }
19788
19789         this.items = nitems;
19790         
19791         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19792         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19793         
19794         
19795         
19796         this.initEvents();
19797     },
19798     
19799     resizeMask : function()
19800     {
19801         this.maskEl.setSize(
19802             Roo.lib.Dom.getViewWidth(true),
19803             Roo.lib.Dom.getViewHeight(true)
19804         );
19805     },
19806     
19807     initEvents : function()
19808     {
19809         
19810         if (!this.modal) { 
19811             Roo.bootstrap.Popover.register(this);
19812         }
19813          
19814         this.arrowEl = this.el.select('.arrow',true).first();
19815         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19816         this.el.enableDisplayMode('block');
19817         this.el.hide();
19818  
19819         
19820         if (this.over === false && !this.parent()) {
19821             return; 
19822         }
19823         if (this.triggers === false) {
19824             return;
19825         }
19826          
19827         // support parent
19828         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19829         var triggers = this.trigger ? this.trigger.split(' ') : [];
19830         Roo.each(triggers, function(trigger) {
19831         
19832             if (trigger == 'click') {
19833                 on_el.on('click', this.toggle, this);
19834             } else if (trigger != 'manual') {
19835                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19836                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19837       
19838                 on_el.on(eventIn  ,this.enter, this);
19839                 on_el.on(eventOut, this.leave, this);
19840             }
19841         }, this);
19842     },
19843     
19844     
19845     // private
19846     timeout : null,
19847     hoverState : null,
19848     
19849     toggle : function () {
19850         this.hoverState == 'in' ? this.leave() : this.enter();
19851     },
19852     
19853     enter : function () {
19854         
19855         clearTimeout(this.timeout);
19856     
19857         this.hoverState = 'in';
19858     
19859         if (!this.delay || !this.delay.show) {
19860             this.show();
19861             return;
19862         }
19863         var _t = this;
19864         this.timeout = setTimeout(function () {
19865             if (_t.hoverState == 'in') {
19866                 _t.show();
19867             }
19868         }, this.delay.show)
19869     },
19870     
19871     leave : function() {
19872         clearTimeout(this.timeout);
19873     
19874         this.hoverState = 'out';
19875     
19876         if (!this.delay || !this.delay.hide) {
19877             this.hide();
19878             return;
19879         }
19880         var _t = this;
19881         this.timeout = setTimeout(function () {
19882             if (_t.hoverState == 'out') {
19883                 _t.hide();
19884             }
19885         }, this.delay.hide)
19886     },
19887     /**
19888      * Show the popover
19889      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19890      * @param {string} (left|right|top|bottom) position
19891      */
19892     show : function (on_el, placement)
19893     {
19894         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19895         on_el = on_el || false; // default to false
19896          
19897         if (!on_el) {
19898             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19899                 on_el = this.parent().el;
19900             } else if (this.over) {
19901                 Roo.get(this.over);
19902             }
19903             
19904         }
19905         
19906         this.alignEl = Roo.get( on_el );
19907
19908         if (!this.el) {
19909             this.render(document.body);
19910         }
19911         
19912         
19913          
19914         
19915         if (this.title === false) {
19916             this.headerEl.hide();
19917         }
19918         
19919        
19920         this.el.show();
19921         this.el.dom.style.display = 'block';
19922          
19923  
19924         if (this.alignEl) {
19925             this.updatePosition(this.placement, true);
19926              
19927         } else {
19928             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19929             var es = this.el.getSize();
19930             var x = Roo.lib.Dom.getViewWidth()/2;
19931             var y = Roo.lib.Dom.getViewHeight()/2;
19932             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19933             
19934         }
19935
19936         
19937         //var arrow = this.el.select('.arrow',true).first();
19938         //arrow.set(align[2], 
19939         
19940         this.el.addClass('in');
19941         
19942          
19943         
19944         this.hoverState = 'in';
19945         
19946         if (this.modal) {
19947             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19948             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19949             this.maskEl.dom.style.display = 'block';
19950             this.maskEl.addClass('show');
19951         }
19952         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19953  
19954         this.fireEvent('show', this);
19955         
19956     },
19957     /**
19958      * fire this manually after loading a grid in the table for example
19959      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19960      * @param {Boolean} try and move it if we cant get right position.
19961      */
19962     updatePosition : function(placement, try_move)
19963     {
19964         // allow for calling with no parameters
19965         placement = placement   ? placement :  this.placement;
19966         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19967         
19968         this.el.removeClass([
19969             'fade','top','bottom', 'left', 'right','in',
19970             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19971         ]);
19972         this.el.addClass(placement + ' bs-popover-' + placement);
19973         
19974         if (!this.alignEl ) {
19975             return false;
19976         }
19977         
19978         switch (placement) {
19979             case 'right':
19980                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19981                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19982                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19983                     //normal display... or moved up/down.
19984                     this.el.setXY(offset);
19985                     var xy = this.alignEl.getAnchorXY('tr', false);
19986                     xy[0]+=2;xy[1]+=5;
19987                     this.arrowEl.setXY(xy);
19988                     return true;
19989                 }
19990                 // continue through...
19991                 return this.updatePosition('left', false);
19992                 
19993             
19994             case 'left':
19995                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19996                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19997                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19998                     //normal display... or moved up/down.
19999                     this.el.setXY(offset);
20000                     var xy = this.alignEl.getAnchorXY('tl', false);
20001                     xy[0]-=10;xy[1]+=5; // << fix me
20002                     this.arrowEl.setXY(xy);
20003                     return true;
20004                 }
20005                 // call self...
20006                 return this.updatePosition('right', false);
20007             
20008             case 'top':
20009                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20010                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20011                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20012                     //normal display... or moved up/down.
20013                     this.el.setXY(offset);
20014                     var xy = this.alignEl.getAnchorXY('t', false);
20015                     xy[1]-=10; // << fix me
20016                     this.arrowEl.setXY(xy);
20017                     return true;
20018                 }
20019                 // fall through
20020                return this.updatePosition('bottom', false);
20021             
20022             case 'bottom':
20023                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20024                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20025                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20026                     //normal display... or moved up/down.
20027                     this.el.setXY(offset);
20028                     var xy = this.alignEl.getAnchorXY('b', false);
20029                      xy[1]+=2; // << fix me
20030                     this.arrowEl.setXY(xy);
20031                     return true;
20032                 }
20033                 // fall through
20034                 return this.updatePosition('top', false);
20035                 
20036             
20037         }
20038         
20039         
20040         return false;
20041     },
20042     
20043     hide : function()
20044     {
20045         this.el.setXY([0,0]);
20046         this.el.removeClass('in');
20047         this.el.hide();
20048         this.hoverState = null;
20049         this.maskEl.hide(); // always..
20050         this.fireEvent('hide', this);
20051     }
20052     
20053 });
20054
20055
20056 Roo.apply(Roo.bootstrap.Popover, {
20057
20058     alignment : {
20059         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20060         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20061         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20062         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20063     },
20064     
20065     zIndex : 20001,
20066
20067     clickHander : false,
20068     
20069
20070     onMouseDown : function(e)
20071     {
20072         if (!e.getTarget(".roo-popover")) {
20073             this.hideAll();
20074         }
20075          
20076     },
20077     
20078     popups : [],
20079     
20080     register : function(popup)
20081     {
20082         if (!Roo.bootstrap.Popover.clickHandler) {
20083             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20084         }
20085         // hide other popups.
20086         this.hideAll();
20087         this.popups.push(popup);
20088     },
20089     hideAll : function()
20090     {
20091         this.popups.forEach(function(p) {
20092             p.hide();
20093         });
20094     }
20095
20096 });/*
20097  * - LGPL
20098  *
20099  * Card header - holder for the card header elements.
20100  * 
20101  */
20102
20103 /**
20104  * @class Roo.bootstrap.PopoverNav
20105  * @extends Roo.bootstrap.NavGroup
20106  * Bootstrap Popover header navigation class
20107  * @constructor
20108  * Create a new Popover Header Navigation 
20109  * @param {Object} config The config object
20110  */
20111
20112 Roo.bootstrap.PopoverNav = function(config){
20113     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20114 };
20115
20116 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20117     
20118     
20119     container_method : 'getPopoverHeader' 
20120     
20121      
20122     
20123     
20124    
20125 });
20126
20127  
20128
20129  /*
20130  * - LGPL
20131  *
20132  * Progress
20133  * 
20134  */
20135
20136 /**
20137  * @class Roo.bootstrap.Progress
20138  * @extends Roo.bootstrap.Component
20139  * Bootstrap Progress class
20140  * @cfg {Boolean} striped striped of the progress bar
20141  * @cfg {Boolean} active animated of the progress bar
20142  * 
20143  * 
20144  * @constructor
20145  * Create a new Progress
20146  * @param {Object} config The config object
20147  */
20148
20149 Roo.bootstrap.Progress = function(config){
20150     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20151 };
20152
20153 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20154     
20155     striped : false,
20156     active: false,
20157     
20158     getAutoCreate : function(){
20159         var cfg = {
20160             tag: 'div',
20161             cls: 'progress'
20162         };
20163         
20164         
20165         if(this.striped){
20166             cfg.cls += ' progress-striped';
20167         }
20168       
20169         if(this.active){
20170             cfg.cls += ' active';
20171         }
20172         
20173         
20174         return cfg;
20175     }
20176    
20177 });
20178
20179  
20180
20181  /*
20182  * - LGPL
20183  *
20184  * ProgressBar
20185  * 
20186  */
20187
20188 /**
20189  * @class Roo.bootstrap.ProgressBar
20190  * @extends Roo.bootstrap.Component
20191  * Bootstrap ProgressBar class
20192  * @cfg {Number} aria_valuenow aria-value now
20193  * @cfg {Number} aria_valuemin aria-value min
20194  * @cfg {Number} aria_valuemax aria-value max
20195  * @cfg {String} label label for the progress bar
20196  * @cfg {String} panel (success | info | warning | danger )
20197  * @cfg {String} role role of the progress bar
20198  * @cfg {String} sr_only text
20199  * 
20200  * 
20201  * @constructor
20202  * Create a new ProgressBar
20203  * @param {Object} config The config object
20204  */
20205
20206 Roo.bootstrap.ProgressBar = function(config){
20207     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20208 };
20209
20210 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20211     
20212     aria_valuenow : 0,
20213     aria_valuemin : 0,
20214     aria_valuemax : 100,
20215     label : false,
20216     panel : false,
20217     role : false,
20218     sr_only: false,
20219     
20220     getAutoCreate : function()
20221     {
20222         
20223         var cfg = {
20224             tag: 'div',
20225             cls: 'progress-bar',
20226             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20227         };
20228         
20229         if(this.sr_only){
20230             cfg.cn = {
20231                 tag: 'span',
20232                 cls: 'sr-only',
20233                 html: this.sr_only
20234             }
20235         }
20236         
20237         if(this.role){
20238             cfg.role = this.role;
20239         }
20240         
20241         if(this.aria_valuenow){
20242             cfg['aria-valuenow'] = this.aria_valuenow;
20243         }
20244         
20245         if(this.aria_valuemin){
20246             cfg['aria-valuemin'] = this.aria_valuemin;
20247         }
20248         
20249         if(this.aria_valuemax){
20250             cfg['aria-valuemax'] = this.aria_valuemax;
20251         }
20252         
20253         if(this.label && !this.sr_only){
20254             cfg.html = this.label;
20255         }
20256         
20257         if(this.panel){
20258             cfg.cls += ' progress-bar-' + this.panel;
20259         }
20260         
20261         return cfg;
20262     },
20263     
20264     update : function(aria_valuenow)
20265     {
20266         this.aria_valuenow = aria_valuenow;
20267         
20268         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20269     }
20270    
20271 });
20272
20273  
20274
20275  /*
20276  * - LGPL
20277  *
20278  * column
20279  * 
20280  */
20281
20282 /**
20283  * @class Roo.bootstrap.TabGroup
20284  * @extends Roo.bootstrap.Column
20285  * Bootstrap Column class
20286  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20287  * @cfg {Boolean} carousel true to make the group behave like a carousel
20288  * @cfg {Boolean} bullets show bullets for the panels
20289  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20290  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20291  * @cfg {Boolean} showarrow (true|false) show arrow default true
20292  * 
20293  * @constructor
20294  * Create a new TabGroup
20295  * @param {Object} config The config object
20296  */
20297
20298 Roo.bootstrap.TabGroup = function(config){
20299     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20300     if (!this.navId) {
20301         this.navId = Roo.id();
20302     }
20303     this.tabs = [];
20304     Roo.bootstrap.TabGroup.register(this);
20305     
20306 };
20307
20308 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20309     
20310     carousel : false,
20311     transition : false,
20312     bullets : 0,
20313     timer : 0,
20314     autoslide : false,
20315     slideFn : false,
20316     slideOnTouch : false,
20317     showarrow : true,
20318     
20319     getAutoCreate : function()
20320     {
20321         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20322         
20323         cfg.cls += ' tab-content';
20324         
20325         if (this.carousel) {
20326             cfg.cls += ' carousel slide';
20327             
20328             cfg.cn = [{
20329                cls : 'carousel-inner',
20330                cn : []
20331             }];
20332         
20333             if(this.bullets  && !Roo.isTouch){
20334                 
20335                 var bullets = {
20336                     cls : 'carousel-bullets',
20337                     cn : []
20338                 };
20339                
20340                 if(this.bullets_cls){
20341                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20342                 }
20343                 
20344                 bullets.cn.push({
20345                     cls : 'clear'
20346                 });
20347                 
20348                 cfg.cn[0].cn.push(bullets);
20349             }
20350             
20351             if(this.showarrow){
20352                 cfg.cn[0].cn.push({
20353                     tag : 'div',
20354                     class : 'carousel-arrow',
20355                     cn : [
20356                         {
20357                             tag : 'div',
20358                             class : 'carousel-prev',
20359                             cn : [
20360                                 {
20361                                     tag : 'i',
20362                                     class : 'fa fa-chevron-left'
20363                                 }
20364                             ]
20365                         },
20366                         {
20367                             tag : 'div',
20368                             class : 'carousel-next',
20369                             cn : [
20370                                 {
20371                                     tag : 'i',
20372                                     class : 'fa fa-chevron-right'
20373                                 }
20374                             ]
20375                         }
20376                     ]
20377                 });
20378             }
20379             
20380         }
20381         
20382         return cfg;
20383     },
20384     
20385     initEvents:  function()
20386     {
20387 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20388 //            this.el.on("touchstart", this.onTouchStart, this);
20389 //        }
20390         
20391         if(this.autoslide){
20392             var _this = this;
20393             
20394             this.slideFn = window.setInterval(function() {
20395                 _this.showPanelNext();
20396             }, this.timer);
20397         }
20398         
20399         if(this.showarrow){
20400             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20401             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20402         }
20403         
20404         
20405     },
20406     
20407 //    onTouchStart : function(e, el, o)
20408 //    {
20409 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20410 //            return;
20411 //        }
20412 //        
20413 //        this.showPanelNext();
20414 //    },
20415     
20416     
20417     getChildContainer : function()
20418     {
20419         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20420     },
20421     
20422     /**
20423     * register a Navigation item
20424     * @param {Roo.bootstrap.NavItem} the navitem to add
20425     */
20426     register : function(item)
20427     {
20428         this.tabs.push( item);
20429         item.navId = this.navId; // not really needed..
20430         this.addBullet();
20431     
20432     },
20433     
20434     getActivePanel : function()
20435     {
20436         var r = false;
20437         Roo.each(this.tabs, function(t) {
20438             if (t.active) {
20439                 r = t;
20440                 return false;
20441             }
20442             return null;
20443         });
20444         return r;
20445         
20446     },
20447     getPanelByName : function(n)
20448     {
20449         var r = false;
20450         Roo.each(this.tabs, function(t) {
20451             if (t.tabId == n) {
20452                 r = t;
20453                 return false;
20454             }
20455             return null;
20456         });
20457         return r;
20458     },
20459     indexOfPanel : function(p)
20460     {
20461         var r = false;
20462         Roo.each(this.tabs, function(t,i) {
20463             if (t.tabId == p.tabId) {
20464                 r = i;
20465                 return false;
20466             }
20467             return null;
20468         });
20469         return r;
20470     },
20471     /**
20472      * show a specific panel
20473      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20474      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20475      */
20476     showPanel : function (pan)
20477     {
20478         if(this.transition || typeof(pan) == 'undefined'){
20479             Roo.log("waiting for the transitionend");
20480             return false;
20481         }
20482         
20483         if (typeof(pan) == 'number') {
20484             pan = this.tabs[pan];
20485         }
20486         
20487         if (typeof(pan) == 'string') {
20488             pan = this.getPanelByName(pan);
20489         }
20490         
20491         var cur = this.getActivePanel();
20492         
20493         if(!pan || !cur){
20494             Roo.log('pan or acitve pan is undefined');
20495             return false;
20496         }
20497         
20498         if (pan.tabId == this.getActivePanel().tabId) {
20499             return true;
20500         }
20501         
20502         if (false === cur.fireEvent('beforedeactivate')) {
20503             return false;
20504         }
20505         
20506         if(this.bullets > 0 && !Roo.isTouch){
20507             this.setActiveBullet(this.indexOfPanel(pan));
20508         }
20509         
20510         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20511             
20512             //class="carousel-item carousel-item-next carousel-item-left"
20513             
20514             this.transition = true;
20515             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20516             var lr = dir == 'next' ? 'left' : 'right';
20517             pan.el.addClass(dir); // or prev
20518             pan.el.addClass('carousel-item-' + dir); // or prev
20519             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20520             cur.el.addClass(lr); // or right
20521             pan.el.addClass(lr);
20522             cur.el.addClass('carousel-item-' +lr); // or right
20523             pan.el.addClass('carousel-item-' +lr);
20524             
20525             
20526             var _this = this;
20527             cur.el.on('transitionend', function() {
20528                 Roo.log("trans end?");
20529                 
20530                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20531                 pan.setActive(true);
20532                 
20533                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20534                 cur.setActive(false);
20535                 
20536                 _this.transition = false;
20537                 
20538             }, this, { single:  true } );
20539             
20540             return true;
20541         }
20542         
20543         cur.setActive(false);
20544         pan.setActive(true);
20545         
20546         return true;
20547         
20548     },
20549     showPanelNext : function()
20550     {
20551         var i = this.indexOfPanel(this.getActivePanel());
20552         
20553         if (i >= this.tabs.length - 1 && !this.autoslide) {
20554             return;
20555         }
20556         
20557         if (i >= this.tabs.length - 1 && this.autoslide) {
20558             i = -1;
20559         }
20560         
20561         this.showPanel(this.tabs[i+1]);
20562     },
20563     
20564     showPanelPrev : function()
20565     {
20566         var i = this.indexOfPanel(this.getActivePanel());
20567         
20568         if (i  < 1 && !this.autoslide) {
20569             return;
20570         }
20571         
20572         if (i < 1 && this.autoslide) {
20573             i = this.tabs.length;
20574         }
20575         
20576         this.showPanel(this.tabs[i-1]);
20577     },
20578     
20579     
20580     addBullet: function()
20581     {
20582         if(!this.bullets || Roo.isTouch){
20583             return;
20584         }
20585         var ctr = this.el.select('.carousel-bullets',true).first();
20586         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20587         var bullet = ctr.createChild({
20588             cls : 'bullet bullet-' + i
20589         },ctr.dom.lastChild);
20590         
20591         
20592         var _this = this;
20593         
20594         bullet.on('click', (function(e, el, o, ii, t){
20595
20596             e.preventDefault();
20597
20598             this.showPanel(ii);
20599
20600             if(this.autoslide && this.slideFn){
20601                 clearInterval(this.slideFn);
20602                 this.slideFn = window.setInterval(function() {
20603                     _this.showPanelNext();
20604                 }, this.timer);
20605             }
20606
20607         }).createDelegate(this, [i, bullet], true));
20608                 
20609         
20610     },
20611      
20612     setActiveBullet : function(i)
20613     {
20614         if(Roo.isTouch){
20615             return;
20616         }
20617         
20618         Roo.each(this.el.select('.bullet', true).elements, function(el){
20619             el.removeClass('selected');
20620         });
20621
20622         var bullet = this.el.select('.bullet-' + i, true).first();
20623         
20624         if(!bullet){
20625             return;
20626         }
20627         
20628         bullet.addClass('selected');
20629     }
20630     
20631     
20632   
20633 });
20634
20635  
20636
20637  
20638  
20639 Roo.apply(Roo.bootstrap.TabGroup, {
20640     
20641     groups: {},
20642      /**
20643     * register a Navigation Group
20644     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20645     */
20646     register : function(navgrp)
20647     {
20648         this.groups[navgrp.navId] = navgrp;
20649         
20650     },
20651     /**
20652     * fetch a Navigation Group based on the navigation ID
20653     * if one does not exist , it will get created.
20654     * @param {string} the navgroup to add
20655     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20656     */
20657     get: function(navId) {
20658         if (typeof(this.groups[navId]) == 'undefined') {
20659             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20660         }
20661         return this.groups[navId] ;
20662     }
20663     
20664     
20665     
20666 });
20667
20668  /*
20669  * - LGPL
20670  *
20671  * TabPanel
20672  * 
20673  */
20674
20675 /**
20676  * @class Roo.bootstrap.TabPanel
20677  * @extends Roo.bootstrap.Component
20678  * Bootstrap TabPanel class
20679  * @cfg {Boolean} active panel active
20680  * @cfg {String} html panel content
20681  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20682  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20683  * @cfg {String} href click to link..
20684  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20685  * 
20686  * 
20687  * @constructor
20688  * Create a new TabPanel
20689  * @param {Object} config The config object
20690  */
20691
20692 Roo.bootstrap.TabPanel = function(config){
20693     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20694     this.addEvents({
20695         /**
20696              * @event changed
20697              * Fires when the active status changes
20698              * @param {Roo.bootstrap.TabPanel} this
20699              * @param {Boolean} state the new state
20700             
20701          */
20702         'changed': true,
20703         /**
20704              * @event beforedeactivate
20705              * Fires before a tab is de-activated - can be used to do validation on a form.
20706              * @param {Roo.bootstrap.TabPanel} this
20707              * @return {Boolean} false if there is an error
20708             
20709          */
20710         'beforedeactivate': true
20711      });
20712     
20713     this.tabId = this.tabId || Roo.id();
20714   
20715 };
20716
20717 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20718     
20719     active: false,
20720     html: false,
20721     tabId: false,
20722     navId : false,
20723     href : '',
20724     touchSlide : false,
20725     getAutoCreate : function(){
20726         
20727         
20728         var cfg = {
20729             tag: 'div',
20730             // item is needed for carousel - not sure if it has any effect otherwise
20731             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20732             html: this.html || ''
20733         };
20734         
20735         if(this.active){
20736             cfg.cls += ' active';
20737         }
20738         
20739         if(this.tabId){
20740             cfg.tabId = this.tabId;
20741         }
20742         
20743         
20744         
20745         return cfg;
20746     },
20747     
20748     initEvents:  function()
20749     {
20750         var p = this.parent();
20751         
20752         this.navId = this.navId || p.navId;
20753         
20754         if (typeof(this.navId) != 'undefined') {
20755             // not really needed.. but just in case.. parent should be a NavGroup.
20756             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20757             
20758             tg.register(this);
20759             
20760             var i = tg.tabs.length - 1;
20761             
20762             if(this.active && tg.bullets > 0 && i < tg.bullets){
20763                 tg.setActiveBullet(i);
20764             }
20765         }
20766         
20767         this.el.on('click', this.onClick, this);
20768         
20769         if(Roo.isTouch && this.touchSlide){
20770             this.el.on("touchstart", this.onTouchStart, this);
20771             this.el.on("touchmove", this.onTouchMove, this);
20772             this.el.on("touchend", this.onTouchEnd, this);
20773         }
20774         
20775     },
20776     
20777     onRender : function(ct, position)
20778     {
20779         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20780     },
20781     
20782     setActive : function(state)
20783     {
20784         Roo.log("panel - set active " + this.tabId + "=" + state);
20785         
20786         this.active = state;
20787         if (!state) {
20788             this.el.removeClass('active');
20789             
20790         } else  if (!this.el.hasClass('active')) {
20791             this.el.addClass('active');
20792         }
20793         
20794         this.fireEvent('changed', this, state);
20795     },
20796     
20797     onClick : function(e)
20798     {
20799         e.preventDefault();
20800         
20801         if(!this.href.length){
20802             return;
20803         }
20804         
20805         window.location.href = this.href;
20806     },
20807     
20808     startX : 0,
20809     startY : 0,
20810     endX : 0,
20811     endY : 0,
20812     swiping : false,
20813     
20814     onTouchStart : function(e)
20815     {
20816         this.swiping = false;
20817         
20818         this.startX = e.browserEvent.touches[0].clientX;
20819         this.startY = e.browserEvent.touches[0].clientY;
20820     },
20821     
20822     onTouchMove : function(e)
20823     {
20824         this.swiping = true;
20825         
20826         this.endX = e.browserEvent.touches[0].clientX;
20827         this.endY = e.browserEvent.touches[0].clientY;
20828     },
20829     
20830     onTouchEnd : function(e)
20831     {
20832         if(!this.swiping){
20833             this.onClick(e);
20834             return;
20835         }
20836         
20837         var tabGroup = this.parent();
20838         
20839         if(this.endX > this.startX){ // swiping right
20840             tabGroup.showPanelPrev();
20841             return;
20842         }
20843         
20844         if(this.startX > this.endX){ // swiping left
20845             tabGroup.showPanelNext();
20846             return;
20847         }
20848     }
20849     
20850     
20851 });
20852  
20853
20854  
20855
20856  /*
20857  * - LGPL
20858  *
20859  * DateField
20860  * 
20861  */
20862
20863 /**
20864  * @class Roo.bootstrap.DateField
20865  * @extends Roo.bootstrap.Input
20866  * Bootstrap DateField class
20867  * @cfg {Number} weekStart default 0
20868  * @cfg {String} viewMode default empty, (months|years)
20869  * @cfg {String} minViewMode default empty, (months|years)
20870  * @cfg {Number} startDate default -Infinity
20871  * @cfg {Number} endDate default Infinity
20872  * @cfg {Boolean} todayHighlight default false
20873  * @cfg {Boolean} todayBtn default false
20874  * @cfg {Boolean} calendarWeeks default false
20875  * @cfg {Object} daysOfWeekDisabled default empty
20876  * @cfg {Boolean} singleMode default false (true | false)
20877  * 
20878  * @cfg {Boolean} keyboardNavigation default true
20879  * @cfg {String} language default en
20880  * 
20881  * @constructor
20882  * Create a new DateField
20883  * @param {Object} config The config object
20884  */
20885
20886 Roo.bootstrap.DateField = function(config){
20887     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20888      this.addEvents({
20889             /**
20890              * @event show
20891              * Fires when this field show.
20892              * @param {Roo.bootstrap.DateField} this
20893              * @param {Mixed} date The date value
20894              */
20895             show : true,
20896             /**
20897              * @event show
20898              * Fires when this field hide.
20899              * @param {Roo.bootstrap.DateField} this
20900              * @param {Mixed} date The date value
20901              */
20902             hide : true,
20903             /**
20904              * @event select
20905              * Fires when select a date.
20906              * @param {Roo.bootstrap.DateField} this
20907              * @param {Mixed} date The date value
20908              */
20909             select : true,
20910             /**
20911              * @event beforeselect
20912              * Fires when before select a date.
20913              * @param {Roo.bootstrap.DateField} this
20914              * @param {Mixed} date The date value
20915              */
20916             beforeselect : true
20917         });
20918 };
20919
20920 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20921     
20922     /**
20923      * @cfg {String} format
20924      * The default date format string which can be overriden for localization support.  The format must be
20925      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20926      */
20927     format : "m/d/y",
20928     /**
20929      * @cfg {String} altFormats
20930      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20931      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20932      */
20933     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20934     
20935     weekStart : 0,
20936     
20937     viewMode : '',
20938     
20939     minViewMode : '',
20940     
20941     todayHighlight : false,
20942     
20943     todayBtn: false,
20944     
20945     language: 'en',
20946     
20947     keyboardNavigation: true,
20948     
20949     calendarWeeks: false,
20950     
20951     startDate: -Infinity,
20952     
20953     endDate: Infinity,
20954     
20955     daysOfWeekDisabled: [],
20956     
20957     _events: [],
20958     
20959     singleMode : false,
20960     
20961     UTCDate: function()
20962     {
20963         return new Date(Date.UTC.apply(Date, arguments));
20964     },
20965     
20966     UTCToday: function()
20967     {
20968         var today = new Date();
20969         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20970     },
20971     
20972     getDate: function() {
20973             var d = this.getUTCDate();
20974             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20975     },
20976     
20977     getUTCDate: function() {
20978             return this.date;
20979     },
20980     
20981     setDate: function(d) {
20982             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20983     },
20984     
20985     setUTCDate: function(d) {
20986             this.date = d;
20987             this.setValue(this.formatDate(this.date));
20988     },
20989         
20990     onRender: function(ct, position)
20991     {
20992         
20993         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20994         
20995         this.language = this.language || 'en';
20996         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20997         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20998         
20999         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21000         this.format = this.format || 'm/d/y';
21001         this.isInline = false;
21002         this.isInput = true;
21003         this.component = this.el.select('.add-on', true).first() || false;
21004         this.component = (this.component && this.component.length === 0) ? false : this.component;
21005         this.hasInput = this.component && this.inputEl().length;
21006         
21007         if (typeof(this.minViewMode === 'string')) {
21008             switch (this.minViewMode) {
21009                 case 'months':
21010                     this.minViewMode = 1;
21011                     break;
21012                 case 'years':
21013                     this.minViewMode = 2;
21014                     break;
21015                 default:
21016                     this.minViewMode = 0;
21017                     break;
21018             }
21019         }
21020         
21021         if (typeof(this.viewMode === 'string')) {
21022             switch (this.viewMode) {
21023                 case 'months':
21024                     this.viewMode = 1;
21025                     break;
21026                 case 'years':
21027                     this.viewMode = 2;
21028                     break;
21029                 default:
21030                     this.viewMode = 0;
21031                     break;
21032             }
21033         }
21034                 
21035         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21036         
21037 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21038         
21039         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21040         
21041         this.picker().on('mousedown', this.onMousedown, this);
21042         this.picker().on('click', this.onClick, this);
21043         
21044         this.picker().addClass('datepicker-dropdown');
21045         
21046         this.startViewMode = this.viewMode;
21047         
21048         if(this.singleMode){
21049             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21050                 v.setVisibilityMode(Roo.Element.DISPLAY);
21051                 v.hide();
21052             });
21053             
21054             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21055                 v.setStyle('width', '189px');
21056             });
21057         }
21058         
21059         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21060             if(!this.calendarWeeks){
21061                 v.remove();
21062                 return;
21063             }
21064             
21065             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21066             v.attr('colspan', function(i, val){
21067                 return parseInt(val) + 1;
21068             });
21069         });
21070                         
21071         
21072         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21073         
21074         this.setStartDate(this.startDate);
21075         this.setEndDate(this.endDate);
21076         
21077         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21078         
21079         this.fillDow();
21080         this.fillMonths();
21081         this.update();
21082         this.showMode();
21083         
21084         if(this.isInline) {
21085             this.showPopup();
21086         }
21087     },
21088     
21089     picker : function()
21090     {
21091         return this.pickerEl;
21092 //        return this.el.select('.datepicker', true).first();
21093     },
21094     
21095     fillDow: function()
21096     {
21097         var dowCnt = this.weekStart;
21098         
21099         var dow = {
21100             tag: 'tr',
21101             cn: [
21102                 
21103             ]
21104         };
21105         
21106         if(this.calendarWeeks){
21107             dow.cn.push({
21108                 tag: 'th',
21109                 cls: 'cw',
21110                 html: '&nbsp;'
21111             })
21112         }
21113         
21114         while (dowCnt < this.weekStart + 7) {
21115             dow.cn.push({
21116                 tag: 'th',
21117                 cls: 'dow',
21118                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21119             });
21120         }
21121         
21122         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21123     },
21124     
21125     fillMonths: function()
21126     {    
21127         var i = 0;
21128         var months = this.picker().select('>.datepicker-months td', true).first();
21129         
21130         months.dom.innerHTML = '';
21131         
21132         while (i < 12) {
21133             var month = {
21134                 tag: 'span',
21135                 cls: 'month',
21136                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21137             };
21138             
21139             months.createChild(month);
21140         }
21141         
21142     },
21143     
21144     update: function()
21145     {
21146         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;
21147         
21148         if (this.date < this.startDate) {
21149             this.viewDate = new Date(this.startDate);
21150         } else if (this.date > this.endDate) {
21151             this.viewDate = new Date(this.endDate);
21152         } else {
21153             this.viewDate = new Date(this.date);
21154         }
21155         
21156         this.fill();
21157     },
21158     
21159     fill: function() 
21160     {
21161         var d = new Date(this.viewDate),
21162                 year = d.getUTCFullYear(),
21163                 month = d.getUTCMonth(),
21164                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21165                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21166                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21167                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21168                 currentDate = this.date && this.date.valueOf(),
21169                 today = this.UTCToday();
21170         
21171         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21172         
21173 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21174         
21175 //        this.picker.select('>tfoot th.today').
21176 //                                              .text(dates[this.language].today)
21177 //                                              .toggle(this.todayBtn !== false);
21178     
21179         this.updateNavArrows();
21180         this.fillMonths();
21181                                                 
21182         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21183         
21184         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21185          
21186         prevMonth.setUTCDate(day);
21187         
21188         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21189         
21190         var nextMonth = new Date(prevMonth);
21191         
21192         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21193         
21194         nextMonth = nextMonth.valueOf();
21195         
21196         var fillMonths = false;
21197         
21198         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21199         
21200         while(prevMonth.valueOf() <= nextMonth) {
21201             var clsName = '';
21202             
21203             if (prevMonth.getUTCDay() === this.weekStart) {
21204                 if(fillMonths){
21205                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21206                 }
21207                     
21208                 fillMonths = {
21209                     tag: 'tr',
21210                     cn: []
21211                 };
21212                 
21213                 if(this.calendarWeeks){
21214                     // ISO 8601: First week contains first thursday.
21215                     // ISO also states week starts on Monday, but we can be more abstract here.
21216                     var
21217                     // Start of current week: based on weekstart/current date
21218                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21219                     // Thursday of this week
21220                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21221                     // First Thursday of year, year from thursday
21222                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21223                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21224                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21225                     
21226                     fillMonths.cn.push({
21227                         tag: 'td',
21228                         cls: 'cw',
21229                         html: calWeek
21230                     });
21231                 }
21232             }
21233             
21234             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21235                 clsName += ' old';
21236             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21237                 clsName += ' new';
21238             }
21239             if (this.todayHighlight &&
21240                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21241                 prevMonth.getUTCMonth() == today.getMonth() &&
21242                 prevMonth.getUTCDate() == today.getDate()) {
21243                 clsName += ' today';
21244             }
21245             
21246             if (currentDate && prevMonth.valueOf() === currentDate) {
21247                 clsName += ' active';
21248             }
21249             
21250             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21251                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21252                     clsName += ' disabled';
21253             }
21254             
21255             fillMonths.cn.push({
21256                 tag: 'td',
21257                 cls: 'day ' + clsName,
21258                 html: prevMonth.getDate()
21259             });
21260             
21261             prevMonth.setDate(prevMonth.getDate()+1);
21262         }
21263           
21264         var currentYear = this.date && this.date.getUTCFullYear();
21265         var currentMonth = this.date && this.date.getUTCMonth();
21266         
21267         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21268         
21269         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21270             v.removeClass('active');
21271             
21272             if(currentYear === year && k === currentMonth){
21273                 v.addClass('active');
21274             }
21275             
21276             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21277                 v.addClass('disabled');
21278             }
21279             
21280         });
21281         
21282         
21283         year = parseInt(year/10, 10) * 10;
21284         
21285         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21286         
21287         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21288         
21289         year -= 1;
21290         for (var i = -1; i < 11; i++) {
21291             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21292                 tag: 'span',
21293                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21294                 html: year
21295             });
21296             
21297             year += 1;
21298         }
21299     },
21300     
21301     showMode: function(dir) 
21302     {
21303         if (dir) {
21304             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21305         }
21306         
21307         Roo.each(this.picker().select('>div',true).elements, function(v){
21308             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21309             v.hide();
21310         });
21311         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21312     },
21313     
21314     place: function()
21315     {
21316         if(this.isInline) {
21317             return;
21318         }
21319         
21320         this.picker().removeClass(['bottom', 'top']);
21321         
21322         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21323             /*
21324              * place to the top of element!
21325              *
21326              */
21327             
21328             this.picker().addClass('top');
21329             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21330             
21331             return;
21332         }
21333         
21334         this.picker().addClass('bottom');
21335         
21336         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21337     },
21338     
21339     parseDate : function(value)
21340     {
21341         if(!value || value instanceof Date){
21342             return value;
21343         }
21344         var v = Date.parseDate(value, this.format);
21345         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21346             v = Date.parseDate(value, 'Y-m-d');
21347         }
21348         if(!v && this.altFormats){
21349             if(!this.altFormatsArray){
21350                 this.altFormatsArray = this.altFormats.split("|");
21351             }
21352             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21353                 v = Date.parseDate(value, this.altFormatsArray[i]);
21354             }
21355         }
21356         return v;
21357     },
21358     
21359     formatDate : function(date, fmt)
21360     {   
21361         return (!date || !(date instanceof Date)) ?
21362         date : date.dateFormat(fmt || this.format);
21363     },
21364     
21365     onFocus : function()
21366     {
21367         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21368         this.showPopup();
21369     },
21370     
21371     onBlur : function()
21372     {
21373         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21374         
21375         var d = this.inputEl().getValue();
21376         
21377         this.setValue(d);
21378                 
21379         this.hidePopup();
21380     },
21381     
21382     showPopup : function()
21383     {
21384         this.picker().show();
21385         this.update();
21386         this.place();
21387         
21388         this.fireEvent('showpopup', this, this.date);
21389     },
21390     
21391     hidePopup : function()
21392     {
21393         if(this.isInline) {
21394             return;
21395         }
21396         this.picker().hide();
21397         this.viewMode = this.startViewMode;
21398         this.showMode();
21399         
21400         this.fireEvent('hidepopup', this, this.date);
21401         
21402     },
21403     
21404     onMousedown: function(e)
21405     {
21406         e.stopPropagation();
21407         e.preventDefault();
21408     },
21409     
21410     keyup: function(e)
21411     {
21412         Roo.bootstrap.DateField.superclass.keyup.call(this);
21413         this.update();
21414     },
21415
21416     setValue: function(v)
21417     {
21418         if(this.fireEvent('beforeselect', this, v) !== false){
21419             var d = new Date(this.parseDate(v) ).clearTime();
21420         
21421             if(isNaN(d.getTime())){
21422                 this.date = this.viewDate = '';
21423                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21424                 return;
21425             }
21426
21427             v = this.formatDate(d);
21428
21429             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21430
21431             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21432
21433             this.update();
21434
21435             this.fireEvent('select', this, this.date);
21436         }
21437     },
21438     
21439     getValue: function()
21440     {
21441         return this.formatDate(this.date);
21442     },
21443     
21444     fireKey: function(e)
21445     {
21446         if (!this.picker().isVisible()){
21447             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21448                 this.showPopup();
21449             }
21450             return;
21451         }
21452         
21453         var dateChanged = false,
21454         dir, day, month,
21455         newDate, newViewDate;
21456         
21457         switch(e.keyCode){
21458             case 27: // escape
21459                 this.hidePopup();
21460                 e.preventDefault();
21461                 break;
21462             case 37: // left
21463             case 39: // right
21464                 if (!this.keyboardNavigation) {
21465                     break;
21466                 }
21467                 dir = e.keyCode == 37 ? -1 : 1;
21468                 
21469                 if (e.ctrlKey){
21470                     newDate = this.moveYear(this.date, dir);
21471                     newViewDate = this.moveYear(this.viewDate, dir);
21472                 } else if (e.shiftKey){
21473                     newDate = this.moveMonth(this.date, dir);
21474                     newViewDate = this.moveMonth(this.viewDate, dir);
21475                 } else {
21476                     newDate = new Date(this.date);
21477                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21478                     newViewDate = new Date(this.viewDate);
21479                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21480                 }
21481                 if (this.dateWithinRange(newDate)){
21482                     this.date = newDate;
21483                     this.viewDate = newViewDate;
21484                     this.setValue(this.formatDate(this.date));
21485 //                    this.update();
21486                     e.preventDefault();
21487                     dateChanged = true;
21488                 }
21489                 break;
21490             case 38: // up
21491             case 40: // down
21492                 if (!this.keyboardNavigation) {
21493                     break;
21494                 }
21495                 dir = e.keyCode == 38 ? -1 : 1;
21496                 if (e.ctrlKey){
21497                     newDate = this.moveYear(this.date, dir);
21498                     newViewDate = this.moveYear(this.viewDate, dir);
21499                 } else if (e.shiftKey){
21500                     newDate = this.moveMonth(this.date, dir);
21501                     newViewDate = this.moveMonth(this.viewDate, dir);
21502                 } else {
21503                     newDate = new Date(this.date);
21504                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21505                     newViewDate = new Date(this.viewDate);
21506                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21507                 }
21508                 if (this.dateWithinRange(newDate)){
21509                     this.date = newDate;
21510                     this.viewDate = newViewDate;
21511                     this.setValue(this.formatDate(this.date));
21512 //                    this.update();
21513                     e.preventDefault();
21514                     dateChanged = true;
21515                 }
21516                 break;
21517             case 13: // enter
21518                 this.setValue(this.formatDate(this.date));
21519                 this.hidePopup();
21520                 e.preventDefault();
21521                 break;
21522             case 9: // tab
21523                 this.setValue(this.formatDate(this.date));
21524                 this.hidePopup();
21525                 break;
21526             case 16: // shift
21527             case 17: // ctrl
21528             case 18: // alt
21529                 break;
21530             default :
21531                 this.hidePopup();
21532                 
21533         }
21534     },
21535     
21536     
21537     onClick: function(e) 
21538     {
21539         e.stopPropagation();
21540         e.preventDefault();
21541         
21542         var target = e.getTarget();
21543         
21544         if(target.nodeName.toLowerCase() === 'i'){
21545             target = Roo.get(target).dom.parentNode;
21546         }
21547         
21548         var nodeName = target.nodeName;
21549         var className = target.className;
21550         var html = target.innerHTML;
21551         //Roo.log(nodeName);
21552         
21553         switch(nodeName.toLowerCase()) {
21554             case 'th':
21555                 switch(className) {
21556                     case 'switch':
21557                         this.showMode(1);
21558                         break;
21559                     case 'prev':
21560                     case 'next':
21561                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21562                         switch(this.viewMode){
21563                                 case 0:
21564                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21565                                         break;
21566                                 case 1:
21567                                 case 2:
21568                                         this.viewDate = this.moveYear(this.viewDate, dir);
21569                                         break;
21570                         }
21571                         this.fill();
21572                         break;
21573                     case 'today':
21574                         var date = new Date();
21575                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21576 //                        this.fill()
21577                         this.setValue(this.formatDate(this.date));
21578                         
21579                         this.hidePopup();
21580                         break;
21581                 }
21582                 break;
21583             case 'span':
21584                 if (className.indexOf('disabled') < 0) {
21585                     this.viewDate.setUTCDate(1);
21586                     if (className.indexOf('month') > -1) {
21587                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21588                     } else {
21589                         var year = parseInt(html, 10) || 0;
21590                         this.viewDate.setUTCFullYear(year);
21591                         
21592                     }
21593                     
21594                     if(this.singleMode){
21595                         this.setValue(this.formatDate(this.viewDate));
21596                         this.hidePopup();
21597                         return;
21598                     }
21599                     
21600                     this.showMode(-1);
21601                     this.fill();
21602                 }
21603                 break;
21604                 
21605             case 'td':
21606                 //Roo.log(className);
21607                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21608                     var day = parseInt(html, 10) || 1;
21609                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21610                         month = (this.viewDate || new Date()).getUTCMonth();
21611
21612                     if (className.indexOf('old') > -1) {
21613                         if(month === 0 ){
21614                             month = 11;
21615                             year -= 1;
21616                         }else{
21617                             month -= 1;
21618                         }
21619                     } else if (className.indexOf('new') > -1) {
21620                         if (month == 11) {
21621                             month = 0;
21622                             year += 1;
21623                         } else {
21624                             month += 1;
21625                         }
21626                     }
21627                     //Roo.log([year,month,day]);
21628                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21629                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21630 //                    this.fill();
21631                     //Roo.log(this.formatDate(this.date));
21632                     this.setValue(this.formatDate(this.date));
21633                     this.hidePopup();
21634                 }
21635                 break;
21636         }
21637     },
21638     
21639     setStartDate: function(startDate)
21640     {
21641         this.startDate = startDate || -Infinity;
21642         if (this.startDate !== -Infinity) {
21643             this.startDate = this.parseDate(this.startDate);
21644         }
21645         this.update();
21646         this.updateNavArrows();
21647     },
21648
21649     setEndDate: function(endDate)
21650     {
21651         this.endDate = endDate || Infinity;
21652         if (this.endDate !== Infinity) {
21653             this.endDate = this.parseDate(this.endDate);
21654         }
21655         this.update();
21656         this.updateNavArrows();
21657     },
21658     
21659     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21660     {
21661         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21662         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21663             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21664         }
21665         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21666             return parseInt(d, 10);
21667         });
21668         this.update();
21669         this.updateNavArrows();
21670     },
21671     
21672     updateNavArrows: function() 
21673     {
21674         if(this.singleMode){
21675             return;
21676         }
21677         
21678         var d = new Date(this.viewDate),
21679         year = d.getUTCFullYear(),
21680         month = d.getUTCMonth();
21681         
21682         Roo.each(this.picker().select('.prev', true).elements, function(v){
21683             v.show();
21684             switch (this.viewMode) {
21685                 case 0:
21686
21687                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21688                         v.hide();
21689                     }
21690                     break;
21691                 case 1:
21692                 case 2:
21693                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21694                         v.hide();
21695                     }
21696                     break;
21697             }
21698         });
21699         
21700         Roo.each(this.picker().select('.next', true).elements, function(v){
21701             v.show();
21702             switch (this.viewMode) {
21703                 case 0:
21704
21705                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21706                         v.hide();
21707                     }
21708                     break;
21709                 case 1:
21710                 case 2:
21711                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21712                         v.hide();
21713                     }
21714                     break;
21715             }
21716         })
21717     },
21718     
21719     moveMonth: function(date, dir)
21720     {
21721         if (!dir) {
21722             return date;
21723         }
21724         var new_date = new Date(date.valueOf()),
21725         day = new_date.getUTCDate(),
21726         month = new_date.getUTCMonth(),
21727         mag = Math.abs(dir),
21728         new_month, test;
21729         dir = dir > 0 ? 1 : -1;
21730         if (mag == 1){
21731             test = dir == -1
21732             // If going back one month, make sure month is not current month
21733             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21734             ? function(){
21735                 return new_date.getUTCMonth() == month;
21736             }
21737             // If going forward one month, make sure month is as expected
21738             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21739             : function(){
21740                 return new_date.getUTCMonth() != new_month;
21741             };
21742             new_month = month + dir;
21743             new_date.setUTCMonth(new_month);
21744             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21745             if (new_month < 0 || new_month > 11) {
21746                 new_month = (new_month + 12) % 12;
21747             }
21748         } else {
21749             // For magnitudes >1, move one month at a time...
21750             for (var i=0; i<mag; i++) {
21751                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21752                 new_date = this.moveMonth(new_date, dir);
21753             }
21754             // ...then reset the day, keeping it in the new month
21755             new_month = new_date.getUTCMonth();
21756             new_date.setUTCDate(day);
21757             test = function(){
21758                 return new_month != new_date.getUTCMonth();
21759             };
21760         }
21761         // Common date-resetting loop -- if date is beyond end of month, make it
21762         // end of month
21763         while (test()){
21764             new_date.setUTCDate(--day);
21765             new_date.setUTCMonth(new_month);
21766         }
21767         return new_date;
21768     },
21769
21770     moveYear: function(date, dir)
21771     {
21772         return this.moveMonth(date, dir*12);
21773     },
21774
21775     dateWithinRange: function(date)
21776     {
21777         return date >= this.startDate && date <= this.endDate;
21778     },
21779
21780     
21781     remove: function() 
21782     {
21783         this.picker().remove();
21784     },
21785     
21786     validateValue : function(value)
21787     {
21788         if(this.getVisibilityEl().hasClass('hidden')){
21789             return true;
21790         }
21791         
21792         if(value.length < 1)  {
21793             if(this.allowBlank){
21794                 return true;
21795             }
21796             return false;
21797         }
21798         
21799         if(value.length < this.minLength){
21800             return false;
21801         }
21802         if(value.length > this.maxLength){
21803             return false;
21804         }
21805         if(this.vtype){
21806             var vt = Roo.form.VTypes;
21807             if(!vt[this.vtype](value, this)){
21808                 return false;
21809             }
21810         }
21811         if(typeof this.validator == "function"){
21812             var msg = this.validator(value);
21813             if(msg !== true){
21814                 return false;
21815             }
21816         }
21817         
21818         if(this.regex && !this.regex.test(value)){
21819             return false;
21820         }
21821         
21822         if(typeof(this.parseDate(value)) == 'undefined'){
21823             return false;
21824         }
21825         
21826         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21827             return false;
21828         }      
21829         
21830         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21831             return false;
21832         } 
21833         
21834         
21835         return true;
21836     },
21837     
21838     reset : function()
21839     {
21840         this.date = this.viewDate = '';
21841         
21842         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21843     }
21844    
21845 });
21846
21847 Roo.apply(Roo.bootstrap.DateField,  {
21848     
21849     head : {
21850         tag: 'thead',
21851         cn: [
21852         {
21853             tag: 'tr',
21854             cn: [
21855             {
21856                 tag: 'th',
21857                 cls: 'prev',
21858                 html: '<i class="fa fa-arrow-left"/>'
21859             },
21860             {
21861                 tag: 'th',
21862                 cls: 'switch',
21863                 colspan: '5'
21864             },
21865             {
21866                 tag: 'th',
21867                 cls: 'next',
21868                 html: '<i class="fa fa-arrow-right"/>'
21869             }
21870
21871             ]
21872         }
21873         ]
21874     },
21875     
21876     content : {
21877         tag: 'tbody',
21878         cn: [
21879         {
21880             tag: 'tr',
21881             cn: [
21882             {
21883                 tag: 'td',
21884                 colspan: '7'
21885             }
21886             ]
21887         }
21888         ]
21889     },
21890     
21891     footer : {
21892         tag: 'tfoot',
21893         cn: [
21894         {
21895             tag: 'tr',
21896             cn: [
21897             {
21898                 tag: 'th',
21899                 colspan: '7',
21900                 cls: 'today'
21901             }
21902                     
21903             ]
21904         }
21905         ]
21906     },
21907     
21908     dates:{
21909         en: {
21910             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21911             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21912             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21913             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21914             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21915             today: "Today"
21916         }
21917     },
21918     
21919     modes: [
21920     {
21921         clsName: 'days',
21922         navFnc: 'Month',
21923         navStep: 1
21924     },
21925     {
21926         clsName: 'months',
21927         navFnc: 'FullYear',
21928         navStep: 1
21929     },
21930     {
21931         clsName: 'years',
21932         navFnc: 'FullYear',
21933         navStep: 10
21934     }]
21935 });
21936
21937 Roo.apply(Roo.bootstrap.DateField,  {
21938   
21939     template : {
21940         tag: 'div',
21941         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21942         cn: [
21943         {
21944             tag: 'div',
21945             cls: 'datepicker-days',
21946             cn: [
21947             {
21948                 tag: 'table',
21949                 cls: 'table-condensed',
21950                 cn:[
21951                 Roo.bootstrap.DateField.head,
21952                 {
21953                     tag: 'tbody'
21954                 },
21955                 Roo.bootstrap.DateField.footer
21956                 ]
21957             }
21958             ]
21959         },
21960         {
21961             tag: 'div',
21962             cls: 'datepicker-months',
21963             cn: [
21964             {
21965                 tag: 'table',
21966                 cls: 'table-condensed',
21967                 cn:[
21968                 Roo.bootstrap.DateField.head,
21969                 Roo.bootstrap.DateField.content,
21970                 Roo.bootstrap.DateField.footer
21971                 ]
21972             }
21973             ]
21974         },
21975         {
21976             tag: 'div',
21977             cls: 'datepicker-years',
21978             cn: [
21979             {
21980                 tag: 'table',
21981                 cls: 'table-condensed',
21982                 cn:[
21983                 Roo.bootstrap.DateField.head,
21984                 Roo.bootstrap.DateField.content,
21985                 Roo.bootstrap.DateField.footer
21986                 ]
21987             }
21988             ]
21989         }
21990         ]
21991     }
21992 });
21993
21994  
21995
21996  /*
21997  * - LGPL
21998  *
21999  * TimeField
22000  * 
22001  */
22002
22003 /**
22004  * @class Roo.bootstrap.TimeField
22005  * @extends Roo.bootstrap.Input
22006  * Bootstrap DateField class
22007  * 
22008  * 
22009  * @constructor
22010  * Create a new TimeField
22011  * @param {Object} config The config object
22012  */
22013
22014 Roo.bootstrap.TimeField = function(config){
22015     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22016     this.addEvents({
22017             /**
22018              * @event show
22019              * Fires when this field show.
22020              * @param {Roo.bootstrap.DateField} thisthis
22021              * @param {Mixed} date The date value
22022              */
22023             show : true,
22024             /**
22025              * @event show
22026              * Fires when this field hide.
22027              * @param {Roo.bootstrap.DateField} this
22028              * @param {Mixed} date The date value
22029              */
22030             hide : true,
22031             /**
22032              * @event select
22033              * Fires when select a date.
22034              * @param {Roo.bootstrap.DateField} this
22035              * @param {Mixed} date The date value
22036              */
22037             select : true
22038         });
22039 };
22040
22041 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22042     
22043     /**
22044      * @cfg {String} format
22045      * The default time format string which can be overriden for localization support.  The format must be
22046      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22047      */
22048     format : "H:i",
22049
22050     getAutoCreate : function()
22051     {
22052         this.after = '<i class="fa far fa-clock"></i>';
22053         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22054         
22055          
22056     },
22057     onRender: function(ct, position)
22058     {
22059         
22060         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22061                 
22062         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22063         
22064         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22065         
22066         this.pop = this.picker().select('>.datepicker-time',true).first();
22067         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22068         
22069         this.picker().on('mousedown', this.onMousedown, this);
22070         this.picker().on('click', this.onClick, this);
22071         
22072         this.picker().addClass('datepicker-dropdown');
22073     
22074         this.fillTime();
22075         this.update();
22076             
22077         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22078         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22079         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22080         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22081         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22082         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22083
22084     },
22085     
22086     fireKey: function(e){
22087         if (!this.picker().isVisible()){
22088             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22089                 this.show();
22090             }
22091             return;
22092         }
22093
22094         e.preventDefault();
22095         
22096         switch(e.keyCode){
22097             case 27: // escape
22098                 this.hide();
22099                 break;
22100             case 37: // left
22101             case 39: // right
22102                 this.onTogglePeriod();
22103                 break;
22104             case 38: // up
22105                 this.onIncrementMinutes();
22106                 break;
22107             case 40: // down
22108                 this.onDecrementMinutes();
22109                 break;
22110             case 13: // enter
22111             case 9: // tab
22112                 this.setTime();
22113                 break;
22114         }
22115     },
22116     
22117     onClick: function(e) {
22118         e.stopPropagation();
22119         e.preventDefault();
22120     },
22121     
22122     picker : function()
22123     {
22124         return this.pickerEl;
22125     },
22126     
22127     fillTime: function()
22128     {    
22129         var time = this.pop.select('tbody', true).first();
22130         
22131         time.dom.innerHTML = '';
22132         
22133         time.createChild({
22134             tag: 'tr',
22135             cn: [
22136                 {
22137                     tag: 'td',
22138                     cn: [
22139                         {
22140                             tag: 'a',
22141                             href: '#',
22142                             cls: 'btn',
22143                             cn: [
22144                                 {
22145                                     tag: 'i',
22146                                     cls: 'hours-up fa fas fa-chevron-up'
22147                                 }
22148                             ]
22149                         } 
22150                     ]
22151                 },
22152                 {
22153                     tag: 'td',
22154                     cls: 'separator'
22155                 },
22156                 {
22157                     tag: 'td',
22158                     cn: [
22159                         {
22160                             tag: 'a',
22161                             href: '#',
22162                             cls: 'btn',
22163                             cn: [
22164                                 {
22165                                     tag: 'i',
22166                                     cls: 'minutes-up fa fas fa-chevron-up'
22167                                 }
22168                             ]
22169                         }
22170                     ]
22171                 },
22172                 {
22173                     tag: 'td',
22174                     cls: 'separator'
22175                 }
22176             ]
22177         });
22178         
22179         time.createChild({
22180             tag: 'tr',
22181             cn: [
22182                 {
22183                     tag: 'td',
22184                     cn: [
22185                         {
22186                             tag: 'span',
22187                             cls: 'timepicker-hour',
22188                             html: '00'
22189                         }  
22190                     ]
22191                 },
22192                 {
22193                     tag: 'td',
22194                     cls: 'separator',
22195                     html: ':'
22196                 },
22197                 {
22198                     tag: 'td',
22199                     cn: [
22200                         {
22201                             tag: 'span',
22202                             cls: 'timepicker-minute',
22203                             html: '00'
22204                         }  
22205                     ]
22206                 },
22207                 {
22208                     tag: 'td',
22209                     cls: 'separator'
22210                 },
22211                 {
22212                     tag: 'td',
22213                     cn: [
22214                         {
22215                             tag: 'button',
22216                             type: 'button',
22217                             cls: 'btn btn-primary period',
22218                             html: 'AM'
22219                             
22220                         }
22221                     ]
22222                 }
22223             ]
22224         });
22225         
22226         time.createChild({
22227             tag: 'tr',
22228             cn: [
22229                 {
22230                     tag: 'td',
22231                     cn: [
22232                         {
22233                             tag: 'a',
22234                             href: '#',
22235                             cls: 'btn',
22236                             cn: [
22237                                 {
22238                                     tag: 'span',
22239                                     cls: 'hours-down fa fas fa-chevron-down'
22240                                 }
22241                             ]
22242                         }
22243                     ]
22244                 },
22245                 {
22246                     tag: 'td',
22247                     cls: 'separator'
22248                 },
22249                 {
22250                     tag: 'td',
22251                     cn: [
22252                         {
22253                             tag: 'a',
22254                             href: '#',
22255                             cls: 'btn',
22256                             cn: [
22257                                 {
22258                                     tag: 'span',
22259                                     cls: 'minutes-down fa fas fa-chevron-down'
22260                                 }
22261                             ]
22262                         }
22263                     ]
22264                 },
22265                 {
22266                     tag: 'td',
22267                     cls: 'separator'
22268                 }
22269             ]
22270         });
22271         
22272     },
22273     
22274     update: function()
22275     {
22276         
22277         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22278         
22279         this.fill();
22280     },
22281     
22282     fill: function() 
22283     {
22284         var hours = this.time.getHours();
22285         var minutes = this.time.getMinutes();
22286         var period = 'AM';
22287         
22288         if(hours > 11){
22289             period = 'PM';
22290         }
22291         
22292         if(hours == 0){
22293             hours = 12;
22294         }
22295         
22296         
22297         if(hours > 12){
22298             hours = hours - 12;
22299         }
22300         
22301         if(hours < 10){
22302             hours = '0' + hours;
22303         }
22304         
22305         if(minutes < 10){
22306             minutes = '0' + minutes;
22307         }
22308         
22309         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22310         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22311         this.pop.select('button', true).first().dom.innerHTML = period;
22312         
22313     },
22314     
22315     place: function()
22316     {   
22317         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22318         
22319         var cls = ['bottom'];
22320         
22321         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22322             cls.pop();
22323             cls.push('top');
22324         }
22325         
22326         cls.push('right');
22327         
22328         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22329             cls.pop();
22330             cls.push('left');
22331         }
22332         //this.picker().setXY(20000,20000);
22333         this.picker().addClass(cls.join('-'));
22334         
22335         var _this = this;
22336         
22337         Roo.each(cls, function(c){
22338             if(c == 'bottom'){
22339                 (function() {
22340                  //  
22341                 }).defer(200);
22342                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22343                 //_this.picker().setTop(_this.inputEl().getHeight());
22344                 return;
22345             }
22346             if(c == 'top'){
22347                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22348                 
22349                 //_this.picker().setTop(0 - _this.picker().getHeight());
22350                 return;
22351             }
22352             /*
22353             if(c == 'left'){
22354                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22355                 return;
22356             }
22357             if(c == 'right'){
22358                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22359                 return;
22360             }
22361             */
22362         });
22363         
22364     },
22365   
22366     onFocus : function()
22367     {
22368         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22369         this.show();
22370     },
22371     
22372     onBlur : function()
22373     {
22374         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22375         this.hide();
22376     },
22377     
22378     show : function()
22379     {
22380         this.picker().show();
22381         this.pop.show();
22382         this.update();
22383         this.place();
22384         
22385         this.fireEvent('show', this, this.date);
22386     },
22387     
22388     hide : function()
22389     {
22390         this.picker().hide();
22391         this.pop.hide();
22392         
22393         this.fireEvent('hide', this, this.date);
22394     },
22395     
22396     setTime : function()
22397     {
22398         this.hide();
22399         this.setValue(this.time.format(this.format));
22400         
22401         this.fireEvent('select', this, this.date);
22402         
22403         
22404     },
22405     
22406     onMousedown: function(e){
22407         e.stopPropagation();
22408         e.preventDefault();
22409     },
22410     
22411     onIncrementHours: function()
22412     {
22413         Roo.log('onIncrementHours');
22414         this.time = this.time.add(Date.HOUR, 1);
22415         this.update();
22416         
22417     },
22418     
22419     onDecrementHours: function()
22420     {
22421         Roo.log('onDecrementHours');
22422         this.time = this.time.add(Date.HOUR, -1);
22423         this.update();
22424     },
22425     
22426     onIncrementMinutes: function()
22427     {
22428         Roo.log('onIncrementMinutes');
22429         this.time = this.time.add(Date.MINUTE, 1);
22430         this.update();
22431     },
22432     
22433     onDecrementMinutes: function()
22434     {
22435         Roo.log('onDecrementMinutes');
22436         this.time = this.time.add(Date.MINUTE, -1);
22437         this.update();
22438     },
22439     
22440     onTogglePeriod: function()
22441     {
22442         Roo.log('onTogglePeriod');
22443         this.time = this.time.add(Date.HOUR, 12);
22444         this.update();
22445     }
22446     
22447    
22448 });
22449  
22450
22451 Roo.apply(Roo.bootstrap.TimeField,  {
22452   
22453     template : {
22454         tag: 'div',
22455         cls: 'datepicker dropdown-menu',
22456         cn: [
22457             {
22458                 tag: 'div',
22459                 cls: 'datepicker-time',
22460                 cn: [
22461                 {
22462                     tag: 'table',
22463                     cls: 'table-condensed',
22464                     cn:[
22465                         {
22466                             tag: 'tbody',
22467                             cn: [
22468                                 {
22469                                     tag: 'tr',
22470                                     cn: [
22471                                     {
22472                                         tag: 'td',
22473                                         colspan: '7'
22474                                     }
22475                                     ]
22476                                 }
22477                             ]
22478                         },
22479                         {
22480                             tag: 'tfoot',
22481                             cn: [
22482                                 {
22483                                     tag: 'tr',
22484                                     cn: [
22485                                     {
22486                                         tag: 'th',
22487                                         colspan: '7',
22488                                         cls: '',
22489                                         cn: [
22490                                             {
22491                                                 tag: 'button',
22492                                                 cls: 'btn btn-info ok',
22493                                                 html: 'OK'
22494                                             }
22495                                         ]
22496                                     }
22497                     
22498                                     ]
22499                                 }
22500                             ]
22501                         }
22502                     ]
22503                 }
22504                 ]
22505             }
22506         ]
22507     }
22508 });
22509
22510  
22511
22512  /*
22513  * - LGPL
22514  *
22515  * MonthField
22516  * 
22517  */
22518
22519 /**
22520  * @class Roo.bootstrap.MonthField
22521  * @extends Roo.bootstrap.Input
22522  * Bootstrap MonthField class
22523  * 
22524  * @cfg {String} language default en
22525  * 
22526  * @constructor
22527  * Create a new MonthField
22528  * @param {Object} config The config object
22529  */
22530
22531 Roo.bootstrap.MonthField = function(config){
22532     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22533     
22534     this.addEvents({
22535         /**
22536          * @event show
22537          * Fires when this field show.
22538          * @param {Roo.bootstrap.MonthField} this
22539          * @param {Mixed} date The date value
22540          */
22541         show : true,
22542         /**
22543          * @event show
22544          * Fires when this field hide.
22545          * @param {Roo.bootstrap.MonthField} this
22546          * @param {Mixed} date The date value
22547          */
22548         hide : true,
22549         /**
22550          * @event select
22551          * Fires when select a date.
22552          * @param {Roo.bootstrap.MonthField} this
22553          * @param {String} oldvalue The old value
22554          * @param {String} newvalue The new value
22555          */
22556         select : true
22557     });
22558 };
22559
22560 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22561     
22562     onRender: function(ct, position)
22563     {
22564         
22565         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22566         
22567         this.language = this.language || 'en';
22568         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22569         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22570         
22571         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22572         this.isInline = false;
22573         this.isInput = true;
22574         this.component = this.el.select('.add-on', true).first() || false;
22575         this.component = (this.component && this.component.length === 0) ? false : this.component;
22576         this.hasInput = this.component && this.inputEL().length;
22577         
22578         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22579         
22580         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22581         
22582         this.picker().on('mousedown', this.onMousedown, this);
22583         this.picker().on('click', this.onClick, this);
22584         
22585         this.picker().addClass('datepicker-dropdown');
22586         
22587         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22588             v.setStyle('width', '189px');
22589         });
22590         
22591         this.fillMonths();
22592         
22593         this.update();
22594         
22595         if(this.isInline) {
22596             this.show();
22597         }
22598         
22599     },
22600     
22601     setValue: function(v, suppressEvent)
22602     {   
22603         var o = this.getValue();
22604         
22605         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22606         
22607         this.update();
22608
22609         if(suppressEvent !== true){
22610             this.fireEvent('select', this, o, v);
22611         }
22612         
22613     },
22614     
22615     getValue: function()
22616     {
22617         return this.value;
22618     },
22619     
22620     onClick: function(e) 
22621     {
22622         e.stopPropagation();
22623         e.preventDefault();
22624         
22625         var target = e.getTarget();
22626         
22627         if(target.nodeName.toLowerCase() === 'i'){
22628             target = Roo.get(target).dom.parentNode;
22629         }
22630         
22631         var nodeName = target.nodeName;
22632         var className = target.className;
22633         var html = target.innerHTML;
22634         
22635         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22636             return;
22637         }
22638         
22639         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22640         
22641         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22642         
22643         this.hide();
22644                         
22645     },
22646     
22647     picker : function()
22648     {
22649         return this.pickerEl;
22650     },
22651     
22652     fillMonths: function()
22653     {    
22654         var i = 0;
22655         var months = this.picker().select('>.datepicker-months td', true).first();
22656         
22657         months.dom.innerHTML = '';
22658         
22659         while (i < 12) {
22660             var month = {
22661                 tag: 'span',
22662                 cls: 'month',
22663                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22664             };
22665             
22666             months.createChild(month);
22667         }
22668         
22669     },
22670     
22671     update: function()
22672     {
22673         var _this = this;
22674         
22675         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22676             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22677         }
22678         
22679         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22680             e.removeClass('active');
22681             
22682             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22683                 e.addClass('active');
22684             }
22685         })
22686     },
22687     
22688     place: function()
22689     {
22690         if(this.isInline) {
22691             return;
22692         }
22693         
22694         this.picker().removeClass(['bottom', 'top']);
22695         
22696         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22697             /*
22698              * place to the top of element!
22699              *
22700              */
22701             
22702             this.picker().addClass('top');
22703             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22704             
22705             return;
22706         }
22707         
22708         this.picker().addClass('bottom');
22709         
22710         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22711     },
22712     
22713     onFocus : function()
22714     {
22715         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22716         this.show();
22717     },
22718     
22719     onBlur : function()
22720     {
22721         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22722         
22723         var d = this.inputEl().getValue();
22724         
22725         this.setValue(d);
22726                 
22727         this.hide();
22728     },
22729     
22730     show : function()
22731     {
22732         this.picker().show();
22733         this.picker().select('>.datepicker-months', true).first().show();
22734         this.update();
22735         this.place();
22736         
22737         this.fireEvent('show', this, this.date);
22738     },
22739     
22740     hide : function()
22741     {
22742         if(this.isInline) {
22743             return;
22744         }
22745         this.picker().hide();
22746         this.fireEvent('hide', this, this.date);
22747         
22748     },
22749     
22750     onMousedown: function(e)
22751     {
22752         e.stopPropagation();
22753         e.preventDefault();
22754     },
22755     
22756     keyup: function(e)
22757     {
22758         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22759         this.update();
22760     },
22761
22762     fireKey: function(e)
22763     {
22764         if (!this.picker().isVisible()){
22765             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22766                 this.show();
22767             }
22768             return;
22769         }
22770         
22771         var dir;
22772         
22773         switch(e.keyCode){
22774             case 27: // escape
22775                 this.hide();
22776                 e.preventDefault();
22777                 break;
22778             case 37: // left
22779             case 39: // right
22780                 dir = e.keyCode == 37 ? -1 : 1;
22781                 
22782                 this.vIndex = this.vIndex + dir;
22783                 
22784                 if(this.vIndex < 0){
22785                     this.vIndex = 0;
22786                 }
22787                 
22788                 if(this.vIndex > 11){
22789                     this.vIndex = 11;
22790                 }
22791                 
22792                 if(isNaN(this.vIndex)){
22793                     this.vIndex = 0;
22794                 }
22795                 
22796                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22797                 
22798                 break;
22799             case 38: // up
22800             case 40: // down
22801                 
22802                 dir = e.keyCode == 38 ? -1 : 1;
22803                 
22804                 this.vIndex = this.vIndex + dir * 4;
22805                 
22806                 if(this.vIndex < 0){
22807                     this.vIndex = 0;
22808                 }
22809                 
22810                 if(this.vIndex > 11){
22811                     this.vIndex = 11;
22812                 }
22813                 
22814                 if(isNaN(this.vIndex)){
22815                     this.vIndex = 0;
22816                 }
22817                 
22818                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22819                 break;
22820                 
22821             case 13: // enter
22822                 
22823                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22824                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22825                 }
22826                 
22827                 this.hide();
22828                 e.preventDefault();
22829                 break;
22830             case 9: // tab
22831                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22832                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22833                 }
22834                 this.hide();
22835                 break;
22836             case 16: // shift
22837             case 17: // ctrl
22838             case 18: // alt
22839                 break;
22840             default :
22841                 this.hide();
22842                 
22843         }
22844     },
22845     
22846     remove: function() 
22847     {
22848         this.picker().remove();
22849     }
22850    
22851 });
22852
22853 Roo.apply(Roo.bootstrap.MonthField,  {
22854     
22855     content : {
22856         tag: 'tbody',
22857         cn: [
22858         {
22859             tag: 'tr',
22860             cn: [
22861             {
22862                 tag: 'td',
22863                 colspan: '7'
22864             }
22865             ]
22866         }
22867         ]
22868     },
22869     
22870     dates:{
22871         en: {
22872             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22873             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22874         }
22875     }
22876 });
22877
22878 Roo.apply(Roo.bootstrap.MonthField,  {
22879   
22880     template : {
22881         tag: 'div',
22882         cls: 'datepicker dropdown-menu roo-dynamic',
22883         cn: [
22884             {
22885                 tag: 'div',
22886                 cls: 'datepicker-months',
22887                 cn: [
22888                 {
22889                     tag: 'table',
22890                     cls: 'table-condensed',
22891                     cn:[
22892                         Roo.bootstrap.DateField.content
22893                     ]
22894                 }
22895                 ]
22896             }
22897         ]
22898     }
22899 });
22900
22901  
22902
22903  
22904  /*
22905  * - LGPL
22906  *
22907  * CheckBox
22908  * 
22909  */
22910
22911 /**
22912  * @class Roo.bootstrap.CheckBox
22913  * @extends Roo.bootstrap.Input
22914  * Bootstrap CheckBox class
22915  * 
22916  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22917  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22918  * @cfg {String} boxLabel The text that appears beside the checkbox
22919  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22920  * @cfg {Boolean} checked initnal the element
22921  * @cfg {Boolean} inline inline the element (default false)
22922  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22923  * @cfg {String} tooltip label tooltip
22924  * 
22925  * @constructor
22926  * Create a new CheckBox
22927  * @param {Object} config The config object
22928  */
22929
22930 Roo.bootstrap.CheckBox = function(config){
22931     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22932    
22933     this.addEvents({
22934         /**
22935         * @event check
22936         * Fires when the element is checked or unchecked.
22937         * @param {Roo.bootstrap.CheckBox} this This input
22938         * @param {Boolean} checked The new checked value
22939         */
22940        check : true,
22941        /**
22942         * @event click
22943         * Fires when the element is click.
22944         * @param {Roo.bootstrap.CheckBox} this This input
22945         */
22946        click : true
22947     });
22948     
22949 };
22950
22951 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22952   
22953     inputType: 'checkbox',
22954     inputValue: 1,
22955     valueOff: 0,
22956     boxLabel: false,
22957     checked: false,
22958     weight : false,
22959     inline: false,
22960     tooltip : '',
22961     
22962     // checkbox success does not make any sense really.. 
22963     invalidClass : "",
22964     validClass : "",
22965     
22966     
22967     getAutoCreate : function()
22968     {
22969         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22970         
22971         var id = Roo.id();
22972         
22973         var cfg = {};
22974         
22975         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22976         
22977         if(this.inline){
22978             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22979         }
22980         
22981         var input =  {
22982             tag: 'input',
22983             id : id,
22984             type : this.inputType,
22985             value : this.inputValue,
22986             cls : 'roo-' + this.inputType, //'form-box',
22987             placeholder : this.placeholder || ''
22988             
22989         };
22990         
22991         if(this.inputType != 'radio'){
22992             var hidden =  {
22993                 tag: 'input',
22994                 type : 'hidden',
22995                 cls : 'roo-hidden-value',
22996                 value : this.checked ? this.inputValue : this.valueOff
22997             };
22998         }
22999         
23000             
23001         if (this.weight) { // Validity check?
23002             cfg.cls += " " + this.inputType + "-" + this.weight;
23003         }
23004         
23005         if (this.disabled) {
23006             input.disabled=true;
23007         }
23008         
23009         if(this.checked){
23010             input.checked = this.checked;
23011         }
23012         
23013         if (this.name) {
23014             
23015             input.name = this.name;
23016             
23017             if(this.inputType != 'radio'){
23018                 hidden.name = this.name;
23019                 input.name = '_hidden_' + this.name;
23020             }
23021         }
23022         
23023         if (this.size) {
23024             input.cls += ' input-' + this.size;
23025         }
23026         
23027         var settings=this;
23028         
23029         ['xs','sm','md','lg'].map(function(size){
23030             if (settings[size]) {
23031                 cfg.cls += ' col-' + size + '-' + settings[size];
23032             }
23033         });
23034         
23035         var inputblock = input;
23036          
23037         if (this.before || this.after) {
23038             
23039             inputblock = {
23040                 cls : 'input-group',
23041                 cn :  [] 
23042             };
23043             
23044             if (this.before) {
23045                 inputblock.cn.push({
23046                     tag :'span',
23047                     cls : 'input-group-addon',
23048                     html : this.before
23049                 });
23050             }
23051             
23052             inputblock.cn.push(input);
23053             
23054             if(this.inputType != 'radio'){
23055                 inputblock.cn.push(hidden);
23056             }
23057             
23058             if (this.after) {
23059                 inputblock.cn.push({
23060                     tag :'span',
23061                     cls : 'input-group-addon',
23062                     html : this.after
23063                 });
23064             }
23065             
23066         }
23067         var boxLabelCfg = false;
23068         
23069         if(this.boxLabel){
23070            
23071             boxLabelCfg = {
23072                 tag: 'label',
23073                 //'for': id, // box label is handled by onclick - so no for...
23074                 cls: 'box-label',
23075                 html: this.boxLabel
23076             };
23077             if(this.tooltip){
23078                 boxLabelCfg.tooltip = this.tooltip;
23079             }
23080              
23081         }
23082         
23083         
23084         if (align ==='left' && this.fieldLabel.length) {
23085 //                Roo.log("left and has label");
23086             cfg.cn = [
23087                 {
23088                     tag: 'label',
23089                     'for' :  id,
23090                     cls : 'control-label',
23091                     html : this.fieldLabel
23092                 },
23093                 {
23094                     cls : "", 
23095                     cn: [
23096                         inputblock
23097                     ]
23098                 }
23099             ];
23100             
23101             if (boxLabelCfg) {
23102                 cfg.cn[1].cn.push(boxLabelCfg);
23103             }
23104             
23105             if(this.labelWidth > 12){
23106                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23107             }
23108             
23109             if(this.labelWidth < 13 && this.labelmd == 0){
23110                 this.labelmd = this.labelWidth;
23111             }
23112             
23113             if(this.labellg > 0){
23114                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23115                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23116             }
23117             
23118             if(this.labelmd > 0){
23119                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23120                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23121             }
23122             
23123             if(this.labelsm > 0){
23124                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23125                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23126             }
23127             
23128             if(this.labelxs > 0){
23129                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23130                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23131             }
23132             
23133         } else if ( this.fieldLabel.length) {
23134 //                Roo.log(" label");
23135                 cfg.cn = [
23136                    
23137                     {
23138                         tag: this.boxLabel ? 'span' : 'label',
23139                         'for': id,
23140                         cls: 'control-label box-input-label',
23141                         //cls : 'input-group-addon',
23142                         html : this.fieldLabel
23143                     },
23144                     
23145                     inputblock
23146                     
23147                 ];
23148                 if (boxLabelCfg) {
23149                     cfg.cn.push(boxLabelCfg);
23150                 }
23151
23152         } else {
23153             
23154 //                Roo.log(" no label && no align");
23155                 cfg.cn = [  inputblock ] ;
23156                 if (boxLabelCfg) {
23157                     cfg.cn.push(boxLabelCfg);
23158                 }
23159
23160                 
23161         }
23162         
23163        
23164         
23165         if(this.inputType != 'radio'){
23166             cfg.cn.push(hidden);
23167         }
23168         
23169         return cfg;
23170         
23171     },
23172     
23173     /**
23174      * return the real input element.
23175      */
23176     inputEl: function ()
23177     {
23178         return this.el.select('input.roo-' + this.inputType,true).first();
23179     },
23180     hiddenEl: function ()
23181     {
23182         return this.el.select('input.roo-hidden-value',true).first();
23183     },
23184     
23185     labelEl: function()
23186     {
23187         return this.el.select('label.control-label',true).first();
23188     },
23189     /* depricated... */
23190     
23191     label: function()
23192     {
23193         return this.labelEl();
23194     },
23195     
23196     boxLabelEl: function()
23197     {
23198         return this.el.select('label.box-label',true).first();
23199     },
23200     
23201     initEvents : function()
23202     {
23203 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23204         
23205         this.inputEl().on('click', this.onClick,  this);
23206         
23207         if (this.boxLabel) { 
23208             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23209         }
23210         
23211         this.startValue = this.getValue();
23212         
23213         if(this.groupId){
23214             Roo.bootstrap.CheckBox.register(this);
23215         }
23216     },
23217     
23218     onClick : function(e)
23219     {   
23220         if(this.fireEvent('click', this, e) !== false){
23221             this.setChecked(!this.checked);
23222         }
23223         
23224     },
23225     
23226     setChecked : function(state,suppressEvent)
23227     {
23228         this.startValue = this.getValue();
23229
23230         if(this.inputType == 'radio'){
23231             
23232             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23233                 e.dom.checked = false;
23234             });
23235             
23236             this.inputEl().dom.checked = true;
23237             
23238             this.inputEl().dom.value = this.inputValue;
23239             
23240             if(suppressEvent !== true){
23241                 this.fireEvent('check', this, true);
23242             }
23243             
23244             this.validate();
23245             
23246             return;
23247         }
23248         
23249         this.checked = state;
23250         
23251         this.inputEl().dom.checked = state;
23252         
23253         
23254         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23255         
23256         if(suppressEvent !== true){
23257             this.fireEvent('check', this, state);
23258         }
23259         
23260         this.validate();
23261     },
23262     
23263     getValue : function()
23264     {
23265         if(this.inputType == 'radio'){
23266             return this.getGroupValue();
23267         }
23268         
23269         return this.hiddenEl().dom.value;
23270         
23271     },
23272     
23273     getGroupValue : function()
23274     {
23275         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23276             return '';
23277         }
23278         
23279         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23280     },
23281     
23282     setValue : function(v,suppressEvent)
23283     {
23284         if(this.inputType == 'radio'){
23285             this.setGroupValue(v, suppressEvent);
23286             return;
23287         }
23288         
23289         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23290         
23291         this.validate();
23292     },
23293     
23294     setGroupValue : function(v, suppressEvent)
23295     {
23296         this.startValue = this.getValue();
23297         
23298         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23299             e.dom.checked = false;
23300             
23301             if(e.dom.value == v){
23302                 e.dom.checked = true;
23303             }
23304         });
23305         
23306         if(suppressEvent !== true){
23307             this.fireEvent('check', this, true);
23308         }
23309
23310         this.validate();
23311         
23312         return;
23313     },
23314     
23315     validate : function()
23316     {
23317         if(this.getVisibilityEl().hasClass('hidden')){
23318             return true;
23319         }
23320         
23321         if(
23322                 this.disabled || 
23323                 (this.inputType == 'radio' && this.validateRadio()) ||
23324                 (this.inputType == 'checkbox' && this.validateCheckbox())
23325         ){
23326             this.markValid();
23327             return true;
23328         }
23329         
23330         this.markInvalid();
23331         return false;
23332     },
23333     
23334     validateRadio : function()
23335     {
23336         if(this.getVisibilityEl().hasClass('hidden')){
23337             return true;
23338         }
23339         
23340         if(this.allowBlank){
23341             return true;
23342         }
23343         
23344         var valid = false;
23345         
23346         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23347             if(!e.dom.checked){
23348                 return;
23349             }
23350             
23351             valid = true;
23352             
23353             return false;
23354         });
23355         
23356         return valid;
23357     },
23358     
23359     validateCheckbox : function()
23360     {
23361         if(!this.groupId){
23362             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23363             //return (this.getValue() == this.inputValue) ? true : false;
23364         }
23365         
23366         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23367         
23368         if(!group){
23369             return false;
23370         }
23371         
23372         var r = false;
23373         
23374         for(var i in group){
23375             if(group[i].el.isVisible(true)){
23376                 r = false;
23377                 break;
23378             }
23379             
23380             r = true;
23381         }
23382         
23383         for(var i in group){
23384             if(r){
23385                 break;
23386             }
23387             
23388             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23389         }
23390         
23391         return r;
23392     },
23393     
23394     /**
23395      * Mark this field as valid
23396      */
23397     markValid : function()
23398     {
23399         var _this = this;
23400         
23401         this.fireEvent('valid', this);
23402         
23403         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23404         
23405         if(this.groupId){
23406             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23407         }
23408         
23409         if(label){
23410             label.markValid();
23411         }
23412
23413         if(this.inputType == 'radio'){
23414             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23415                 var fg = e.findParent('.form-group', false, true);
23416                 if (Roo.bootstrap.version == 3) {
23417                     fg.removeClass([_this.invalidClass, _this.validClass]);
23418                     fg.addClass(_this.validClass);
23419                 } else {
23420                     fg.removeClass(['is-valid', 'is-invalid']);
23421                     fg.addClass('is-valid');
23422                 }
23423             });
23424             
23425             return;
23426         }
23427
23428         if(!this.groupId){
23429             var fg = this.el.findParent('.form-group', false, true);
23430             if (Roo.bootstrap.version == 3) {
23431                 fg.removeClass([this.invalidClass, this.validClass]);
23432                 fg.addClass(this.validClass);
23433             } else {
23434                 fg.removeClass(['is-valid', 'is-invalid']);
23435                 fg.addClass('is-valid');
23436             }
23437             return;
23438         }
23439         
23440         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23441         
23442         if(!group){
23443             return;
23444         }
23445         
23446         for(var i in group){
23447             var fg = group[i].el.findParent('.form-group', false, true);
23448             if (Roo.bootstrap.version == 3) {
23449                 fg.removeClass([this.invalidClass, this.validClass]);
23450                 fg.addClass(this.validClass);
23451             } else {
23452                 fg.removeClass(['is-valid', 'is-invalid']);
23453                 fg.addClass('is-valid');
23454             }
23455         }
23456     },
23457     
23458      /**
23459      * Mark this field as invalid
23460      * @param {String} msg The validation message
23461      */
23462     markInvalid : function(msg)
23463     {
23464         if(this.allowBlank){
23465             return;
23466         }
23467         
23468         var _this = this;
23469         
23470         this.fireEvent('invalid', this, msg);
23471         
23472         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23473         
23474         if(this.groupId){
23475             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23476         }
23477         
23478         if(label){
23479             label.markInvalid();
23480         }
23481             
23482         if(this.inputType == 'radio'){
23483             
23484             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23485                 var fg = e.findParent('.form-group', false, true);
23486                 if (Roo.bootstrap.version == 3) {
23487                     fg.removeClass([_this.invalidClass, _this.validClass]);
23488                     fg.addClass(_this.invalidClass);
23489                 } else {
23490                     fg.removeClass(['is-invalid', 'is-valid']);
23491                     fg.addClass('is-invalid');
23492                 }
23493             });
23494             
23495             return;
23496         }
23497         
23498         if(!this.groupId){
23499             var fg = this.el.findParent('.form-group', false, true);
23500             if (Roo.bootstrap.version == 3) {
23501                 fg.removeClass([_this.invalidClass, _this.validClass]);
23502                 fg.addClass(_this.invalidClass);
23503             } else {
23504                 fg.removeClass(['is-invalid', 'is-valid']);
23505                 fg.addClass('is-invalid');
23506             }
23507             return;
23508         }
23509         
23510         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23511         
23512         if(!group){
23513             return;
23514         }
23515         
23516         for(var i in group){
23517             var fg = group[i].el.findParent('.form-group', false, true);
23518             if (Roo.bootstrap.version == 3) {
23519                 fg.removeClass([_this.invalidClass, _this.validClass]);
23520                 fg.addClass(_this.invalidClass);
23521             } else {
23522                 fg.removeClass(['is-invalid', 'is-valid']);
23523                 fg.addClass('is-invalid');
23524             }
23525         }
23526         
23527     },
23528     
23529     clearInvalid : function()
23530     {
23531         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23532         
23533         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23534         
23535         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23536         
23537         if (label && label.iconEl) {
23538             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23539             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23540         }
23541     },
23542     
23543     disable : function()
23544     {
23545         if(this.inputType != 'radio'){
23546             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23547             return;
23548         }
23549         
23550         var _this = this;
23551         
23552         if(this.rendered){
23553             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23554                 _this.getActionEl().addClass(this.disabledClass);
23555                 e.dom.disabled = true;
23556             });
23557         }
23558         
23559         this.disabled = true;
23560         this.fireEvent("disable", this);
23561         return this;
23562     },
23563
23564     enable : function()
23565     {
23566         if(this.inputType != 'radio'){
23567             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23568             return;
23569         }
23570         
23571         var _this = this;
23572         
23573         if(this.rendered){
23574             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23575                 _this.getActionEl().removeClass(this.disabledClass);
23576                 e.dom.disabled = false;
23577             });
23578         }
23579         
23580         this.disabled = false;
23581         this.fireEvent("enable", this);
23582         return this;
23583     },
23584     
23585     setBoxLabel : function(v)
23586     {
23587         this.boxLabel = v;
23588         
23589         if(this.rendered){
23590             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23591         }
23592     }
23593
23594 });
23595
23596 Roo.apply(Roo.bootstrap.CheckBox, {
23597     
23598     groups: {},
23599     
23600      /**
23601     * register a CheckBox Group
23602     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23603     */
23604     register : function(checkbox)
23605     {
23606         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23607             this.groups[checkbox.groupId] = {};
23608         }
23609         
23610         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23611             return;
23612         }
23613         
23614         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23615         
23616     },
23617     /**
23618     * fetch a CheckBox Group based on the group ID
23619     * @param {string} the group ID
23620     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23621     */
23622     get: function(groupId) {
23623         if (typeof(this.groups[groupId]) == 'undefined') {
23624             return false;
23625         }
23626         
23627         return this.groups[groupId] ;
23628     }
23629     
23630     
23631 });
23632 /*
23633  * - LGPL
23634  *
23635  * RadioItem
23636  * 
23637  */
23638
23639 /**
23640  * @class Roo.bootstrap.Radio
23641  * @extends Roo.bootstrap.Component
23642  * Bootstrap Radio class
23643  * @cfg {String} boxLabel - the label associated
23644  * @cfg {String} value - the value of radio
23645  * 
23646  * @constructor
23647  * Create a new Radio
23648  * @param {Object} config The config object
23649  */
23650 Roo.bootstrap.Radio = function(config){
23651     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23652     
23653 };
23654
23655 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23656     
23657     boxLabel : '',
23658     
23659     value : '',
23660     
23661     getAutoCreate : function()
23662     {
23663         var cfg = {
23664             tag : 'div',
23665             cls : 'form-group radio',
23666             cn : [
23667                 {
23668                     tag : 'label',
23669                     cls : 'box-label',
23670                     html : this.boxLabel
23671                 }
23672             ]
23673         };
23674         
23675         return cfg;
23676     },
23677     
23678     initEvents : function() 
23679     {
23680         this.parent().register(this);
23681         
23682         this.el.on('click', this.onClick, this);
23683         
23684     },
23685     
23686     onClick : function(e)
23687     {
23688         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23689             this.setChecked(true);
23690         }
23691     },
23692     
23693     setChecked : function(state, suppressEvent)
23694     {
23695         this.parent().setValue(this.value, suppressEvent);
23696         
23697     },
23698     
23699     setBoxLabel : function(v)
23700     {
23701         this.boxLabel = v;
23702         
23703         if(this.rendered){
23704             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23705         }
23706     }
23707     
23708 });
23709  
23710
23711  /*
23712  * - LGPL
23713  *
23714  * Input
23715  * 
23716  */
23717
23718 /**
23719  * @class Roo.bootstrap.SecurePass
23720  * @extends Roo.bootstrap.Input
23721  * Bootstrap SecurePass class
23722  *
23723  * 
23724  * @constructor
23725  * Create a new SecurePass
23726  * @param {Object} config The config object
23727  */
23728  
23729 Roo.bootstrap.SecurePass = function (config) {
23730     // these go here, so the translation tool can replace them..
23731     this.errors = {
23732         PwdEmpty: "Please type a password, and then retype it to confirm.",
23733         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23734         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23735         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23736         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23737         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23738         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23739         TooWeak: "Your password is Too Weak."
23740     },
23741     this.meterLabel = "Password strength:";
23742     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23743     this.meterClass = [
23744         "roo-password-meter-tooweak", 
23745         "roo-password-meter-weak", 
23746         "roo-password-meter-medium", 
23747         "roo-password-meter-strong", 
23748         "roo-password-meter-grey"
23749     ];
23750     
23751     this.errors = {};
23752     
23753     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23754 }
23755
23756 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23757     /**
23758      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23759      * {
23760      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23761      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23762      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23763      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23764      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23765      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23766      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23767      * })
23768      */
23769     // private
23770     
23771     meterWidth: 300,
23772     errorMsg :'',    
23773     errors: false,
23774     imageRoot: '/',
23775     /**
23776      * @cfg {String/Object} Label for the strength meter (defaults to
23777      * 'Password strength:')
23778      */
23779     // private
23780     meterLabel: '',
23781     /**
23782      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23783      * ['Weak', 'Medium', 'Strong'])
23784      */
23785     // private    
23786     pwdStrengths: false,    
23787     // private
23788     strength: 0,
23789     // private
23790     _lastPwd: null,
23791     // private
23792     kCapitalLetter: 0,
23793     kSmallLetter: 1,
23794     kDigit: 2,
23795     kPunctuation: 3,
23796     
23797     insecure: false,
23798     // private
23799     initEvents: function ()
23800     {
23801         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23802
23803         if (this.el.is('input[type=password]') && Roo.isSafari) {
23804             this.el.on('keydown', this.SafariOnKeyDown, this);
23805         }
23806
23807         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23808     },
23809     // private
23810     onRender: function (ct, position)
23811     {
23812         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23813         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23814         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23815
23816         this.trigger.createChild({
23817                    cn: [
23818                     {
23819                     //id: 'PwdMeter',
23820                     tag: 'div',
23821                     cls: 'roo-password-meter-grey col-xs-12',
23822                     style: {
23823                         //width: 0,
23824                         //width: this.meterWidth + 'px'                                                
23825                         }
23826                     },
23827                     {                            
23828                          cls: 'roo-password-meter-text'                          
23829                     }
23830                 ]            
23831         });
23832
23833          
23834         if (this.hideTrigger) {
23835             this.trigger.setDisplayed(false);
23836         }
23837         this.setSize(this.width || '', this.height || '');
23838     },
23839     // private
23840     onDestroy: function ()
23841     {
23842         if (this.trigger) {
23843             this.trigger.removeAllListeners();
23844             this.trigger.remove();
23845         }
23846         if (this.wrap) {
23847             this.wrap.remove();
23848         }
23849         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23850     },
23851     // private
23852     checkStrength: function ()
23853     {
23854         var pwd = this.inputEl().getValue();
23855         if (pwd == this._lastPwd) {
23856             return;
23857         }
23858
23859         var strength;
23860         if (this.ClientSideStrongPassword(pwd)) {
23861             strength = 3;
23862         } else if (this.ClientSideMediumPassword(pwd)) {
23863             strength = 2;
23864         } else if (this.ClientSideWeakPassword(pwd)) {
23865             strength = 1;
23866         } else {
23867             strength = 0;
23868         }
23869         
23870         Roo.log('strength1: ' + strength);
23871         
23872         //var pm = this.trigger.child('div/div/div').dom;
23873         var pm = this.trigger.child('div/div');
23874         pm.removeClass(this.meterClass);
23875         pm.addClass(this.meterClass[strength]);
23876                 
23877         
23878         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23879                 
23880         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23881         
23882         this._lastPwd = pwd;
23883     },
23884     reset: function ()
23885     {
23886         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23887         
23888         this._lastPwd = '';
23889         
23890         var pm = this.trigger.child('div/div');
23891         pm.removeClass(this.meterClass);
23892         pm.addClass('roo-password-meter-grey');        
23893         
23894         
23895         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23896         
23897         pt.innerHTML = '';
23898         this.inputEl().dom.type='password';
23899     },
23900     // private
23901     validateValue: function (value)
23902     {
23903         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23904             return false;
23905         }
23906         if (value.length == 0) {
23907             if (this.allowBlank) {
23908                 this.clearInvalid();
23909                 return true;
23910             }
23911
23912             this.markInvalid(this.errors.PwdEmpty);
23913             this.errorMsg = this.errors.PwdEmpty;
23914             return false;
23915         }
23916         
23917         if(this.insecure){
23918             return true;
23919         }
23920         
23921         if (!value.match(/[\x21-\x7e]+/)) {
23922             this.markInvalid(this.errors.PwdBadChar);
23923             this.errorMsg = this.errors.PwdBadChar;
23924             return false;
23925         }
23926         if (value.length < 6) {
23927             this.markInvalid(this.errors.PwdShort);
23928             this.errorMsg = this.errors.PwdShort;
23929             return false;
23930         }
23931         if (value.length > 16) {
23932             this.markInvalid(this.errors.PwdLong);
23933             this.errorMsg = this.errors.PwdLong;
23934             return false;
23935         }
23936         var strength;
23937         if (this.ClientSideStrongPassword(value)) {
23938             strength = 3;
23939         } else if (this.ClientSideMediumPassword(value)) {
23940             strength = 2;
23941         } else if (this.ClientSideWeakPassword(value)) {
23942             strength = 1;
23943         } else {
23944             strength = 0;
23945         }
23946
23947         
23948         if (strength < 2) {
23949             //this.markInvalid(this.errors.TooWeak);
23950             this.errorMsg = this.errors.TooWeak;
23951             //return false;
23952         }
23953         
23954         
23955         console.log('strength2: ' + strength);
23956         
23957         //var pm = this.trigger.child('div/div/div').dom;
23958         
23959         var pm = this.trigger.child('div/div');
23960         pm.removeClass(this.meterClass);
23961         pm.addClass(this.meterClass[strength]);
23962                 
23963         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23964                 
23965         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23966         
23967         this.errorMsg = ''; 
23968         return true;
23969     },
23970     // private
23971     CharacterSetChecks: function (type)
23972     {
23973         this.type = type;
23974         this.fResult = false;
23975     },
23976     // private
23977     isctype: function (character, type)
23978     {
23979         switch (type) {  
23980             case this.kCapitalLetter:
23981                 if (character >= 'A' && character <= 'Z') {
23982                     return true;
23983                 }
23984                 break;
23985             
23986             case this.kSmallLetter:
23987                 if (character >= 'a' && character <= 'z') {
23988                     return true;
23989                 }
23990                 break;
23991             
23992             case this.kDigit:
23993                 if (character >= '0' && character <= '9') {
23994                     return true;
23995                 }
23996                 break;
23997             
23998             case this.kPunctuation:
23999                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24000                     return true;
24001                 }
24002                 break;
24003             
24004             default:
24005                 return false;
24006         }
24007
24008     },
24009     // private
24010     IsLongEnough: function (pwd, size)
24011     {
24012         return !(pwd == null || isNaN(size) || pwd.length < size);
24013     },
24014     // private
24015     SpansEnoughCharacterSets: function (word, nb)
24016     {
24017         if (!this.IsLongEnough(word, nb))
24018         {
24019             return false;
24020         }
24021
24022         var characterSetChecks = new Array(
24023             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24024             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24025         );
24026         
24027         for (var index = 0; index < word.length; ++index) {
24028             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24029                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24030                     characterSetChecks[nCharSet].fResult = true;
24031                     break;
24032                 }
24033             }
24034         }
24035
24036         var nCharSets = 0;
24037         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24038             if (characterSetChecks[nCharSet].fResult) {
24039                 ++nCharSets;
24040             }
24041         }
24042
24043         if (nCharSets < nb) {
24044             return false;
24045         }
24046         return true;
24047     },
24048     // private
24049     ClientSideStrongPassword: function (pwd)
24050     {
24051         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24052     },
24053     // private
24054     ClientSideMediumPassword: function (pwd)
24055     {
24056         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24057     },
24058     // private
24059     ClientSideWeakPassword: function (pwd)
24060     {
24061         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24062     }
24063           
24064 })//<script type="text/javascript">
24065
24066 /*
24067  * Based  Ext JS Library 1.1.1
24068  * Copyright(c) 2006-2007, Ext JS, LLC.
24069  * LGPL
24070  *
24071  */
24072  
24073 /**
24074  * @class Roo.HtmlEditorCore
24075  * @extends Roo.Component
24076  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24077  *
24078  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24079  */
24080
24081 Roo.HtmlEditorCore = function(config){
24082     
24083     
24084     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24085     
24086     
24087     this.addEvents({
24088         /**
24089          * @event initialize
24090          * Fires when the editor is fully initialized (including the iframe)
24091          * @param {Roo.HtmlEditorCore} this
24092          */
24093         initialize: true,
24094         /**
24095          * @event activate
24096          * Fires when the editor is first receives the focus. Any insertion must wait
24097          * until after this event.
24098          * @param {Roo.HtmlEditorCore} this
24099          */
24100         activate: true,
24101          /**
24102          * @event beforesync
24103          * Fires before the textarea is updated with content from the editor iframe. Return false
24104          * to cancel the sync.
24105          * @param {Roo.HtmlEditorCore} this
24106          * @param {String} html
24107          */
24108         beforesync: true,
24109          /**
24110          * @event beforepush
24111          * Fires before the iframe editor is updated with content from the textarea. Return false
24112          * to cancel the push.
24113          * @param {Roo.HtmlEditorCore} this
24114          * @param {String} html
24115          */
24116         beforepush: true,
24117          /**
24118          * @event sync
24119          * Fires when the textarea is updated with content from the editor iframe.
24120          * @param {Roo.HtmlEditorCore} this
24121          * @param {String} html
24122          */
24123         sync: true,
24124          /**
24125          * @event push
24126          * Fires when the iframe editor is updated with content from the textarea.
24127          * @param {Roo.HtmlEditorCore} this
24128          * @param {String} html
24129          */
24130         push: true,
24131         
24132         /**
24133          * @event editorevent
24134          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24135          * @param {Roo.HtmlEditorCore} this
24136          */
24137         editorevent: true
24138         
24139     });
24140     
24141     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24142     
24143     // defaults : white / black...
24144     this.applyBlacklists();
24145     
24146     
24147     
24148 };
24149
24150
24151 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24152
24153
24154      /**
24155      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24156      */
24157     
24158     owner : false,
24159     
24160      /**
24161      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24162      *                        Roo.resizable.
24163      */
24164     resizable : false,
24165      /**
24166      * @cfg {Number} height (in pixels)
24167      */   
24168     height: 300,
24169    /**
24170      * @cfg {Number} width (in pixels)
24171      */   
24172     width: 500,
24173     
24174     /**
24175      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24176      * 
24177      */
24178     stylesheets: false,
24179     
24180     // id of frame..
24181     frameId: false,
24182     
24183     // private properties
24184     validationEvent : false,
24185     deferHeight: true,
24186     initialized : false,
24187     activated : false,
24188     sourceEditMode : false,
24189     onFocus : Roo.emptyFn,
24190     iframePad:3,
24191     hideMode:'offsets',
24192     
24193     clearUp: true,
24194     
24195     // blacklist + whitelisted elements..
24196     black: false,
24197     white: false,
24198      
24199     bodyCls : '',
24200
24201     /**
24202      * Protected method that will not generally be called directly. It
24203      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24204      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24205      */
24206     getDocMarkup : function(){
24207         // body styles..
24208         var st = '';
24209         
24210         // inherit styels from page...?? 
24211         if (this.stylesheets === false) {
24212             
24213             Roo.get(document.head).select('style').each(function(node) {
24214                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24215             });
24216             
24217             Roo.get(document.head).select('link').each(function(node) { 
24218                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24219             });
24220             
24221         } else if (!this.stylesheets.length) {
24222                 // simple..
24223                 st = '<style type="text/css">' +
24224                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24225                    '</style>';
24226         } else {
24227             for (var i in this.stylesheets) { 
24228                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24229             }
24230             
24231         }
24232         
24233         st +=  '<style type="text/css">' +
24234             'IMG { cursor: pointer } ' +
24235         '</style>';
24236
24237         var cls = 'roo-htmleditor-body';
24238         
24239         if(this.bodyCls.length){
24240             cls += ' ' + this.bodyCls;
24241         }
24242         
24243         return '<html><head>' + st  +
24244             //<style type="text/css">' +
24245             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24246             //'</style>' +
24247             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24248     },
24249
24250     // private
24251     onRender : function(ct, position)
24252     {
24253         var _t = this;
24254         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24255         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24256         
24257         
24258         this.el.dom.style.border = '0 none';
24259         this.el.dom.setAttribute('tabIndex', -1);
24260         this.el.addClass('x-hidden hide');
24261         
24262         
24263         
24264         if(Roo.isIE){ // fix IE 1px bogus margin
24265             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24266         }
24267        
24268         
24269         this.frameId = Roo.id();
24270         
24271          
24272         
24273         var iframe = this.owner.wrap.createChild({
24274             tag: 'iframe',
24275             cls: 'form-control', // bootstrap..
24276             id: this.frameId,
24277             name: this.frameId,
24278             frameBorder : 'no',
24279             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24280         }, this.el
24281         );
24282         
24283         
24284         this.iframe = iframe.dom;
24285
24286          this.assignDocWin();
24287         
24288         this.doc.designMode = 'on';
24289        
24290         this.doc.open();
24291         this.doc.write(this.getDocMarkup());
24292         this.doc.close();
24293
24294         
24295         var task = { // must defer to wait for browser to be ready
24296             run : function(){
24297                 //console.log("run task?" + this.doc.readyState);
24298                 this.assignDocWin();
24299                 if(this.doc.body || this.doc.readyState == 'complete'){
24300                     try {
24301                         this.doc.designMode="on";
24302                     } catch (e) {
24303                         return;
24304                     }
24305                     Roo.TaskMgr.stop(task);
24306                     this.initEditor.defer(10, this);
24307                 }
24308             },
24309             interval : 10,
24310             duration: 10000,
24311             scope: this
24312         };
24313         Roo.TaskMgr.start(task);
24314
24315     },
24316
24317     // private
24318     onResize : function(w, h)
24319     {
24320          Roo.log('resize: ' +w + ',' + h );
24321         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24322         if(!this.iframe){
24323             return;
24324         }
24325         if(typeof w == 'number'){
24326             
24327             this.iframe.style.width = w + 'px';
24328         }
24329         if(typeof h == 'number'){
24330             
24331             this.iframe.style.height = h + 'px';
24332             if(this.doc){
24333                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24334             }
24335         }
24336         
24337     },
24338
24339     /**
24340      * Toggles the editor between standard and source edit mode.
24341      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24342      */
24343     toggleSourceEdit : function(sourceEditMode){
24344         
24345         this.sourceEditMode = sourceEditMode === true;
24346         
24347         if(this.sourceEditMode){
24348  
24349             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24350             
24351         }else{
24352             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24353             //this.iframe.className = '';
24354             this.deferFocus();
24355         }
24356         //this.setSize(this.owner.wrap.getSize());
24357         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24358     },
24359
24360     
24361   
24362
24363     /**
24364      * Protected method that will not generally be called directly. If you need/want
24365      * custom HTML cleanup, this is the method you should override.
24366      * @param {String} html The HTML to be cleaned
24367      * return {String} The cleaned HTML
24368      */
24369     cleanHtml : function(html){
24370         html = String(html);
24371         if(html.length > 5){
24372             if(Roo.isSafari){ // strip safari nonsense
24373                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24374             }
24375         }
24376         if(html == '&nbsp;'){
24377             html = '';
24378         }
24379         return html;
24380     },
24381
24382     /**
24383      * HTML Editor -> Textarea
24384      * Protected method that will not generally be called directly. Syncs the contents
24385      * of the editor iframe with the textarea.
24386      */
24387     syncValue : function(){
24388         if(this.initialized){
24389             var bd = (this.doc.body || this.doc.documentElement);
24390             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24391             var html = bd.innerHTML;
24392             if(Roo.isSafari){
24393                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24394                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24395                 if(m && m[1]){
24396                     html = '<div style="'+m[0]+'">' + html + '</div>';
24397                 }
24398             }
24399             html = this.cleanHtml(html);
24400             // fix up the special chars.. normaly like back quotes in word...
24401             // however we do not want to do this with chinese..
24402             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24403                 
24404                 var cc = match.charCodeAt();
24405
24406                 // Get the character value, handling surrogate pairs
24407                 if (match.length == 2) {
24408                     // It's a surrogate pair, calculate the Unicode code point
24409                     var high = match.charCodeAt(0) - 0xD800;
24410                     var low  = match.charCodeAt(1) - 0xDC00;
24411                     cc = (high * 0x400) + low + 0x10000;
24412                 }  else if (
24413                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24414                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24415                     (cc >= 0xf900 && cc < 0xfb00 )
24416                 ) {
24417                         return match;
24418                 }  
24419          
24420                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24421                 return "&#" + cc + ";";
24422                 
24423                 
24424             });
24425             
24426             
24427              
24428             if(this.owner.fireEvent('beforesync', this, html) !== false){
24429                 this.el.dom.value = html;
24430                 this.owner.fireEvent('sync', this, html);
24431             }
24432         }
24433     },
24434
24435     /**
24436      * Protected method that will not generally be called directly. Pushes the value of the textarea
24437      * into the iframe editor.
24438      */
24439     pushValue : function(){
24440         if(this.initialized){
24441             var v = this.el.dom.value.trim();
24442             
24443 //            if(v.length < 1){
24444 //                v = '&#160;';
24445 //            }
24446             
24447             if(this.owner.fireEvent('beforepush', this, v) !== false){
24448                 var d = (this.doc.body || this.doc.documentElement);
24449                 d.innerHTML = v;
24450                 this.cleanUpPaste();
24451                 this.el.dom.value = d.innerHTML;
24452                 this.owner.fireEvent('push', this, v);
24453             }
24454         }
24455     },
24456
24457     // private
24458     deferFocus : function(){
24459         this.focus.defer(10, this);
24460     },
24461
24462     // doc'ed in Field
24463     focus : function(){
24464         if(this.win && !this.sourceEditMode){
24465             this.win.focus();
24466         }else{
24467             this.el.focus();
24468         }
24469     },
24470     
24471     assignDocWin: function()
24472     {
24473         var iframe = this.iframe;
24474         
24475          if(Roo.isIE){
24476             this.doc = iframe.contentWindow.document;
24477             this.win = iframe.contentWindow;
24478         } else {
24479 //            if (!Roo.get(this.frameId)) {
24480 //                return;
24481 //            }
24482 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24483 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24484             
24485             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24486                 return;
24487             }
24488             
24489             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24490             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24491         }
24492     },
24493     
24494     // private
24495     initEditor : function(){
24496         //console.log("INIT EDITOR");
24497         this.assignDocWin();
24498         
24499         
24500         
24501         this.doc.designMode="on";
24502         this.doc.open();
24503         this.doc.write(this.getDocMarkup());
24504         this.doc.close();
24505         
24506         var dbody = (this.doc.body || this.doc.documentElement);
24507         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24508         // this copies styles from the containing element into thsi one..
24509         // not sure why we need all of this..
24510         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24511         
24512         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24513         //ss['background-attachment'] = 'fixed'; // w3c
24514         dbody.bgProperties = 'fixed'; // ie
24515         //Roo.DomHelper.applyStyles(dbody, ss);
24516         Roo.EventManager.on(this.doc, {
24517             //'mousedown': this.onEditorEvent,
24518             'mouseup': this.onEditorEvent,
24519             'dblclick': this.onEditorEvent,
24520             'click': this.onEditorEvent,
24521             'keyup': this.onEditorEvent,
24522             buffer:100,
24523             scope: this
24524         });
24525         if(Roo.isGecko){
24526             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24527         }
24528         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24529             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24530         }
24531         this.initialized = true;
24532
24533         this.owner.fireEvent('initialize', this);
24534         this.pushValue();
24535     },
24536
24537     // private
24538     onDestroy : function(){
24539         
24540         
24541         
24542         if(this.rendered){
24543             
24544             //for (var i =0; i < this.toolbars.length;i++) {
24545             //    // fixme - ask toolbars for heights?
24546             //    this.toolbars[i].onDestroy();
24547            // }
24548             
24549             //this.wrap.dom.innerHTML = '';
24550             //this.wrap.remove();
24551         }
24552     },
24553
24554     // private
24555     onFirstFocus : function(){
24556         
24557         this.assignDocWin();
24558         
24559         
24560         this.activated = true;
24561          
24562     
24563         if(Roo.isGecko){ // prevent silly gecko errors
24564             this.win.focus();
24565             var s = this.win.getSelection();
24566             if(!s.focusNode || s.focusNode.nodeType != 3){
24567                 var r = s.getRangeAt(0);
24568                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24569                 r.collapse(true);
24570                 this.deferFocus();
24571             }
24572             try{
24573                 this.execCmd('useCSS', true);
24574                 this.execCmd('styleWithCSS', false);
24575             }catch(e){}
24576         }
24577         this.owner.fireEvent('activate', this);
24578     },
24579
24580     // private
24581     adjustFont: function(btn){
24582         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24583         //if(Roo.isSafari){ // safari
24584         //    adjust *= 2;
24585        // }
24586         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24587         if(Roo.isSafari){ // safari
24588             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24589             v =  (v < 10) ? 10 : v;
24590             v =  (v > 48) ? 48 : v;
24591             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24592             
24593         }
24594         
24595         
24596         v = Math.max(1, v+adjust);
24597         
24598         this.execCmd('FontSize', v  );
24599     },
24600
24601     onEditorEvent : function(e)
24602     {
24603         this.owner.fireEvent('editorevent', this, e);
24604       //  this.updateToolbar();
24605         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24606     },
24607
24608     insertTag : function(tg)
24609     {
24610         // could be a bit smarter... -> wrap the current selected tRoo..
24611         if (tg.toLowerCase() == 'span' ||
24612             tg.toLowerCase() == 'code' ||
24613             tg.toLowerCase() == 'sup' ||
24614             tg.toLowerCase() == 'sub' 
24615             ) {
24616             
24617             range = this.createRange(this.getSelection());
24618             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24619             wrappingNode.appendChild(range.extractContents());
24620             range.insertNode(wrappingNode);
24621
24622             return;
24623             
24624             
24625             
24626         }
24627         this.execCmd("formatblock",   tg);
24628         
24629     },
24630     
24631     insertText : function(txt)
24632     {
24633         
24634         
24635         var range = this.createRange();
24636         range.deleteContents();
24637                //alert(Sender.getAttribute('label'));
24638                
24639         range.insertNode(this.doc.createTextNode(txt));
24640     } ,
24641     
24642      
24643
24644     /**
24645      * Executes a Midas editor command on the editor document and performs necessary focus and
24646      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24647      * @param {String} cmd The Midas command
24648      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24649      */
24650     relayCmd : function(cmd, value){
24651         this.win.focus();
24652         this.execCmd(cmd, value);
24653         this.owner.fireEvent('editorevent', this);
24654         //this.updateToolbar();
24655         this.owner.deferFocus();
24656     },
24657
24658     /**
24659      * Executes a Midas editor command directly on the editor document.
24660      * For visual commands, you should use {@link #relayCmd} instead.
24661      * <b>This should only be called after the editor is initialized.</b>
24662      * @param {String} cmd The Midas command
24663      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24664      */
24665     execCmd : function(cmd, value){
24666         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24667         this.syncValue();
24668     },
24669  
24670  
24671    
24672     /**
24673      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24674      * to insert tRoo.
24675      * @param {String} text | dom node.. 
24676      */
24677     insertAtCursor : function(text)
24678     {
24679         
24680         if(!this.activated){
24681             return;
24682         }
24683         /*
24684         if(Roo.isIE){
24685             this.win.focus();
24686             var r = this.doc.selection.createRange();
24687             if(r){
24688                 r.collapse(true);
24689                 r.pasteHTML(text);
24690                 this.syncValue();
24691                 this.deferFocus();
24692             
24693             }
24694             return;
24695         }
24696         */
24697         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24698             this.win.focus();
24699             
24700             
24701             // from jquery ui (MIT licenced)
24702             var range, node;
24703             var win = this.win;
24704             
24705             if (win.getSelection && win.getSelection().getRangeAt) {
24706                 range = win.getSelection().getRangeAt(0);
24707                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24708                 range.insertNode(node);
24709             } else if (win.document.selection && win.document.selection.createRange) {
24710                 // no firefox support
24711                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24712                 win.document.selection.createRange().pasteHTML(txt);
24713             } else {
24714                 // no firefox support
24715                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24716                 this.execCmd('InsertHTML', txt);
24717             } 
24718             
24719             this.syncValue();
24720             
24721             this.deferFocus();
24722         }
24723     },
24724  // private
24725     mozKeyPress : function(e){
24726         if(e.ctrlKey){
24727             var c = e.getCharCode(), cmd;
24728           
24729             if(c > 0){
24730                 c = String.fromCharCode(c).toLowerCase();
24731                 switch(c){
24732                     case 'b':
24733                         cmd = 'bold';
24734                         break;
24735                     case 'i':
24736                         cmd = 'italic';
24737                         break;
24738                     
24739                     case 'u':
24740                         cmd = 'underline';
24741                         break;
24742                     
24743                     case 'v':
24744                         this.cleanUpPaste.defer(100, this);
24745                         return;
24746                         
24747                 }
24748                 if(cmd){
24749                     this.win.focus();
24750                     this.execCmd(cmd);
24751                     this.deferFocus();
24752                     e.preventDefault();
24753                 }
24754                 
24755             }
24756         }
24757     },
24758
24759     // private
24760     fixKeys : function(){ // load time branching for fastest keydown performance
24761         if(Roo.isIE){
24762             return function(e){
24763                 var k = e.getKey(), r;
24764                 if(k == e.TAB){
24765                     e.stopEvent();
24766                     r = this.doc.selection.createRange();
24767                     if(r){
24768                         r.collapse(true);
24769                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24770                         this.deferFocus();
24771                     }
24772                     return;
24773                 }
24774                 
24775                 if(k == e.ENTER){
24776                     r = this.doc.selection.createRange();
24777                     if(r){
24778                         var target = r.parentElement();
24779                         if(!target || target.tagName.toLowerCase() != 'li'){
24780                             e.stopEvent();
24781                             r.pasteHTML('<br />');
24782                             r.collapse(false);
24783                             r.select();
24784                         }
24785                     }
24786                 }
24787                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24788                     this.cleanUpPaste.defer(100, this);
24789                     return;
24790                 }
24791                 
24792                 
24793             };
24794         }else if(Roo.isOpera){
24795             return function(e){
24796                 var k = e.getKey();
24797                 if(k == e.TAB){
24798                     e.stopEvent();
24799                     this.win.focus();
24800                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24801                     this.deferFocus();
24802                 }
24803                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24804                     this.cleanUpPaste.defer(100, this);
24805                     return;
24806                 }
24807                 
24808             };
24809         }else if(Roo.isSafari){
24810             return function(e){
24811                 var k = e.getKey();
24812                 
24813                 if(k == e.TAB){
24814                     e.stopEvent();
24815                     this.execCmd('InsertText','\t');
24816                     this.deferFocus();
24817                     return;
24818                 }
24819                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24820                     this.cleanUpPaste.defer(100, this);
24821                     return;
24822                 }
24823                 
24824              };
24825         }
24826     }(),
24827     
24828     getAllAncestors: function()
24829     {
24830         var p = this.getSelectedNode();
24831         var a = [];
24832         if (!p) {
24833             a.push(p); // push blank onto stack..
24834             p = this.getParentElement();
24835         }
24836         
24837         
24838         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24839             a.push(p);
24840             p = p.parentNode;
24841         }
24842         a.push(this.doc.body);
24843         return a;
24844     },
24845     lastSel : false,
24846     lastSelNode : false,
24847     
24848     
24849     getSelection : function() 
24850     {
24851         this.assignDocWin();
24852         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24853     },
24854     
24855     getSelectedNode: function() 
24856     {
24857         // this may only work on Gecko!!!
24858         
24859         // should we cache this!!!!
24860         
24861         
24862         
24863          
24864         var range = this.createRange(this.getSelection()).cloneRange();
24865         
24866         if (Roo.isIE) {
24867             var parent = range.parentElement();
24868             while (true) {
24869                 var testRange = range.duplicate();
24870                 testRange.moveToElementText(parent);
24871                 if (testRange.inRange(range)) {
24872                     break;
24873                 }
24874                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24875                     break;
24876                 }
24877                 parent = parent.parentElement;
24878             }
24879             return parent;
24880         }
24881         
24882         // is ancestor a text element.
24883         var ac =  range.commonAncestorContainer;
24884         if (ac.nodeType == 3) {
24885             ac = ac.parentNode;
24886         }
24887         
24888         var ar = ac.childNodes;
24889          
24890         var nodes = [];
24891         var other_nodes = [];
24892         var has_other_nodes = false;
24893         for (var i=0;i<ar.length;i++) {
24894             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24895                 continue;
24896             }
24897             // fullly contained node.
24898             
24899             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24900                 nodes.push(ar[i]);
24901                 continue;
24902             }
24903             
24904             // probably selected..
24905             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24906                 other_nodes.push(ar[i]);
24907                 continue;
24908             }
24909             // outer..
24910             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24911                 continue;
24912             }
24913             
24914             
24915             has_other_nodes = true;
24916         }
24917         if (!nodes.length && other_nodes.length) {
24918             nodes= other_nodes;
24919         }
24920         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24921             return false;
24922         }
24923         
24924         return nodes[0];
24925     },
24926     createRange: function(sel)
24927     {
24928         // this has strange effects when using with 
24929         // top toolbar - not sure if it's a great idea.
24930         //this.editor.contentWindow.focus();
24931         if (typeof sel != "undefined") {
24932             try {
24933                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24934             } catch(e) {
24935                 return this.doc.createRange();
24936             }
24937         } else {
24938             return this.doc.createRange();
24939         }
24940     },
24941     getParentElement: function()
24942     {
24943         
24944         this.assignDocWin();
24945         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24946         
24947         var range = this.createRange(sel);
24948          
24949         try {
24950             var p = range.commonAncestorContainer;
24951             while (p.nodeType == 3) { // text node
24952                 p = p.parentNode;
24953             }
24954             return p;
24955         } catch (e) {
24956             return null;
24957         }
24958     
24959     },
24960     /***
24961      *
24962      * Range intersection.. the hard stuff...
24963      *  '-1' = before
24964      *  '0' = hits..
24965      *  '1' = after.
24966      *         [ -- selected range --- ]
24967      *   [fail]                        [fail]
24968      *
24969      *    basically..
24970      *      if end is before start or  hits it. fail.
24971      *      if start is after end or hits it fail.
24972      *
24973      *   if either hits (but other is outside. - then it's not 
24974      *   
24975      *    
24976      **/
24977     
24978     
24979     // @see http://www.thismuchiknow.co.uk/?p=64.
24980     rangeIntersectsNode : function(range, node)
24981     {
24982         var nodeRange = node.ownerDocument.createRange();
24983         try {
24984             nodeRange.selectNode(node);
24985         } catch (e) {
24986             nodeRange.selectNodeContents(node);
24987         }
24988     
24989         var rangeStartRange = range.cloneRange();
24990         rangeStartRange.collapse(true);
24991     
24992         var rangeEndRange = range.cloneRange();
24993         rangeEndRange.collapse(false);
24994     
24995         var nodeStartRange = nodeRange.cloneRange();
24996         nodeStartRange.collapse(true);
24997     
24998         var nodeEndRange = nodeRange.cloneRange();
24999         nodeEndRange.collapse(false);
25000     
25001         return rangeStartRange.compareBoundaryPoints(
25002                  Range.START_TO_START, nodeEndRange) == -1 &&
25003                rangeEndRange.compareBoundaryPoints(
25004                  Range.START_TO_START, nodeStartRange) == 1;
25005         
25006          
25007     },
25008     rangeCompareNode : function(range, node)
25009     {
25010         var nodeRange = node.ownerDocument.createRange();
25011         try {
25012             nodeRange.selectNode(node);
25013         } catch (e) {
25014             nodeRange.selectNodeContents(node);
25015         }
25016         
25017         
25018         range.collapse(true);
25019     
25020         nodeRange.collapse(true);
25021      
25022         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25023         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25024          
25025         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25026         
25027         var nodeIsBefore   =  ss == 1;
25028         var nodeIsAfter    = ee == -1;
25029         
25030         if (nodeIsBefore && nodeIsAfter) {
25031             return 0; // outer
25032         }
25033         if (!nodeIsBefore && nodeIsAfter) {
25034             return 1; //right trailed.
25035         }
25036         
25037         if (nodeIsBefore && !nodeIsAfter) {
25038             return 2;  // left trailed.
25039         }
25040         // fully contined.
25041         return 3;
25042     },
25043
25044     // private? - in a new class?
25045     cleanUpPaste :  function()
25046     {
25047         // cleans up the whole document..
25048         Roo.log('cleanuppaste');
25049         
25050         this.cleanUpChildren(this.doc.body);
25051         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25052         if (clean != this.doc.body.innerHTML) {
25053             this.doc.body.innerHTML = clean;
25054         }
25055         
25056     },
25057     
25058     cleanWordChars : function(input) {// change the chars to hex code
25059         var he = Roo.HtmlEditorCore;
25060         
25061         var output = input;
25062         Roo.each(he.swapCodes, function(sw) { 
25063             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25064             
25065             output = output.replace(swapper, sw[1]);
25066         });
25067         
25068         return output;
25069     },
25070     
25071     
25072     cleanUpChildren : function (n)
25073     {
25074         if (!n.childNodes.length) {
25075             return;
25076         }
25077         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25078            this.cleanUpChild(n.childNodes[i]);
25079         }
25080     },
25081     
25082     
25083         
25084     
25085     cleanUpChild : function (node)
25086     {
25087         var ed = this;
25088         //console.log(node);
25089         if (node.nodeName == "#text") {
25090             // clean up silly Windows -- stuff?
25091             return; 
25092         }
25093         if (node.nodeName == "#comment") {
25094             node.parentNode.removeChild(node);
25095             // clean up silly Windows -- stuff?
25096             return; 
25097         }
25098         var lcname = node.tagName.toLowerCase();
25099         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25100         // whitelist of tags..
25101         
25102         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25103             // remove node.
25104             node.parentNode.removeChild(node);
25105             return;
25106             
25107         }
25108         
25109         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25110         
25111         // spans with no attributes - just remove them..
25112         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25113             remove_keep_children = true;
25114         }
25115         
25116         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25117         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25118         
25119         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25120         //    remove_keep_children = true;
25121         //}
25122         
25123         if (remove_keep_children) {
25124             this.cleanUpChildren(node);
25125             // inserts everything just before this node...
25126             while (node.childNodes.length) {
25127                 var cn = node.childNodes[0];
25128                 node.removeChild(cn);
25129                 node.parentNode.insertBefore(cn, node);
25130             }
25131             node.parentNode.removeChild(node);
25132             return;
25133         }
25134         
25135         if (!node.attributes || !node.attributes.length) {
25136             
25137           
25138             
25139             
25140             this.cleanUpChildren(node);
25141             return;
25142         }
25143         
25144         function cleanAttr(n,v)
25145         {
25146             
25147             if (v.match(/^\./) || v.match(/^\//)) {
25148                 return;
25149             }
25150             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25151                 return;
25152             }
25153             if (v.match(/^#/)) {
25154                 return;
25155             }
25156             if (v.match(/^\{/)) { // allow template editing.
25157                 return;
25158             }
25159 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25160             node.removeAttribute(n);
25161             
25162         }
25163         
25164         var cwhite = this.cwhite;
25165         var cblack = this.cblack;
25166             
25167         function cleanStyle(n,v)
25168         {
25169             if (v.match(/expression/)) { //XSS?? should we even bother..
25170                 node.removeAttribute(n);
25171                 return;
25172             }
25173             
25174             var parts = v.split(/;/);
25175             var clean = [];
25176             
25177             Roo.each(parts, function(p) {
25178                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25179                 if (!p.length) {
25180                     return true;
25181                 }
25182                 var l = p.split(':').shift().replace(/\s+/g,'');
25183                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25184                 
25185                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25186 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25187                     //node.removeAttribute(n);
25188                     return true;
25189                 }
25190                 //Roo.log()
25191                 // only allow 'c whitelisted system attributes'
25192                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25193 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25194                     //node.removeAttribute(n);
25195                     return true;
25196                 }
25197                 
25198                 
25199                  
25200                 
25201                 clean.push(p);
25202                 return true;
25203             });
25204             if (clean.length) { 
25205                 node.setAttribute(n, clean.join(';'));
25206             } else {
25207                 node.removeAttribute(n);
25208             }
25209             
25210         }
25211         
25212         
25213         for (var i = node.attributes.length-1; i > -1 ; i--) {
25214             var a = node.attributes[i];
25215             //console.log(a);
25216             
25217             if (a.name.toLowerCase().substr(0,2)=='on')  {
25218                 node.removeAttribute(a.name);
25219                 continue;
25220             }
25221             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25222                 node.removeAttribute(a.name);
25223                 continue;
25224             }
25225             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25226                 cleanAttr(a.name,a.value); // fixme..
25227                 continue;
25228             }
25229             if (a.name == 'style') {
25230                 cleanStyle(a.name,a.value);
25231                 continue;
25232             }
25233             /// clean up MS crap..
25234             // tecnically this should be a list of valid class'es..
25235             
25236             
25237             if (a.name == 'class') {
25238                 if (a.value.match(/^Mso/)) {
25239                     node.removeAttribute('class');
25240                 }
25241                 
25242                 if (a.value.match(/^body$/)) {
25243                     node.removeAttribute('class');
25244                 }
25245                 continue;
25246             }
25247             
25248             // style cleanup!?
25249             // class cleanup?
25250             
25251         }
25252         
25253         
25254         this.cleanUpChildren(node);
25255         
25256         
25257     },
25258     
25259     /**
25260      * Clean up MS wordisms...
25261      */
25262     cleanWord : function(node)
25263     {
25264         if (!node) {
25265             this.cleanWord(this.doc.body);
25266             return;
25267         }
25268         
25269         if(
25270                 node.nodeName == 'SPAN' &&
25271                 !node.hasAttributes() &&
25272                 node.childNodes.length == 1 &&
25273                 node.firstChild.nodeName == "#text"  
25274         ) {
25275             var textNode = node.firstChild;
25276             node.removeChild(textNode);
25277             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25278                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25279             }
25280             node.parentNode.insertBefore(textNode, node);
25281             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25282                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25283             }
25284             node.parentNode.removeChild(node);
25285         }
25286         
25287         if (node.nodeName == "#text") {
25288             // clean up silly Windows -- stuff?
25289             return; 
25290         }
25291         if (node.nodeName == "#comment") {
25292             node.parentNode.removeChild(node);
25293             // clean up silly Windows -- stuff?
25294             return; 
25295         }
25296         
25297         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25298             node.parentNode.removeChild(node);
25299             return;
25300         }
25301         //Roo.log(node.tagName);
25302         // remove - but keep children..
25303         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25304             //Roo.log('-- removed');
25305             while (node.childNodes.length) {
25306                 var cn = node.childNodes[0];
25307                 node.removeChild(cn);
25308                 node.parentNode.insertBefore(cn, node);
25309                 // move node to parent - and clean it..
25310                 this.cleanWord(cn);
25311             }
25312             node.parentNode.removeChild(node);
25313             /// no need to iterate chidlren = it's got none..
25314             //this.iterateChildren(node, this.cleanWord);
25315             return;
25316         }
25317         // clean styles
25318         if (node.className.length) {
25319             
25320             var cn = node.className.split(/\W+/);
25321             var cna = [];
25322             Roo.each(cn, function(cls) {
25323                 if (cls.match(/Mso[a-zA-Z]+/)) {
25324                     return;
25325                 }
25326                 cna.push(cls);
25327             });
25328             node.className = cna.length ? cna.join(' ') : '';
25329             if (!cna.length) {
25330                 node.removeAttribute("class");
25331             }
25332         }
25333         
25334         if (node.hasAttribute("lang")) {
25335             node.removeAttribute("lang");
25336         }
25337         
25338         if (node.hasAttribute("style")) {
25339             
25340             var styles = node.getAttribute("style").split(";");
25341             var nstyle = [];
25342             Roo.each(styles, function(s) {
25343                 if (!s.match(/:/)) {
25344                     return;
25345                 }
25346                 var kv = s.split(":");
25347                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25348                     return;
25349                 }
25350                 // what ever is left... we allow.
25351                 nstyle.push(s);
25352             });
25353             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25354             if (!nstyle.length) {
25355                 node.removeAttribute('style');
25356             }
25357         }
25358         this.iterateChildren(node, this.cleanWord);
25359         
25360         
25361         
25362     },
25363     /**
25364      * iterateChildren of a Node, calling fn each time, using this as the scole..
25365      * @param {DomNode} node node to iterate children of.
25366      * @param {Function} fn method of this class to call on each item.
25367      */
25368     iterateChildren : function(node, fn)
25369     {
25370         if (!node.childNodes.length) {
25371                 return;
25372         }
25373         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25374            fn.call(this, node.childNodes[i])
25375         }
25376     },
25377     
25378     
25379     /**
25380      * cleanTableWidths.
25381      *
25382      * Quite often pasting from word etc.. results in tables with column and widths.
25383      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25384      *
25385      */
25386     cleanTableWidths : function(node)
25387     {
25388          
25389          
25390         if (!node) {
25391             this.cleanTableWidths(this.doc.body);
25392             return;
25393         }
25394         
25395         // ignore list...
25396         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25397             return; 
25398         }
25399         Roo.log(node.tagName);
25400         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25401             this.iterateChildren(node, this.cleanTableWidths);
25402             return;
25403         }
25404         if (node.hasAttribute('width')) {
25405             node.removeAttribute('width');
25406         }
25407         
25408          
25409         if (node.hasAttribute("style")) {
25410             // pretty basic...
25411             
25412             var styles = node.getAttribute("style").split(";");
25413             var nstyle = [];
25414             Roo.each(styles, function(s) {
25415                 if (!s.match(/:/)) {
25416                     return;
25417                 }
25418                 var kv = s.split(":");
25419                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25420                     return;
25421                 }
25422                 // what ever is left... we allow.
25423                 nstyle.push(s);
25424             });
25425             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25426             if (!nstyle.length) {
25427                 node.removeAttribute('style');
25428             }
25429         }
25430         
25431         this.iterateChildren(node, this.cleanTableWidths);
25432         
25433         
25434     },
25435     
25436     
25437     
25438     
25439     domToHTML : function(currentElement, depth, nopadtext) {
25440         
25441         depth = depth || 0;
25442         nopadtext = nopadtext || false;
25443     
25444         if (!currentElement) {
25445             return this.domToHTML(this.doc.body);
25446         }
25447         
25448         //Roo.log(currentElement);
25449         var j;
25450         var allText = false;
25451         var nodeName = currentElement.nodeName;
25452         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25453         
25454         if  (nodeName == '#text') {
25455             
25456             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25457         }
25458         
25459         
25460         var ret = '';
25461         if (nodeName != 'BODY') {
25462              
25463             var i = 0;
25464             // Prints the node tagName, such as <A>, <IMG>, etc
25465             if (tagName) {
25466                 var attr = [];
25467                 for(i = 0; i < currentElement.attributes.length;i++) {
25468                     // quoting?
25469                     var aname = currentElement.attributes.item(i).name;
25470                     if (!currentElement.attributes.item(i).value.length) {
25471                         continue;
25472                     }
25473                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25474                 }
25475                 
25476                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25477             } 
25478             else {
25479                 
25480                 // eack
25481             }
25482         } else {
25483             tagName = false;
25484         }
25485         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25486             return ret;
25487         }
25488         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25489             nopadtext = true;
25490         }
25491         
25492         
25493         // Traverse the tree
25494         i = 0;
25495         var currentElementChild = currentElement.childNodes.item(i);
25496         var allText = true;
25497         var innerHTML  = '';
25498         lastnode = '';
25499         while (currentElementChild) {
25500             // Formatting code (indent the tree so it looks nice on the screen)
25501             var nopad = nopadtext;
25502             if (lastnode == 'SPAN') {
25503                 nopad  = true;
25504             }
25505             // text
25506             if  (currentElementChild.nodeName == '#text') {
25507                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25508                 toadd = nopadtext ? toadd : toadd.trim();
25509                 if (!nopad && toadd.length > 80) {
25510                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25511                 }
25512                 innerHTML  += toadd;
25513                 
25514                 i++;
25515                 currentElementChild = currentElement.childNodes.item(i);
25516                 lastNode = '';
25517                 continue;
25518             }
25519             allText = false;
25520             
25521             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25522                 
25523             // Recursively traverse the tree structure of the child node
25524             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25525             lastnode = currentElementChild.nodeName;
25526             i++;
25527             currentElementChild=currentElement.childNodes.item(i);
25528         }
25529         
25530         ret += innerHTML;
25531         
25532         if (!allText) {
25533                 // The remaining code is mostly for formatting the tree
25534             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25535         }
25536         
25537         
25538         if (tagName) {
25539             ret+= "</"+tagName+">";
25540         }
25541         return ret;
25542         
25543     },
25544         
25545     applyBlacklists : function()
25546     {
25547         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25548         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25549         
25550         this.white = [];
25551         this.black = [];
25552         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25553             if (b.indexOf(tag) > -1) {
25554                 return;
25555             }
25556             this.white.push(tag);
25557             
25558         }, this);
25559         
25560         Roo.each(w, function(tag) {
25561             if (b.indexOf(tag) > -1) {
25562                 return;
25563             }
25564             if (this.white.indexOf(tag) > -1) {
25565                 return;
25566             }
25567             this.white.push(tag);
25568             
25569         }, this);
25570         
25571         
25572         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25573             if (w.indexOf(tag) > -1) {
25574                 return;
25575             }
25576             this.black.push(tag);
25577             
25578         }, this);
25579         
25580         Roo.each(b, function(tag) {
25581             if (w.indexOf(tag) > -1) {
25582                 return;
25583             }
25584             if (this.black.indexOf(tag) > -1) {
25585                 return;
25586             }
25587             this.black.push(tag);
25588             
25589         }, this);
25590         
25591         
25592         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25593         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25594         
25595         this.cwhite = [];
25596         this.cblack = [];
25597         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25598             if (b.indexOf(tag) > -1) {
25599                 return;
25600             }
25601             this.cwhite.push(tag);
25602             
25603         }, this);
25604         
25605         Roo.each(w, function(tag) {
25606             if (b.indexOf(tag) > -1) {
25607                 return;
25608             }
25609             if (this.cwhite.indexOf(tag) > -1) {
25610                 return;
25611             }
25612             this.cwhite.push(tag);
25613             
25614         }, this);
25615         
25616         
25617         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25618             if (w.indexOf(tag) > -1) {
25619                 return;
25620             }
25621             this.cblack.push(tag);
25622             
25623         }, this);
25624         
25625         Roo.each(b, function(tag) {
25626             if (w.indexOf(tag) > -1) {
25627                 return;
25628             }
25629             if (this.cblack.indexOf(tag) > -1) {
25630                 return;
25631             }
25632             this.cblack.push(tag);
25633             
25634         }, this);
25635     },
25636     
25637     setStylesheets : function(stylesheets)
25638     {
25639         if(typeof(stylesheets) == 'string'){
25640             Roo.get(this.iframe.contentDocument.head).createChild({
25641                 tag : 'link',
25642                 rel : 'stylesheet',
25643                 type : 'text/css',
25644                 href : stylesheets
25645             });
25646             
25647             return;
25648         }
25649         var _this = this;
25650      
25651         Roo.each(stylesheets, function(s) {
25652             if(!s.length){
25653                 return;
25654             }
25655             
25656             Roo.get(_this.iframe.contentDocument.head).createChild({
25657                 tag : 'link',
25658                 rel : 'stylesheet',
25659                 type : 'text/css',
25660                 href : s
25661             });
25662         });
25663
25664         
25665     },
25666     
25667     removeStylesheets : function()
25668     {
25669         var _this = this;
25670         
25671         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25672             s.remove();
25673         });
25674     },
25675     
25676     setStyle : function(style)
25677     {
25678         Roo.get(this.iframe.contentDocument.head).createChild({
25679             tag : 'style',
25680             type : 'text/css',
25681             html : style
25682         });
25683
25684         return;
25685     }
25686     
25687     // hide stuff that is not compatible
25688     /**
25689      * @event blur
25690      * @hide
25691      */
25692     /**
25693      * @event change
25694      * @hide
25695      */
25696     /**
25697      * @event focus
25698      * @hide
25699      */
25700     /**
25701      * @event specialkey
25702      * @hide
25703      */
25704     /**
25705      * @cfg {String} fieldClass @hide
25706      */
25707     /**
25708      * @cfg {String} focusClass @hide
25709      */
25710     /**
25711      * @cfg {String} autoCreate @hide
25712      */
25713     /**
25714      * @cfg {String} inputType @hide
25715      */
25716     /**
25717      * @cfg {String} invalidClass @hide
25718      */
25719     /**
25720      * @cfg {String} invalidText @hide
25721      */
25722     /**
25723      * @cfg {String} msgFx @hide
25724      */
25725     /**
25726      * @cfg {String} validateOnBlur @hide
25727      */
25728 });
25729
25730 Roo.HtmlEditorCore.white = [
25731         'area', 'br', 'img', 'input', 'hr', 'wbr',
25732         
25733        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25734        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25735        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25736        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25737        'table',   'ul',         'xmp', 
25738        
25739        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25740       'thead',   'tr', 
25741      
25742       'dir', 'menu', 'ol', 'ul', 'dl',
25743        
25744       'embed',  'object'
25745 ];
25746
25747
25748 Roo.HtmlEditorCore.black = [
25749     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25750         'applet', // 
25751         'base',   'basefont', 'bgsound', 'blink',  'body', 
25752         'frame',  'frameset', 'head',    'html',   'ilayer', 
25753         'iframe', 'layer',  'link',     'meta',    'object',   
25754         'script', 'style' ,'title',  'xml' // clean later..
25755 ];
25756 Roo.HtmlEditorCore.clean = [
25757     'script', 'style', 'title', 'xml'
25758 ];
25759 Roo.HtmlEditorCore.remove = [
25760     'font'
25761 ];
25762 // attributes..
25763
25764 Roo.HtmlEditorCore.ablack = [
25765     'on'
25766 ];
25767     
25768 Roo.HtmlEditorCore.aclean = [ 
25769     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25770 ];
25771
25772 // protocols..
25773 Roo.HtmlEditorCore.pwhite= [
25774         'http',  'https',  'mailto'
25775 ];
25776
25777 // white listed style attributes.
25778 Roo.HtmlEditorCore.cwhite= [
25779       //  'text-align', /// default is to allow most things..
25780       
25781          
25782 //        'font-size'//??
25783 ];
25784
25785 // black listed style attributes.
25786 Roo.HtmlEditorCore.cblack= [
25787       //  'font-size' -- this can be set by the project 
25788 ];
25789
25790
25791 Roo.HtmlEditorCore.swapCodes   =[ 
25792     [    8211, "--" ], 
25793     [    8212, "--" ], 
25794     [    8216,  "'" ],  
25795     [    8217, "'" ],  
25796     [    8220, '"' ],  
25797     [    8221, '"' ],  
25798     [    8226, "*" ],  
25799     [    8230, "..." ]
25800 ]; 
25801
25802     /*
25803  * - LGPL
25804  *
25805  * HtmlEditor
25806  * 
25807  */
25808
25809 /**
25810  * @class Roo.bootstrap.HtmlEditor
25811  * @extends Roo.bootstrap.TextArea
25812  * Bootstrap HtmlEditor class
25813
25814  * @constructor
25815  * Create a new HtmlEditor
25816  * @param {Object} config The config object
25817  */
25818
25819 Roo.bootstrap.HtmlEditor = function(config){
25820     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25821     if (!this.toolbars) {
25822         this.toolbars = [];
25823     }
25824     
25825     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25826     this.addEvents({
25827             /**
25828              * @event initialize
25829              * Fires when the editor is fully initialized (including the iframe)
25830              * @param {HtmlEditor} this
25831              */
25832             initialize: true,
25833             /**
25834              * @event activate
25835              * Fires when the editor is first receives the focus. Any insertion must wait
25836              * until after this event.
25837              * @param {HtmlEditor} this
25838              */
25839             activate: true,
25840              /**
25841              * @event beforesync
25842              * Fires before the textarea is updated with content from the editor iframe. Return false
25843              * to cancel the sync.
25844              * @param {HtmlEditor} this
25845              * @param {String} html
25846              */
25847             beforesync: true,
25848              /**
25849              * @event beforepush
25850              * Fires before the iframe editor is updated with content from the textarea. Return false
25851              * to cancel the push.
25852              * @param {HtmlEditor} this
25853              * @param {String} html
25854              */
25855             beforepush: true,
25856              /**
25857              * @event sync
25858              * Fires when the textarea is updated with content from the editor iframe.
25859              * @param {HtmlEditor} this
25860              * @param {String} html
25861              */
25862             sync: true,
25863              /**
25864              * @event push
25865              * Fires when the iframe editor is updated with content from the textarea.
25866              * @param {HtmlEditor} this
25867              * @param {String} html
25868              */
25869             push: true,
25870              /**
25871              * @event editmodechange
25872              * Fires when the editor switches edit modes
25873              * @param {HtmlEditor} this
25874              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25875              */
25876             editmodechange: true,
25877             /**
25878              * @event editorevent
25879              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25880              * @param {HtmlEditor} this
25881              */
25882             editorevent: true,
25883             /**
25884              * @event firstfocus
25885              * Fires when on first focus - needed by toolbars..
25886              * @param {HtmlEditor} this
25887              */
25888             firstfocus: true,
25889             /**
25890              * @event autosave
25891              * Auto save the htmlEditor value as a file into Events
25892              * @param {HtmlEditor} this
25893              */
25894             autosave: true,
25895             /**
25896              * @event savedpreview
25897              * preview the saved version of htmlEditor
25898              * @param {HtmlEditor} this
25899              */
25900             savedpreview: true
25901         });
25902 };
25903
25904
25905 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25906     
25907     
25908       /**
25909      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25910      */
25911     toolbars : false,
25912     
25913      /**
25914     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25915     */
25916     btns : [],
25917    
25918      /**
25919      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25920      *                        Roo.resizable.
25921      */
25922     resizable : false,
25923      /**
25924      * @cfg {Number} height (in pixels)
25925      */   
25926     height: 300,
25927    /**
25928      * @cfg {Number} width (in pixels)
25929      */   
25930     width: false,
25931     
25932     /**
25933      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25934      * 
25935      */
25936     stylesheets: false,
25937     
25938     // id of frame..
25939     frameId: false,
25940     
25941     // private properties
25942     validationEvent : false,
25943     deferHeight: true,
25944     initialized : false,
25945     activated : false,
25946     
25947     onFocus : Roo.emptyFn,
25948     iframePad:3,
25949     hideMode:'offsets',
25950     
25951     tbContainer : false,
25952     
25953     bodyCls : '',
25954     
25955     toolbarContainer :function() {
25956         return this.wrap.select('.x-html-editor-tb',true).first();
25957     },
25958
25959     /**
25960      * Protected method that will not generally be called directly. It
25961      * is called when the editor creates its toolbar. Override this method if you need to
25962      * add custom toolbar buttons.
25963      * @param {HtmlEditor} editor
25964      */
25965     createToolbar : function(){
25966         Roo.log('renewing');
25967         Roo.log("create toolbars");
25968         
25969         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25970         this.toolbars[0].render(this.toolbarContainer());
25971         
25972         return;
25973         
25974 //        if (!editor.toolbars || !editor.toolbars.length) {
25975 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25976 //        }
25977 //        
25978 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25979 //            editor.toolbars[i] = Roo.factory(
25980 //                    typeof(editor.toolbars[i]) == 'string' ?
25981 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25982 //                Roo.bootstrap.HtmlEditor);
25983 //            editor.toolbars[i].init(editor);
25984 //        }
25985     },
25986
25987      
25988     // private
25989     onRender : function(ct, position)
25990     {
25991        // Roo.log("Call onRender: " + this.xtype);
25992         var _t = this;
25993         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25994       
25995         this.wrap = this.inputEl().wrap({
25996             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25997         });
25998         
25999         this.editorcore.onRender(ct, position);
26000          
26001         if (this.resizable) {
26002             this.resizeEl = new Roo.Resizable(this.wrap, {
26003                 pinned : true,
26004                 wrap: true,
26005                 dynamic : true,
26006                 minHeight : this.height,
26007                 height: this.height,
26008                 handles : this.resizable,
26009                 width: this.width,
26010                 listeners : {
26011                     resize : function(r, w, h) {
26012                         _t.onResize(w,h); // -something
26013                     }
26014                 }
26015             });
26016             
26017         }
26018         this.createToolbar(this);
26019        
26020         
26021         if(!this.width && this.resizable){
26022             this.setSize(this.wrap.getSize());
26023         }
26024         if (this.resizeEl) {
26025             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26026             // should trigger onReize..
26027         }
26028         
26029     },
26030
26031     // private
26032     onResize : function(w, h)
26033     {
26034         Roo.log('resize: ' +w + ',' + h );
26035         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26036         var ew = false;
26037         var eh = false;
26038         
26039         if(this.inputEl() ){
26040             if(typeof w == 'number'){
26041                 var aw = w - this.wrap.getFrameWidth('lr');
26042                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26043                 ew = aw;
26044             }
26045             if(typeof h == 'number'){
26046                  var tbh = -11;  // fixme it needs to tool bar size!
26047                 for (var i =0; i < this.toolbars.length;i++) {
26048                     // fixme - ask toolbars for heights?
26049                     tbh += this.toolbars[i].el.getHeight();
26050                     //if (this.toolbars[i].footer) {
26051                     //    tbh += this.toolbars[i].footer.el.getHeight();
26052                     //}
26053                 }
26054               
26055                 
26056                 
26057                 
26058                 
26059                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26060                 ah -= 5; // knock a few pixes off for look..
26061                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26062                 var eh = ah;
26063             }
26064         }
26065         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26066         this.editorcore.onResize(ew,eh);
26067         
26068     },
26069
26070     /**
26071      * Toggles the editor between standard and source edit mode.
26072      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26073      */
26074     toggleSourceEdit : function(sourceEditMode)
26075     {
26076         this.editorcore.toggleSourceEdit(sourceEditMode);
26077         
26078         if(this.editorcore.sourceEditMode){
26079             Roo.log('editor - showing textarea');
26080             
26081 //            Roo.log('in');
26082 //            Roo.log(this.syncValue());
26083             this.syncValue();
26084             this.inputEl().removeClass(['hide', 'x-hidden']);
26085             this.inputEl().dom.removeAttribute('tabIndex');
26086             this.inputEl().focus();
26087         }else{
26088             Roo.log('editor - hiding textarea');
26089 //            Roo.log('out')
26090 //            Roo.log(this.pushValue()); 
26091             this.pushValue();
26092             
26093             this.inputEl().addClass(['hide', 'x-hidden']);
26094             this.inputEl().dom.setAttribute('tabIndex', -1);
26095             //this.deferFocus();
26096         }
26097          
26098         if(this.resizable){
26099             this.setSize(this.wrap.getSize());
26100         }
26101         
26102         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26103     },
26104  
26105     // private (for BoxComponent)
26106     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26107
26108     // private (for BoxComponent)
26109     getResizeEl : function(){
26110         return this.wrap;
26111     },
26112
26113     // private (for BoxComponent)
26114     getPositionEl : function(){
26115         return this.wrap;
26116     },
26117
26118     // private
26119     initEvents : function(){
26120         this.originalValue = this.getValue();
26121     },
26122
26123 //    /**
26124 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26125 //     * @method
26126 //     */
26127 //    markInvalid : Roo.emptyFn,
26128 //    /**
26129 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26130 //     * @method
26131 //     */
26132 //    clearInvalid : Roo.emptyFn,
26133
26134     setValue : function(v){
26135         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26136         this.editorcore.pushValue();
26137     },
26138
26139      
26140     // private
26141     deferFocus : function(){
26142         this.focus.defer(10, this);
26143     },
26144
26145     // doc'ed in Field
26146     focus : function(){
26147         this.editorcore.focus();
26148         
26149     },
26150       
26151
26152     // private
26153     onDestroy : function(){
26154         
26155         
26156         
26157         if(this.rendered){
26158             
26159             for (var i =0; i < this.toolbars.length;i++) {
26160                 // fixme - ask toolbars for heights?
26161                 this.toolbars[i].onDestroy();
26162             }
26163             
26164             this.wrap.dom.innerHTML = '';
26165             this.wrap.remove();
26166         }
26167     },
26168
26169     // private
26170     onFirstFocus : function(){
26171         //Roo.log("onFirstFocus");
26172         this.editorcore.onFirstFocus();
26173          for (var i =0; i < this.toolbars.length;i++) {
26174             this.toolbars[i].onFirstFocus();
26175         }
26176         
26177     },
26178     
26179     // private
26180     syncValue : function()
26181     {   
26182         this.editorcore.syncValue();
26183     },
26184     
26185     pushValue : function()
26186     {   
26187         this.editorcore.pushValue();
26188     }
26189      
26190     
26191     // hide stuff that is not compatible
26192     /**
26193      * @event blur
26194      * @hide
26195      */
26196     /**
26197      * @event change
26198      * @hide
26199      */
26200     /**
26201      * @event focus
26202      * @hide
26203      */
26204     /**
26205      * @event specialkey
26206      * @hide
26207      */
26208     /**
26209      * @cfg {String} fieldClass @hide
26210      */
26211     /**
26212      * @cfg {String} focusClass @hide
26213      */
26214     /**
26215      * @cfg {String} autoCreate @hide
26216      */
26217     /**
26218      * @cfg {String} inputType @hide
26219      */
26220      
26221     /**
26222      * @cfg {String} invalidText @hide
26223      */
26224     /**
26225      * @cfg {String} msgFx @hide
26226      */
26227     /**
26228      * @cfg {String} validateOnBlur @hide
26229      */
26230 });
26231  
26232     
26233    
26234    
26235    
26236       
26237 Roo.namespace('Roo.bootstrap.htmleditor');
26238 /**
26239  * @class Roo.bootstrap.HtmlEditorToolbar1
26240  * Basic Toolbar
26241  * 
26242  * @example
26243  * Usage:
26244  *
26245  new Roo.bootstrap.HtmlEditor({
26246     ....
26247     toolbars : [
26248         new Roo.bootstrap.HtmlEditorToolbar1({
26249             disable : { fonts: 1 , format: 1, ..., ... , ...],
26250             btns : [ .... ]
26251         })
26252     }
26253      
26254  * 
26255  * @cfg {Object} disable List of elements to disable..
26256  * @cfg {Array} btns List of additional buttons.
26257  * 
26258  * 
26259  * NEEDS Extra CSS? 
26260  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26261  */
26262  
26263 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26264 {
26265     
26266     Roo.apply(this, config);
26267     
26268     // default disabled, based on 'good practice'..
26269     this.disable = this.disable || {};
26270     Roo.applyIf(this.disable, {
26271         fontSize : true,
26272         colors : true,
26273         specialElements : true
26274     });
26275     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26276     
26277     this.editor = config.editor;
26278     this.editorcore = config.editor.editorcore;
26279     
26280     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26281     
26282     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26283     // dont call parent... till later.
26284 }
26285 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26286      
26287     bar : true,
26288     
26289     editor : false,
26290     editorcore : false,
26291     
26292     
26293     formats : [
26294         "p" ,  
26295         "h1","h2","h3","h4","h5","h6", 
26296         "pre", "code", 
26297         "abbr", "acronym", "address", "cite", "samp", "var",
26298         'div','span'
26299     ],
26300     
26301     onRender : function(ct, position)
26302     {
26303        // Roo.log("Call onRender: " + this.xtype);
26304         
26305        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26306        Roo.log(this.el);
26307        this.el.dom.style.marginBottom = '0';
26308        var _this = this;
26309        var editorcore = this.editorcore;
26310        var editor= this.editor;
26311        
26312        var children = [];
26313        var btn = function(id,cmd , toggle, handler, html){
26314        
26315             var  event = toggle ? 'toggle' : 'click';
26316        
26317             var a = {
26318                 size : 'sm',
26319                 xtype: 'Button',
26320                 xns: Roo.bootstrap,
26321                 //glyphicon : id,
26322                 fa: id,
26323                 cmd : id || cmd,
26324                 enableToggle:toggle !== false,
26325                 html : html || '',
26326                 pressed : toggle ? false : null,
26327                 listeners : {}
26328             };
26329             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26330                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26331             };
26332             children.push(a);
26333             return a;
26334        }
26335        
26336     //    var cb_box = function...
26337         
26338         var style = {
26339                 xtype: 'Button',
26340                 size : 'sm',
26341                 xns: Roo.bootstrap,
26342                 fa : 'font',
26343                 //html : 'submit'
26344                 menu : {
26345                     xtype: 'Menu',
26346                     xns: Roo.bootstrap,
26347                     items:  []
26348                 }
26349         };
26350         Roo.each(this.formats, function(f) {
26351             style.menu.items.push({
26352                 xtype :'MenuItem',
26353                 xns: Roo.bootstrap,
26354                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26355                 tagname : f,
26356                 listeners : {
26357                     click : function()
26358                     {
26359                         editorcore.insertTag(this.tagname);
26360                         editor.focus();
26361                     }
26362                 }
26363                 
26364             });
26365         });
26366         children.push(style);   
26367         
26368         btn('bold',false,true);
26369         btn('italic',false,true);
26370         btn('align-left', 'justifyleft',true);
26371         btn('align-center', 'justifycenter',true);
26372         btn('align-right' , 'justifyright',true);
26373         btn('link', false, false, function(btn) {
26374             //Roo.log("create link?");
26375             var url = prompt(this.createLinkText, this.defaultLinkValue);
26376             if(url && url != 'http:/'+'/'){
26377                 this.editorcore.relayCmd('createlink', url);
26378             }
26379         }),
26380         btn('list','insertunorderedlist',true);
26381         btn('pencil', false,true, function(btn){
26382                 Roo.log(this);
26383                 this.toggleSourceEdit(btn.pressed);
26384         });
26385         
26386         if (this.editor.btns.length > 0) {
26387             for (var i = 0; i<this.editor.btns.length; i++) {
26388                 children.push(this.editor.btns[i]);
26389             }
26390         }
26391         
26392         /*
26393         var cog = {
26394                 xtype: 'Button',
26395                 size : 'sm',
26396                 xns: Roo.bootstrap,
26397                 glyphicon : 'cog',
26398                 //html : 'submit'
26399                 menu : {
26400                     xtype: 'Menu',
26401                     xns: Roo.bootstrap,
26402                     items:  []
26403                 }
26404         };
26405         
26406         cog.menu.items.push({
26407             xtype :'MenuItem',
26408             xns: Roo.bootstrap,
26409             html : Clean styles,
26410             tagname : f,
26411             listeners : {
26412                 click : function()
26413                 {
26414                     editorcore.insertTag(this.tagname);
26415                     editor.focus();
26416                 }
26417             }
26418             
26419         });
26420        */
26421         
26422          
26423        this.xtype = 'NavSimplebar';
26424         
26425         for(var i=0;i< children.length;i++) {
26426             
26427             this.buttons.add(this.addxtypeChild(children[i]));
26428             
26429         }
26430         
26431         editor.on('editorevent', this.updateToolbar, this);
26432     },
26433     onBtnClick : function(id)
26434     {
26435        this.editorcore.relayCmd(id);
26436        this.editorcore.focus();
26437     },
26438     
26439     /**
26440      * Protected method that will not generally be called directly. It triggers
26441      * a toolbar update by reading the markup state of the current selection in the editor.
26442      */
26443     updateToolbar: function(){
26444
26445         if(!this.editorcore.activated){
26446             this.editor.onFirstFocus(); // is this neeed?
26447             return;
26448         }
26449
26450         var btns = this.buttons; 
26451         var doc = this.editorcore.doc;
26452         btns.get('bold').setActive(doc.queryCommandState('bold'));
26453         btns.get('italic').setActive(doc.queryCommandState('italic'));
26454         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26455         
26456         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26457         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26458         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26459         
26460         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26461         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26462          /*
26463         
26464         var ans = this.editorcore.getAllAncestors();
26465         if (this.formatCombo) {
26466             
26467             
26468             var store = this.formatCombo.store;
26469             this.formatCombo.setValue("");
26470             for (var i =0; i < ans.length;i++) {
26471                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26472                     // select it..
26473                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26474                     break;
26475                 }
26476             }
26477         }
26478         
26479         
26480         
26481         // hides menus... - so this cant be on a menu...
26482         Roo.bootstrap.MenuMgr.hideAll();
26483         */
26484         Roo.bootstrap.MenuMgr.hideAll();
26485         //this.editorsyncValue();
26486     },
26487     onFirstFocus: function() {
26488         this.buttons.each(function(item){
26489            item.enable();
26490         });
26491     },
26492     toggleSourceEdit : function(sourceEditMode){
26493         
26494           
26495         if(sourceEditMode){
26496             Roo.log("disabling buttons");
26497            this.buttons.each( function(item){
26498                 if(item.cmd != 'pencil'){
26499                     item.disable();
26500                 }
26501             });
26502           
26503         }else{
26504             Roo.log("enabling buttons");
26505             if(this.editorcore.initialized){
26506                 this.buttons.each( function(item){
26507                     item.enable();
26508                 });
26509             }
26510             
26511         }
26512         Roo.log("calling toggole on editor");
26513         // tell the editor that it's been pressed..
26514         this.editor.toggleSourceEdit(sourceEditMode);
26515        
26516     }
26517 });
26518
26519
26520
26521
26522  
26523 /*
26524  * - LGPL
26525  */
26526
26527 /**
26528  * @class Roo.bootstrap.Markdown
26529  * @extends Roo.bootstrap.TextArea
26530  * Bootstrap Showdown editable area
26531  * @cfg {string} content
26532  * 
26533  * @constructor
26534  * Create a new Showdown
26535  */
26536
26537 Roo.bootstrap.Markdown = function(config){
26538     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26539    
26540 };
26541
26542 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26543     
26544     editing :false,
26545     
26546     initEvents : function()
26547     {
26548         
26549         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26550         this.markdownEl = this.el.createChild({
26551             cls : 'roo-markdown-area'
26552         });
26553         this.inputEl().addClass('d-none');
26554         if (this.getValue() == '') {
26555             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26556             
26557         } else {
26558             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26559         }
26560         this.markdownEl.on('click', this.toggleTextEdit, this);
26561         this.on('blur', this.toggleTextEdit, this);
26562         this.on('specialkey', this.resizeTextArea, this);
26563     },
26564     
26565     toggleTextEdit : function()
26566     {
26567         var sh = this.markdownEl.getHeight();
26568         this.inputEl().addClass('d-none');
26569         this.markdownEl.addClass('d-none');
26570         if (!this.editing) {
26571             // show editor?
26572             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26573             this.inputEl().removeClass('d-none');
26574             this.inputEl().focus();
26575             this.editing = true;
26576             return;
26577         }
26578         // show showdown...
26579         this.updateMarkdown();
26580         this.markdownEl.removeClass('d-none');
26581         this.editing = false;
26582         return;
26583     },
26584     updateMarkdown : function()
26585     {
26586         if (this.getValue() == '') {
26587             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26588             return;
26589         }
26590  
26591         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26592     },
26593     
26594     resizeTextArea: function () {
26595         
26596         var sh = 100;
26597         Roo.log([sh, this.getValue().split("\n").length * 30]);
26598         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26599     },
26600     setValue : function(val)
26601     {
26602         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26603         if (!this.editing) {
26604             this.updateMarkdown();
26605         }
26606         
26607     },
26608     focus : function()
26609     {
26610         if (!this.editing) {
26611             this.toggleTextEdit();
26612         }
26613         
26614     }
26615
26616
26617 });
26618 /**
26619  * @class Roo.bootstrap.Table.AbstractSelectionModel
26620  * @extends Roo.util.Observable
26621  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26622  * implemented by descendant classes.  This class should not be directly instantiated.
26623  * @constructor
26624  */
26625 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26626     this.locked = false;
26627     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26628 };
26629
26630
26631 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26632     /** @ignore Called by the grid automatically. Do not call directly. */
26633     init : function(grid){
26634         this.grid = grid;
26635         this.initEvents();
26636     },
26637
26638     /**
26639      * Locks the selections.
26640      */
26641     lock : function(){
26642         this.locked = true;
26643     },
26644
26645     /**
26646      * Unlocks the selections.
26647      */
26648     unlock : function(){
26649         this.locked = false;
26650     },
26651
26652     /**
26653      * Returns true if the selections are locked.
26654      * @return {Boolean}
26655      */
26656     isLocked : function(){
26657         return this.locked;
26658     },
26659     
26660     
26661     initEvents : function ()
26662     {
26663         
26664     }
26665 });
26666 /**
26667  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26668  * @class Roo.bootstrap.Table.RowSelectionModel
26669  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26670  * It supports multiple selections and keyboard selection/navigation. 
26671  * @constructor
26672  * @param {Object} config
26673  */
26674
26675 Roo.bootstrap.Table.RowSelectionModel = function(config){
26676     Roo.apply(this, config);
26677     this.selections = new Roo.util.MixedCollection(false, function(o){
26678         return o.id;
26679     });
26680
26681     this.last = false;
26682     this.lastActive = false;
26683
26684     this.addEvents({
26685         /**
26686              * @event selectionchange
26687              * Fires when the selection changes
26688              * @param {SelectionModel} this
26689              */
26690             "selectionchange" : true,
26691         /**
26692              * @event afterselectionchange
26693              * Fires after the selection changes (eg. by key press or clicking)
26694              * @param {SelectionModel} this
26695              */
26696             "afterselectionchange" : true,
26697         /**
26698              * @event beforerowselect
26699              * Fires when a row is selected being selected, return false to cancel.
26700              * @param {SelectionModel} this
26701              * @param {Number} rowIndex The selected index
26702              * @param {Boolean} keepExisting False if other selections will be cleared
26703              */
26704             "beforerowselect" : true,
26705         /**
26706              * @event rowselect
26707              * Fires when a row is selected.
26708              * @param {SelectionModel} this
26709              * @param {Number} rowIndex The selected index
26710              * @param {Roo.data.Record} r The record
26711              */
26712             "rowselect" : true,
26713         /**
26714              * @event rowdeselect
26715              * Fires when a row is deselected.
26716              * @param {SelectionModel} this
26717              * @param {Number} rowIndex The selected index
26718              */
26719         "rowdeselect" : true
26720     });
26721     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26722     this.locked = false;
26723  };
26724
26725 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26726     /**
26727      * @cfg {Boolean} singleSelect
26728      * True to allow selection of only one row at a time (defaults to false)
26729      */
26730     singleSelect : false,
26731
26732     // private
26733     initEvents : function()
26734     {
26735
26736         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26737         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26738         //}else{ // allow click to work like normal
26739          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26740         //}
26741         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26742         this.grid.on("rowclick", this.handleMouseDown, this);
26743         
26744         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26745             "up" : function(e){
26746                 if(!e.shiftKey){
26747                     this.selectPrevious(e.shiftKey);
26748                 }else if(this.last !== false && this.lastActive !== false){
26749                     var last = this.last;
26750                     this.selectRange(this.last,  this.lastActive-1);
26751                     this.grid.getView().focusRow(this.lastActive);
26752                     if(last !== false){
26753                         this.last = last;
26754                     }
26755                 }else{
26756                     this.selectFirstRow();
26757                 }
26758                 this.fireEvent("afterselectionchange", this);
26759             },
26760             "down" : function(e){
26761                 if(!e.shiftKey){
26762                     this.selectNext(e.shiftKey);
26763                 }else if(this.last !== false && this.lastActive !== false){
26764                     var last = this.last;
26765                     this.selectRange(this.last,  this.lastActive+1);
26766                     this.grid.getView().focusRow(this.lastActive);
26767                     if(last !== false){
26768                         this.last = last;
26769                     }
26770                 }else{
26771                     this.selectFirstRow();
26772                 }
26773                 this.fireEvent("afterselectionchange", this);
26774             },
26775             scope: this
26776         });
26777         this.grid.store.on('load', function(){
26778             this.selections.clear();
26779         },this);
26780         /*
26781         var view = this.grid.view;
26782         view.on("refresh", this.onRefresh, this);
26783         view.on("rowupdated", this.onRowUpdated, this);
26784         view.on("rowremoved", this.onRemove, this);
26785         */
26786     },
26787
26788     // private
26789     onRefresh : function()
26790     {
26791         var ds = this.grid.store, i, v = this.grid.view;
26792         var s = this.selections;
26793         s.each(function(r){
26794             if((i = ds.indexOfId(r.id)) != -1){
26795                 v.onRowSelect(i);
26796             }else{
26797                 s.remove(r);
26798             }
26799         });
26800     },
26801
26802     // private
26803     onRemove : function(v, index, r){
26804         this.selections.remove(r);
26805     },
26806
26807     // private
26808     onRowUpdated : function(v, index, r){
26809         if(this.isSelected(r)){
26810             v.onRowSelect(index);
26811         }
26812     },
26813
26814     /**
26815      * Select records.
26816      * @param {Array} records The records to select
26817      * @param {Boolean} keepExisting (optional) True to keep existing selections
26818      */
26819     selectRecords : function(records, keepExisting)
26820     {
26821         if(!keepExisting){
26822             this.clearSelections();
26823         }
26824             var ds = this.grid.store;
26825         for(var i = 0, len = records.length; i < len; i++){
26826             this.selectRow(ds.indexOf(records[i]), true);
26827         }
26828     },
26829
26830     /**
26831      * Gets the number of selected rows.
26832      * @return {Number}
26833      */
26834     getCount : function(){
26835         return this.selections.length;
26836     },
26837
26838     /**
26839      * Selects the first row in the grid.
26840      */
26841     selectFirstRow : function(){
26842         this.selectRow(0);
26843     },
26844
26845     /**
26846      * Select the last row.
26847      * @param {Boolean} keepExisting (optional) True to keep existing selections
26848      */
26849     selectLastRow : function(keepExisting){
26850         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26851         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26852     },
26853
26854     /**
26855      * Selects the row immediately following the last selected row.
26856      * @param {Boolean} keepExisting (optional) True to keep existing selections
26857      */
26858     selectNext : function(keepExisting)
26859     {
26860             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26861             this.selectRow(this.last+1, keepExisting);
26862             this.grid.getView().focusRow(this.last);
26863         }
26864     },
26865
26866     /**
26867      * Selects the row that precedes the last selected row.
26868      * @param {Boolean} keepExisting (optional) True to keep existing selections
26869      */
26870     selectPrevious : function(keepExisting){
26871         if(this.last){
26872             this.selectRow(this.last-1, keepExisting);
26873             this.grid.getView().focusRow(this.last);
26874         }
26875     },
26876
26877     /**
26878      * Returns the selected records
26879      * @return {Array} Array of selected records
26880      */
26881     getSelections : function(){
26882         return [].concat(this.selections.items);
26883     },
26884
26885     /**
26886      * Returns the first selected record.
26887      * @return {Record}
26888      */
26889     getSelected : function(){
26890         return this.selections.itemAt(0);
26891     },
26892
26893
26894     /**
26895      * Clears all selections.
26896      */
26897     clearSelections : function(fast)
26898     {
26899         if(this.locked) {
26900             return;
26901         }
26902         if(fast !== true){
26903                 var ds = this.grid.store;
26904             var s = this.selections;
26905             s.each(function(r){
26906                 this.deselectRow(ds.indexOfId(r.id));
26907             }, this);
26908             s.clear();
26909         }else{
26910             this.selections.clear();
26911         }
26912         this.last = false;
26913     },
26914
26915
26916     /**
26917      * Selects all rows.
26918      */
26919     selectAll : function(){
26920         if(this.locked) {
26921             return;
26922         }
26923         this.selections.clear();
26924         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26925             this.selectRow(i, true);
26926         }
26927     },
26928
26929     /**
26930      * Returns True if there is a selection.
26931      * @return {Boolean}
26932      */
26933     hasSelection : function(){
26934         return this.selections.length > 0;
26935     },
26936
26937     /**
26938      * Returns True if the specified row is selected.
26939      * @param {Number/Record} record The record or index of the record to check
26940      * @return {Boolean}
26941      */
26942     isSelected : function(index){
26943             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26944         return (r && this.selections.key(r.id) ? true : false);
26945     },
26946
26947     /**
26948      * Returns True if the specified record id is selected.
26949      * @param {String} id The id of record to check
26950      * @return {Boolean}
26951      */
26952     isIdSelected : function(id){
26953         return (this.selections.key(id) ? true : false);
26954     },
26955
26956
26957     // private
26958     handleMouseDBClick : function(e, t){
26959         
26960     },
26961     // private
26962     handleMouseDown : function(e, t)
26963     {
26964             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26965         if(this.isLocked() || rowIndex < 0 ){
26966             return;
26967         };
26968         if(e.shiftKey && this.last !== false){
26969             var last = this.last;
26970             this.selectRange(last, rowIndex, e.ctrlKey);
26971             this.last = last; // reset the last
26972             t.focus();
26973     
26974         }else{
26975             var isSelected = this.isSelected(rowIndex);
26976             //Roo.log("select row:" + rowIndex);
26977             if(isSelected){
26978                 this.deselectRow(rowIndex);
26979             } else {
26980                         this.selectRow(rowIndex, true);
26981             }
26982     
26983             /*
26984                 if(e.button !== 0 && isSelected){
26985                 alert('rowIndex 2: ' + rowIndex);
26986                     view.focusRow(rowIndex);
26987                 }else if(e.ctrlKey && isSelected){
26988                     this.deselectRow(rowIndex);
26989                 }else if(!isSelected){
26990                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26991                     view.focusRow(rowIndex);
26992                 }
26993             */
26994         }
26995         this.fireEvent("afterselectionchange", this);
26996     },
26997     // private
26998     handleDragableRowClick :  function(grid, rowIndex, e) 
26999     {
27000         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27001             this.selectRow(rowIndex, false);
27002             grid.view.focusRow(rowIndex);
27003              this.fireEvent("afterselectionchange", this);
27004         }
27005     },
27006     
27007     /**
27008      * Selects multiple rows.
27009      * @param {Array} rows Array of the indexes of the row to select
27010      * @param {Boolean} keepExisting (optional) True to keep existing selections
27011      */
27012     selectRows : function(rows, keepExisting){
27013         if(!keepExisting){
27014             this.clearSelections();
27015         }
27016         for(var i = 0, len = rows.length; i < len; i++){
27017             this.selectRow(rows[i], true);
27018         }
27019     },
27020
27021     /**
27022      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27023      * @param {Number} startRow The index of the first row in the range
27024      * @param {Number} endRow The index of the last row in the range
27025      * @param {Boolean} keepExisting (optional) True to retain existing selections
27026      */
27027     selectRange : function(startRow, endRow, keepExisting){
27028         if(this.locked) {
27029             return;
27030         }
27031         if(!keepExisting){
27032             this.clearSelections();
27033         }
27034         if(startRow <= endRow){
27035             for(var i = startRow; i <= endRow; i++){
27036                 this.selectRow(i, true);
27037             }
27038         }else{
27039             for(var i = startRow; i >= endRow; i--){
27040                 this.selectRow(i, true);
27041             }
27042         }
27043     },
27044
27045     /**
27046      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27047      * @param {Number} startRow The index of the first row in the range
27048      * @param {Number} endRow The index of the last row in the range
27049      */
27050     deselectRange : function(startRow, endRow, preventViewNotify){
27051         if(this.locked) {
27052             return;
27053         }
27054         for(var i = startRow; i <= endRow; i++){
27055             this.deselectRow(i, preventViewNotify);
27056         }
27057     },
27058
27059     /**
27060      * Selects a row.
27061      * @param {Number} row The index of the row to select
27062      * @param {Boolean} keepExisting (optional) True to keep existing selections
27063      */
27064     selectRow : function(index, keepExisting, preventViewNotify)
27065     {
27066             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27067             return;
27068         }
27069         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27070             if(!keepExisting || this.singleSelect){
27071                 this.clearSelections();
27072             }
27073             
27074             var r = this.grid.store.getAt(index);
27075             //console.log('selectRow - record id :' + r.id);
27076             
27077             this.selections.add(r);
27078             this.last = this.lastActive = index;
27079             if(!preventViewNotify){
27080                 var proxy = new Roo.Element(
27081                                 this.grid.getRowDom(index)
27082                 );
27083                 proxy.addClass('bg-info info');
27084             }
27085             this.fireEvent("rowselect", this, index, r);
27086             this.fireEvent("selectionchange", this);
27087         }
27088     },
27089
27090     /**
27091      * Deselects a row.
27092      * @param {Number} row The index of the row to deselect
27093      */
27094     deselectRow : function(index, preventViewNotify)
27095     {
27096         if(this.locked) {
27097             return;
27098         }
27099         if(this.last == index){
27100             this.last = false;
27101         }
27102         if(this.lastActive == index){
27103             this.lastActive = false;
27104         }
27105         
27106         var r = this.grid.store.getAt(index);
27107         if (!r) {
27108             return;
27109         }
27110         
27111         this.selections.remove(r);
27112         //.console.log('deselectRow - record id :' + r.id);
27113         if(!preventViewNotify){
27114         
27115             var proxy = new Roo.Element(
27116                 this.grid.getRowDom(index)
27117             );
27118             proxy.removeClass('bg-info info');
27119         }
27120         this.fireEvent("rowdeselect", this, index);
27121         this.fireEvent("selectionchange", this);
27122     },
27123
27124     // private
27125     restoreLast : function(){
27126         if(this._last){
27127             this.last = this._last;
27128         }
27129     },
27130
27131     // private
27132     acceptsNav : function(row, col, cm){
27133         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27134     },
27135
27136     // private
27137     onEditorKey : function(field, e){
27138         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27139         if(k == e.TAB){
27140             e.stopEvent();
27141             ed.completeEdit();
27142             if(e.shiftKey){
27143                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27144             }else{
27145                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27146             }
27147         }else if(k == e.ENTER && !e.ctrlKey){
27148             e.stopEvent();
27149             ed.completeEdit();
27150             if(e.shiftKey){
27151                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27152             }else{
27153                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27154             }
27155         }else if(k == e.ESC){
27156             ed.cancelEdit();
27157         }
27158         if(newCell){
27159             g.startEditing(newCell[0], newCell[1]);
27160         }
27161     }
27162 });
27163 /*
27164  * Based on:
27165  * Ext JS Library 1.1.1
27166  * Copyright(c) 2006-2007, Ext JS, LLC.
27167  *
27168  * Originally Released Under LGPL - original licence link has changed is not relivant.
27169  *
27170  * Fork - LGPL
27171  * <script type="text/javascript">
27172  */
27173  
27174 /**
27175  * @class Roo.bootstrap.PagingToolbar
27176  * @extends Roo.bootstrap.NavSimplebar
27177  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27178  * @constructor
27179  * Create a new PagingToolbar
27180  * @param {Object} config The config object
27181  * @param {Roo.data.Store} store
27182  */
27183 Roo.bootstrap.PagingToolbar = function(config)
27184 {
27185     // old args format still supported... - xtype is prefered..
27186         // created from xtype...
27187     
27188     this.ds = config.dataSource;
27189     
27190     if (config.store && !this.ds) {
27191         this.store= Roo.factory(config.store, Roo.data);
27192         this.ds = this.store;
27193         this.ds.xmodule = this.xmodule || false;
27194     }
27195     
27196     this.toolbarItems = [];
27197     if (config.items) {
27198         this.toolbarItems = config.items;
27199     }
27200     
27201     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27202     
27203     this.cursor = 0;
27204     
27205     if (this.ds) { 
27206         this.bind(this.ds);
27207     }
27208     
27209     if (Roo.bootstrap.version == 4) {
27210         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27211     } else {
27212         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27213     }
27214     
27215 };
27216
27217 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27218     /**
27219      * @cfg {Roo.data.Store} dataSource
27220      * The underlying data store providing the paged data
27221      */
27222     /**
27223      * @cfg {String/HTMLElement/Element} container
27224      * container The id or element that will contain the toolbar
27225      */
27226     /**
27227      * @cfg {Boolean} displayInfo
27228      * True to display the displayMsg (defaults to false)
27229      */
27230     /**
27231      * @cfg {Number} pageSize
27232      * The number of records to display per page (defaults to 20)
27233      */
27234     pageSize: 20,
27235     /**
27236      * @cfg {String} displayMsg
27237      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27238      */
27239     displayMsg : 'Displaying {0} - {1} of {2}',
27240     /**
27241      * @cfg {String} emptyMsg
27242      * The message to display when no records are found (defaults to "No data to display")
27243      */
27244     emptyMsg : 'No data to display',
27245     /**
27246      * Customizable piece of the default paging text (defaults to "Page")
27247      * @type String
27248      */
27249     beforePageText : "Page",
27250     /**
27251      * Customizable piece of the default paging text (defaults to "of %0")
27252      * @type String
27253      */
27254     afterPageText : "of {0}",
27255     /**
27256      * Customizable piece of the default paging text (defaults to "First Page")
27257      * @type String
27258      */
27259     firstText : "First Page",
27260     /**
27261      * Customizable piece of the default paging text (defaults to "Previous Page")
27262      * @type String
27263      */
27264     prevText : "Previous Page",
27265     /**
27266      * Customizable piece of the default paging text (defaults to "Next Page")
27267      * @type String
27268      */
27269     nextText : "Next Page",
27270     /**
27271      * Customizable piece of the default paging text (defaults to "Last Page")
27272      * @type String
27273      */
27274     lastText : "Last Page",
27275     /**
27276      * Customizable piece of the default paging text (defaults to "Refresh")
27277      * @type String
27278      */
27279     refreshText : "Refresh",
27280
27281     buttons : false,
27282     // private
27283     onRender : function(ct, position) 
27284     {
27285         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27286         this.navgroup.parentId = this.id;
27287         this.navgroup.onRender(this.el, null);
27288         // add the buttons to the navgroup
27289         
27290         if(this.displayInfo){
27291             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27292             this.displayEl = this.el.select('.x-paging-info', true).first();
27293 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27294 //            this.displayEl = navel.el.select('span',true).first();
27295         }
27296         
27297         var _this = this;
27298         
27299         if(this.buttons){
27300             Roo.each(_this.buttons, function(e){ // this might need to use render????
27301                Roo.factory(e).render(_this.el);
27302             });
27303         }
27304             
27305         Roo.each(_this.toolbarItems, function(e) {
27306             _this.navgroup.addItem(e);
27307         });
27308         
27309         
27310         this.first = this.navgroup.addItem({
27311             tooltip: this.firstText,
27312             cls: "prev btn-outline-secondary",
27313             html : ' <i class="fa fa-step-backward"></i>',
27314             disabled: true,
27315             preventDefault: true,
27316             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27317         });
27318         
27319         this.prev =  this.navgroup.addItem({
27320             tooltip: this.prevText,
27321             cls: "prev btn-outline-secondary",
27322             html : ' <i class="fa fa-backward"></i>',
27323             disabled: true,
27324             preventDefault: true,
27325             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27326         });
27327     //this.addSeparator();
27328         
27329         
27330         var field = this.navgroup.addItem( {
27331             tagtype : 'span',
27332             cls : 'x-paging-position  btn-outline-secondary',
27333              disabled: true,
27334             html : this.beforePageText  +
27335                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27336                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27337          } ); //?? escaped?
27338         
27339         this.field = field.el.select('input', true).first();
27340         this.field.on("keydown", this.onPagingKeydown, this);
27341         this.field.on("focus", function(){this.dom.select();});
27342     
27343     
27344         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27345         //this.field.setHeight(18);
27346         //this.addSeparator();
27347         this.next = this.navgroup.addItem({
27348             tooltip: this.nextText,
27349             cls: "next btn-outline-secondary",
27350             html : ' <i class="fa fa-forward"></i>',
27351             disabled: true,
27352             preventDefault: true,
27353             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27354         });
27355         this.last = this.navgroup.addItem({
27356             tooltip: this.lastText,
27357             html : ' <i class="fa fa-step-forward"></i>',
27358             cls: "next btn-outline-secondary",
27359             disabled: true,
27360             preventDefault: true,
27361             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27362         });
27363     //this.addSeparator();
27364         this.loading = this.navgroup.addItem({
27365             tooltip: this.refreshText,
27366             cls: "btn-outline-secondary",
27367             html : ' <i class="fa fa-refresh"></i>',
27368             preventDefault: true,
27369             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27370         });
27371         
27372     },
27373
27374     // private
27375     updateInfo : function(){
27376         if(this.displayEl){
27377             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27378             var msg = count == 0 ?
27379                 this.emptyMsg :
27380                 String.format(
27381                     this.displayMsg,
27382                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27383                 );
27384             this.displayEl.update(msg);
27385         }
27386     },
27387
27388     // private
27389     onLoad : function(ds, r, o)
27390     {
27391         this.cursor = o.params && o.params.start ? o.params.start : 0;
27392         
27393         var d = this.getPageData(),
27394             ap = d.activePage,
27395             ps = d.pages;
27396         
27397         
27398         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27399         this.field.dom.value = ap;
27400         this.first.setDisabled(ap == 1);
27401         this.prev.setDisabled(ap == 1);
27402         this.next.setDisabled(ap == ps);
27403         this.last.setDisabled(ap == ps);
27404         this.loading.enable();
27405         this.updateInfo();
27406     },
27407
27408     // private
27409     getPageData : function(){
27410         var total = this.ds.getTotalCount();
27411         return {
27412             total : total,
27413             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27414             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27415         };
27416     },
27417
27418     // private
27419     onLoadError : function(){
27420         this.loading.enable();
27421     },
27422
27423     // private
27424     onPagingKeydown : function(e){
27425         var k = e.getKey();
27426         var d = this.getPageData();
27427         if(k == e.RETURN){
27428             var v = this.field.dom.value, pageNum;
27429             if(!v || isNaN(pageNum = parseInt(v, 10))){
27430                 this.field.dom.value = d.activePage;
27431                 return;
27432             }
27433             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27434             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27435             e.stopEvent();
27436         }
27437         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))
27438         {
27439           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27440           this.field.dom.value = pageNum;
27441           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27442           e.stopEvent();
27443         }
27444         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27445         {
27446           var v = this.field.dom.value, pageNum; 
27447           var increment = (e.shiftKey) ? 10 : 1;
27448           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27449                 increment *= -1;
27450           }
27451           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27452             this.field.dom.value = d.activePage;
27453             return;
27454           }
27455           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27456           {
27457             this.field.dom.value = parseInt(v, 10) + increment;
27458             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27459             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27460           }
27461           e.stopEvent();
27462         }
27463     },
27464
27465     // private
27466     beforeLoad : function(){
27467         if(this.loading){
27468             this.loading.disable();
27469         }
27470     },
27471
27472     // private
27473     onClick : function(which){
27474         
27475         var ds = this.ds;
27476         if (!ds) {
27477             return;
27478         }
27479         
27480         switch(which){
27481             case "first":
27482                 ds.load({params:{start: 0, limit: this.pageSize}});
27483             break;
27484             case "prev":
27485                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27486             break;
27487             case "next":
27488                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27489             break;
27490             case "last":
27491                 var total = ds.getTotalCount();
27492                 var extra = total % this.pageSize;
27493                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27494                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27495             break;
27496             case "refresh":
27497                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27498             break;
27499         }
27500     },
27501
27502     /**
27503      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27504      * @param {Roo.data.Store} store The data store to unbind
27505      */
27506     unbind : function(ds){
27507         ds.un("beforeload", this.beforeLoad, this);
27508         ds.un("load", this.onLoad, this);
27509         ds.un("loadexception", this.onLoadError, this);
27510         ds.un("remove", this.updateInfo, this);
27511         ds.un("add", this.updateInfo, this);
27512         this.ds = undefined;
27513     },
27514
27515     /**
27516      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27517      * @param {Roo.data.Store} store The data store to bind
27518      */
27519     bind : function(ds){
27520         ds.on("beforeload", this.beforeLoad, this);
27521         ds.on("load", this.onLoad, this);
27522         ds.on("loadexception", this.onLoadError, this);
27523         ds.on("remove", this.updateInfo, this);
27524         ds.on("add", this.updateInfo, this);
27525         this.ds = ds;
27526     }
27527 });/*
27528  * - LGPL
27529  *
27530  * element
27531  * 
27532  */
27533
27534 /**
27535  * @class Roo.bootstrap.MessageBar
27536  * @extends Roo.bootstrap.Component
27537  * Bootstrap MessageBar class
27538  * @cfg {String} html contents of the MessageBar
27539  * @cfg {String} weight (info | success | warning | danger) default info
27540  * @cfg {String} beforeClass insert the bar before the given class
27541  * @cfg {Boolean} closable (true | false) default false
27542  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27543  * 
27544  * @constructor
27545  * Create a new Element
27546  * @param {Object} config The config object
27547  */
27548
27549 Roo.bootstrap.MessageBar = function(config){
27550     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27551 };
27552
27553 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27554     
27555     html: '',
27556     weight: 'info',
27557     closable: false,
27558     fixed: false,
27559     beforeClass: 'bootstrap-sticky-wrap',
27560     
27561     getAutoCreate : function(){
27562         
27563         var cfg = {
27564             tag: 'div',
27565             cls: 'alert alert-dismissable alert-' + this.weight,
27566             cn: [
27567                 {
27568                     tag: 'span',
27569                     cls: 'message',
27570                     html: this.html || ''
27571                 }
27572             ]
27573         };
27574         
27575         if(this.fixed){
27576             cfg.cls += ' alert-messages-fixed';
27577         }
27578         
27579         if(this.closable){
27580             cfg.cn.push({
27581                 tag: 'button',
27582                 cls: 'close',
27583                 html: 'x'
27584             });
27585         }
27586         
27587         return cfg;
27588     },
27589     
27590     onRender : function(ct, position)
27591     {
27592         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27593         
27594         if(!this.el){
27595             var cfg = Roo.apply({},  this.getAutoCreate());
27596             cfg.id = Roo.id();
27597             
27598             if (this.cls) {
27599                 cfg.cls += ' ' + this.cls;
27600             }
27601             if (this.style) {
27602                 cfg.style = this.style;
27603             }
27604             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27605             
27606             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27607         }
27608         
27609         this.el.select('>button.close').on('click', this.hide, this);
27610         
27611     },
27612     
27613     show : function()
27614     {
27615         if (!this.rendered) {
27616             this.render();
27617         }
27618         
27619         this.el.show();
27620         
27621         this.fireEvent('show', this);
27622         
27623     },
27624     
27625     hide : function()
27626     {
27627         if (!this.rendered) {
27628             this.render();
27629         }
27630         
27631         this.el.hide();
27632         
27633         this.fireEvent('hide', this);
27634     },
27635     
27636     update : function()
27637     {
27638 //        var e = this.el.dom.firstChild;
27639 //        
27640 //        if(this.closable){
27641 //            e = e.nextSibling;
27642 //        }
27643 //        
27644 //        e.data = this.html || '';
27645
27646         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27647     }
27648    
27649 });
27650
27651  
27652
27653      /*
27654  * - LGPL
27655  *
27656  * Graph
27657  * 
27658  */
27659
27660
27661 /**
27662  * @class Roo.bootstrap.Graph
27663  * @extends Roo.bootstrap.Component
27664  * Bootstrap Graph class
27665 > Prameters
27666  -sm {number} sm 4
27667  -md {number} md 5
27668  @cfg {String} graphtype  bar | vbar | pie
27669  @cfg {number} g_x coodinator | centre x (pie)
27670  @cfg {number} g_y coodinator | centre y (pie)
27671  @cfg {number} g_r radius (pie)
27672  @cfg {number} g_height height of the chart (respected by all elements in the set)
27673  @cfg {number} g_width width of the chart (respected by all elements in the set)
27674  @cfg {Object} title The title of the chart
27675     
27676  -{Array}  values
27677  -opts (object) options for the chart 
27678      o {
27679      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27680      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27681      o vgutter (number)
27682      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.
27683      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27684      o to
27685      o stretch (boolean)
27686      o }
27687  -opts (object) options for the pie
27688      o{
27689      o cut
27690      o startAngle (number)
27691      o endAngle (number)
27692      } 
27693  *
27694  * @constructor
27695  * Create a new Input
27696  * @param {Object} config The config object
27697  */
27698
27699 Roo.bootstrap.Graph = function(config){
27700     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27701     
27702     this.addEvents({
27703         // img events
27704         /**
27705          * @event click
27706          * The img click event for the img.
27707          * @param {Roo.EventObject} e
27708          */
27709         "click" : true
27710     });
27711 };
27712
27713 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27714     
27715     sm: 4,
27716     md: 5,
27717     graphtype: 'bar',
27718     g_height: 250,
27719     g_width: 400,
27720     g_x: 50,
27721     g_y: 50,
27722     g_r: 30,
27723     opts:{
27724         //g_colors: this.colors,
27725         g_type: 'soft',
27726         g_gutter: '20%'
27727
27728     },
27729     title : false,
27730
27731     getAutoCreate : function(){
27732         
27733         var cfg = {
27734             tag: 'div',
27735             html : null
27736         };
27737         
27738         
27739         return  cfg;
27740     },
27741
27742     onRender : function(ct,position){
27743         
27744         
27745         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27746         
27747         if (typeof(Raphael) == 'undefined') {
27748             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27749             return;
27750         }
27751         
27752         this.raphael = Raphael(this.el.dom);
27753         
27754                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27755                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27756                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27757                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27758                 /*
27759                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27760                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27761                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27762                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27763                 
27764                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27765                 r.barchart(330, 10, 300, 220, data1);
27766                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27767                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27768                 */
27769                 
27770                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27771                 // r.barchart(30, 30, 560, 250,  xdata, {
27772                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27773                 //     axis : "0 0 1 1",
27774                 //     axisxlabels :  xdata
27775                 //     //yvalues : cols,
27776                    
27777                 // });
27778 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27779 //        
27780 //        this.load(null,xdata,{
27781 //                axis : "0 0 1 1",
27782 //                axisxlabels :  xdata
27783 //                });
27784
27785     },
27786
27787     load : function(graphtype,xdata,opts)
27788     {
27789         this.raphael.clear();
27790         if(!graphtype) {
27791             graphtype = this.graphtype;
27792         }
27793         if(!opts){
27794             opts = this.opts;
27795         }
27796         var r = this.raphael,
27797             fin = function () {
27798                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27799             },
27800             fout = function () {
27801                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27802             },
27803             pfin = function() {
27804                 this.sector.stop();
27805                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27806
27807                 if (this.label) {
27808                     this.label[0].stop();
27809                     this.label[0].attr({ r: 7.5 });
27810                     this.label[1].attr({ "font-weight": 800 });
27811                 }
27812             },
27813             pfout = function() {
27814                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27815
27816                 if (this.label) {
27817                     this.label[0].animate({ r: 5 }, 500, "bounce");
27818                     this.label[1].attr({ "font-weight": 400 });
27819                 }
27820             };
27821
27822         switch(graphtype){
27823             case 'bar':
27824                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27825                 break;
27826             case 'hbar':
27827                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27828                 break;
27829             case 'pie':
27830 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27831 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27832 //            
27833                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27834                 
27835                 break;
27836
27837         }
27838         
27839         if(this.title){
27840             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27841         }
27842         
27843     },
27844     
27845     setTitle: function(o)
27846     {
27847         this.title = o;
27848     },
27849     
27850     initEvents: function() {
27851         
27852         if(!this.href){
27853             this.el.on('click', this.onClick, this);
27854         }
27855     },
27856     
27857     onClick : function(e)
27858     {
27859         Roo.log('img onclick');
27860         this.fireEvent('click', this, e);
27861     }
27862    
27863 });
27864
27865  
27866 /*
27867  * - LGPL
27868  *
27869  * numberBox
27870  * 
27871  */
27872 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27873
27874 /**
27875  * @class Roo.bootstrap.dash.NumberBox
27876  * @extends Roo.bootstrap.Component
27877  * Bootstrap NumberBox class
27878  * @cfg {String} headline Box headline
27879  * @cfg {String} content Box content
27880  * @cfg {String} icon Box icon
27881  * @cfg {String} footer Footer text
27882  * @cfg {String} fhref Footer href
27883  * 
27884  * @constructor
27885  * Create a new NumberBox
27886  * @param {Object} config The config object
27887  */
27888
27889
27890 Roo.bootstrap.dash.NumberBox = function(config){
27891     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27892     
27893 };
27894
27895 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27896     
27897     headline : '',
27898     content : '',
27899     icon : '',
27900     footer : '',
27901     fhref : '',
27902     ficon : '',
27903     
27904     getAutoCreate : function(){
27905         
27906         var cfg = {
27907             tag : 'div',
27908             cls : 'small-box ',
27909             cn : [
27910                 {
27911                     tag : 'div',
27912                     cls : 'inner',
27913                     cn :[
27914                         {
27915                             tag : 'h3',
27916                             cls : 'roo-headline',
27917                             html : this.headline
27918                         },
27919                         {
27920                             tag : 'p',
27921                             cls : 'roo-content',
27922                             html : this.content
27923                         }
27924                     ]
27925                 }
27926             ]
27927         };
27928         
27929         if(this.icon){
27930             cfg.cn.push({
27931                 tag : 'div',
27932                 cls : 'icon',
27933                 cn :[
27934                     {
27935                         tag : 'i',
27936                         cls : 'ion ' + this.icon
27937                     }
27938                 ]
27939             });
27940         }
27941         
27942         if(this.footer){
27943             var footer = {
27944                 tag : 'a',
27945                 cls : 'small-box-footer',
27946                 href : this.fhref || '#',
27947                 html : this.footer
27948             };
27949             
27950             cfg.cn.push(footer);
27951             
27952         }
27953         
27954         return  cfg;
27955     },
27956
27957     onRender : function(ct,position){
27958         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27959
27960
27961        
27962                 
27963     },
27964
27965     setHeadline: function (value)
27966     {
27967         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27968     },
27969     
27970     setFooter: function (value, href)
27971     {
27972         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27973         
27974         if(href){
27975             this.el.select('a.small-box-footer',true).first().attr('href', href);
27976         }
27977         
27978     },
27979
27980     setContent: function (value)
27981     {
27982         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27983     },
27984
27985     initEvents: function() 
27986     {   
27987         
27988     }
27989     
27990 });
27991
27992  
27993 /*
27994  * - LGPL
27995  *
27996  * TabBox
27997  * 
27998  */
27999 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28000
28001 /**
28002  * @class Roo.bootstrap.dash.TabBox
28003  * @extends Roo.bootstrap.Component
28004  * Bootstrap TabBox class
28005  * @cfg {String} title Title of the TabBox
28006  * @cfg {String} icon Icon of the TabBox
28007  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28008  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28009  * 
28010  * @constructor
28011  * Create a new TabBox
28012  * @param {Object} config The config object
28013  */
28014
28015
28016 Roo.bootstrap.dash.TabBox = function(config){
28017     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28018     this.addEvents({
28019         // raw events
28020         /**
28021          * @event addpane
28022          * When a pane is added
28023          * @param {Roo.bootstrap.dash.TabPane} pane
28024          */
28025         "addpane" : true,
28026         /**
28027          * @event activatepane
28028          * When a pane is activated
28029          * @param {Roo.bootstrap.dash.TabPane} pane
28030          */
28031         "activatepane" : true
28032         
28033          
28034     });
28035     
28036     this.panes = [];
28037 };
28038
28039 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28040
28041     title : '',
28042     icon : false,
28043     showtabs : true,
28044     tabScrollable : false,
28045     
28046     getChildContainer : function()
28047     {
28048         return this.el.select('.tab-content', true).first();
28049     },
28050     
28051     getAutoCreate : function(){
28052         
28053         var header = {
28054             tag: 'li',
28055             cls: 'pull-left header',
28056             html: this.title,
28057             cn : []
28058         };
28059         
28060         if(this.icon){
28061             header.cn.push({
28062                 tag: 'i',
28063                 cls: 'fa ' + this.icon
28064             });
28065         }
28066         
28067         var h = {
28068             tag: 'ul',
28069             cls: 'nav nav-tabs pull-right',
28070             cn: [
28071                 header
28072             ]
28073         };
28074         
28075         if(this.tabScrollable){
28076             h = {
28077                 tag: 'div',
28078                 cls: 'tab-header',
28079                 cn: [
28080                     {
28081                         tag: 'ul',
28082                         cls: 'nav nav-tabs pull-right',
28083                         cn: [
28084                             header
28085                         ]
28086                     }
28087                 ]
28088             };
28089         }
28090         
28091         var cfg = {
28092             tag: 'div',
28093             cls: 'nav-tabs-custom',
28094             cn: [
28095                 h,
28096                 {
28097                     tag: 'div',
28098                     cls: 'tab-content no-padding',
28099                     cn: []
28100                 }
28101             ]
28102         };
28103
28104         return  cfg;
28105     },
28106     initEvents : function()
28107     {
28108         //Roo.log('add add pane handler');
28109         this.on('addpane', this.onAddPane, this);
28110     },
28111      /**
28112      * Updates the box title
28113      * @param {String} html to set the title to.
28114      */
28115     setTitle : function(value)
28116     {
28117         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28118     },
28119     onAddPane : function(pane)
28120     {
28121         this.panes.push(pane);
28122         //Roo.log('addpane');
28123         //Roo.log(pane);
28124         // tabs are rendere left to right..
28125         if(!this.showtabs){
28126             return;
28127         }
28128         
28129         var ctr = this.el.select('.nav-tabs', true).first();
28130          
28131          
28132         var existing = ctr.select('.nav-tab',true);
28133         var qty = existing.getCount();;
28134         
28135         
28136         var tab = ctr.createChild({
28137             tag : 'li',
28138             cls : 'nav-tab' + (qty ? '' : ' active'),
28139             cn : [
28140                 {
28141                     tag : 'a',
28142                     href:'#',
28143                     html : pane.title
28144                 }
28145             ]
28146         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28147         pane.tab = tab;
28148         
28149         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28150         if (!qty) {
28151             pane.el.addClass('active');
28152         }
28153         
28154                 
28155     },
28156     onTabClick : function(ev,un,ob,pane)
28157     {
28158         //Roo.log('tab - prev default');
28159         ev.preventDefault();
28160         
28161         
28162         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28163         pane.tab.addClass('active');
28164         //Roo.log(pane.title);
28165         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28166         // technically we should have a deactivate event.. but maybe add later.
28167         // and it should not de-activate the selected tab...
28168         this.fireEvent('activatepane', pane);
28169         pane.el.addClass('active');
28170         pane.fireEvent('activate');
28171         
28172         
28173     },
28174     
28175     getActivePane : function()
28176     {
28177         var r = false;
28178         Roo.each(this.panes, function(p) {
28179             if(p.el.hasClass('active')){
28180                 r = p;
28181                 return false;
28182             }
28183             
28184             return;
28185         });
28186         
28187         return r;
28188     }
28189     
28190     
28191 });
28192
28193  
28194 /*
28195  * - LGPL
28196  *
28197  * Tab pane
28198  * 
28199  */
28200 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28201 /**
28202  * @class Roo.bootstrap.TabPane
28203  * @extends Roo.bootstrap.Component
28204  * Bootstrap TabPane class
28205  * @cfg {Boolean} active (false | true) Default false
28206  * @cfg {String} title title of panel
28207
28208  * 
28209  * @constructor
28210  * Create a new TabPane
28211  * @param {Object} config The config object
28212  */
28213
28214 Roo.bootstrap.dash.TabPane = function(config){
28215     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28216     
28217     this.addEvents({
28218         // raw events
28219         /**
28220          * @event activate
28221          * When a pane is activated
28222          * @param {Roo.bootstrap.dash.TabPane} pane
28223          */
28224         "activate" : true
28225          
28226     });
28227 };
28228
28229 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28230     
28231     active : false,
28232     title : '',
28233     
28234     // the tabBox that this is attached to.
28235     tab : false,
28236      
28237     getAutoCreate : function() 
28238     {
28239         var cfg = {
28240             tag: 'div',
28241             cls: 'tab-pane'
28242         };
28243         
28244         if(this.active){
28245             cfg.cls += ' active';
28246         }
28247         
28248         return cfg;
28249     },
28250     initEvents  : function()
28251     {
28252         //Roo.log('trigger add pane handler');
28253         this.parent().fireEvent('addpane', this)
28254     },
28255     
28256      /**
28257      * Updates the tab title 
28258      * @param {String} html to set the title to.
28259      */
28260     setTitle: function(str)
28261     {
28262         if (!this.tab) {
28263             return;
28264         }
28265         this.title = str;
28266         this.tab.select('a', true).first().dom.innerHTML = str;
28267         
28268     }
28269     
28270     
28271     
28272 });
28273
28274  
28275
28276
28277  /*
28278  * - LGPL
28279  *
28280  * menu
28281  * 
28282  */
28283 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28284
28285 /**
28286  * @class Roo.bootstrap.menu.Menu
28287  * @extends Roo.bootstrap.Component
28288  * Bootstrap Menu class - container for Menu
28289  * @cfg {String} html Text of the menu
28290  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28291  * @cfg {String} icon Font awesome icon
28292  * @cfg {String} pos Menu align to (top | bottom) default bottom
28293  * 
28294  * 
28295  * @constructor
28296  * Create a new Menu
28297  * @param {Object} config The config object
28298  */
28299
28300
28301 Roo.bootstrap.menu.Menu = function(config){
28302     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28303     
28304     this.addEvents({
28305         /**
28306          * @event beforeshow
28307          * Fires before this menu is displayed
28308          * @param {Roo.bootstrap.menu.Menu} this
28309          */
28310         beforeshow : true,
28311         /**
28312          * @event beforehide
28313          * Fires before this menu is hidden
28314          * @param {Roo.bootstrap.menu.Menu} this
28315          */
28316         beforehide : true,
28317         /**
28318          * @event show
28319          * Fires after this menu is displayed
28320          * @param {Roo.bootstrap.menu.Menu} this
28321          */
28322         show : true,
28323         /**
28324          * @event hide
28325          * Fires after this menu is hidden
28326          * @param {Roo.bootstrap.menu.Menu} this
28327          */
28328         hide : true,
28329         /**
28330          * @event click
28331          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28332          * @param {Roo.bootstrap.menu.Menu} this
28333          * @param {Roo.EventObject} e
28334          */
28335         click : true
28336     });
28337     
28338 };
28339
28340 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28341     
28342     submenu : false,
28343     html : '',
28344     weight : 'default',
28345     icon : false,
28346     pos : 'bottom',
28347     
28348     
28349     getChildContainer : function() {
28350         if(this.isSubMenu){
28351             return this.el;
28352         }
28353         
28354         return this.el.select('ul.dropdown-menu', true).first();  
28355     },
28356     
28357     getAutoCreate : function()
28358     {
28359         var text = [
28360             {
28361                 tag : 'span',
28362                 cls : 'roo-menu-text',
28363                 html : this.html
28364             }
28365         ];
28366         
28367         if(this.icon){
28368             text.unshift({
28369                 tag : 'i',
28370                 cls : 'fa ' + this.icon
28371             })
28372         }
28373         
28374         
28375         var cfg = {
28376             tag : 'div',
28377             cls : 'btn-group',
28378             cn : [
28379                 {
28380                     tag : 'button',
28381                     cls : 'dropdown-button btn btn-' + this.weight,
28382                     cn : text
28383                 },
28384                 {
28385                     tag : 'button',
28386                     cls : 'dropdown-toggle btn btn-' + this.weight,
28387                     cn : [
28388                         {
28389                             tag : 'span',
28390                             cls : 'caret'
28391                         }
28392                     ]
28393                 },
28394                 {
28395                     tag : 'ul',
28396                     cls : 'dropdown-menu'
28397                 }
28398             ]
28399             
28400         };
28401         
28402         if(this.pos == 'top'){
28403             cfg.cls += ' dropup';
28404         }
28405         
28406         if(this.isSubMenu){
28407             cfg = {
28408                 tag : 'ul',
28409                 cls : 'dropdown-menu'
28410             }
28411         }
28412         
28413         return cfg;
28414     },
28415     
28416     onRender : function(ct, position)
28417     {
28418         this.isSubMenu = ct.hasClass('dropdown-submenu');
28419         
28420         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28421     },
28422     
28423     initEvents : function() 
28424     {
28425         if(this.isSubMenu){
28426             return;
28427         }
28428         
28429         this.hidden = true;
28430         
28431         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28432         this.triggerEl.on('click', this.onTriggerPress, this);
28433         
28434         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28435         this.buttonEl.on('click', this.onClick, this);
28436         
28437     },
28438     
28439     list : function()
28440     {
28441         if(this.isSubMenu){
28442             return this.el;
28443         }
28444         
28445         return this.el.select('ul.dropdown-menu', true).first();
28446     },
28447     
28448     onClick : function(e)
28449     {
28450         this.fireEvent("click", this, e);
28451     },
28452     
28453     onTriggerPress  : function(e)
28454     {   
28455         if (this.isVisible()) {
28456             this.hide();
28457         } else {
28458             this.show();
28459         }
28460     },
28461     
28462     isVisible : function(){
28463         return !this.hidden;
28464     },
28465     
28466     show : function()
28467     {
28468         this.fireEvent("beforeshow", this);
28469         
28470         this.hidden = false;
28471         this.el.addClass('open');
28472         
28473         Roo.get(document).on("mouseup", this.onMouseUp, this);
28474         
28475         this.fireEvent("show", this);
28476         
28477         
28478     },
28479     
28480     hide : function()
28481     {
28482         this.fireEvent("beforehide", this);
28483         
28484         this.hidden = true;
28485         this.el.removeClass('open');
28486         
28487         Roo.get(document).un("mouseup", this.onMouseUp);
28488         
28489         this.fireEvent("hide", this);
28490     },
28491     
28492     onMouseUp : function()
28493     {
28494         this.hide();
28495     }
28496     
28497 });
28498
28499  
28500  /*
28501  * - LGPL
28502  *
28503  * menu item
28504  * 
28505  */
28506 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28507
28508 /**
28509  * @class Roo.bootstrap.menu.Item
28510  * @extends Roo.bootstrap.Component
28511  * Bootstrap MenuItem class
28512  * @cfg {Boolean} submenu (true | false) default false
28513  * @cfg {String} html text of the item
28514  * @cfg {String} href the link
28515  * @cfg {Boolean} disable (true | false) default false
28516  * @cfg {Boolean} preventDefault (true | false) default true
28517  * @cfg {String} icon Font awesome icon
28518  * @cfg {String} pos Submenu align to (left | right) default right 
28519  * 
28520  * 
28521  * @constructor
28522  * Create a new Item
28523  * @param {Object} config The config object
28524  */
28525
28526
28527 Roo.bootstrap.menu.Item = function(config){
28528     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28529     this.addEvents({
28530         /**
28531          * @event mouseover
28532          * Fires when the mouse is hovering over this menu
28533          * @param {Roo.bootstrap.menu.Item} this
28534          * @param {Roo.EventObject} e
28535          */
28536         mouseover : true,
28537         /**
28538          * @event mouseout
28539          * Fires when the mouse exits this menu
28540          * @param {Roo.bootstrap.menu.Item} this
28541          * @param {Roo.EventObject} e
28542          */
28543         mouseout : true,
28544         // raw events
28545         /**
28546          * @event click
28547          * The raw click event for the entire grid.
28548          * @param {Roo.EventObject} e
28549          */
28550         click : true
28551     });
28552 };
28553
28554 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28555     
28556     submenu : false,
28557     href : '',
28558     html : '',
28559     preventDefault: true,
28560     disable : false,
28561     icon : false,
28562     pos : 'right',
28563     
28564     getAutoCreate : function()
28565     {
28566         var text = [
28567             {
28568                 tag : 'span',
28569                 cls : 'roo-menu-item-text',
28570                 html : this.html
28571             }
28572         ];
28573         
28574         if(this.icon){
28575             text.unshift({
28576                 tag : 'i',
28577                 cls : 'fa ' + this.icon
28578             })
28579         }
28580         
28581         var cfg = {
28582             tag : 'li',
28583             cn : [
28584                 {
28585                     tag : 'a',
28586                     href : this.href || '#',
28587                     cn : text
28588                 }
28589             ]
28590         };
28591         
28592         if(this.disable){
28593             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28594         }
28595         
28596         if(this.submenu){
28597             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28598             
28599             if(this.pos == 'left'){
28600                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28601             }
28602         }
28603         
28604         return cfg;
28605     },
28606     
28607     initEvents : function() 
28608     {
28609         this.el.on('mouseover', this.onMouseOver, this);
28610         this.el.on('mouseout', this.onMouseOut, this);
28611         
28612         this.el.select('a', true).first().on('click', this.onClick, this);
28613         
28614     },
28615     
28616     onClick : function(e)
28617     {
28618         if(this.preventDefault){
28619             e.preventDefault();
28620         }
28621         
28622         this.fireEvent("click", this, e);
28623     },
28624     
28625     onMouseOver : function(e)
28626     {
28627         if(this.submenu && this.pos == 'left'){
28628             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28629         }
28630         
28631         this.fireEvent("mouseover", this, e);
28632     },
28633     
28634     onMouseOut : function(e)
28635     {
28636         this.fireEvent("mouseout", this, e);
28637     }
28638 });
28639
28640  
28641
28642  /*
28643  * - LGPL
28644  *
28645  * menu separator
28646  * 
28647  */
28648 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28649
28650 /**
28651  * @class Roo.bootstrap.menu.Separator
28652  * @extends Roo.bootstrap.Component
28653  * Bootstrap Separator class
28654  * 
28655  * @constructor
28656  * Create a new Separator
28657  * @param {Object} config The config object
28658  */
28659
28660
28661 Roo.bootstrap.menu.Separator = function(config){
28662     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28663 };
28664
28665 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28666     
28667     getAutoCreate : function(){
28668         var cfg = {
28669             tag : 'li',
28670             cls: 'divider'
28671         };
28672         
28673         return cfg;
28674     }
28675    
28676 });
28677
28678  
28679
28680  /*
28681  * - LGPL
28682  *
28683  * Tooltip
28684  * 
28685  */
28686
28687 /**
28688  * @class Roo.bootstrap.Tooltip
28689  * Bootstrap Tooltip class
28690  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28691  * to determine which dom element triggers the tooltip.
28692  * 
28693  * It needs to add support for additional attributes like tooltip-position
28694  * 
28695  * @constructor
28696  * Create a new Toolti
28697  * @param {Object} config The config object
28698  */
28699
28700 Roo.bootstrap.Tooltip = function(config){
28701     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28702     
28703     this.alignment = Roo.bootstrap.Tooltip.alignment;
28704     
28705     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28706         this.alignment = config.alignment;
28707     }
28708     
28709 };
28710
28711 Roo.apply(Roo.bootstrap.Tooltip, {
28712     /**
28713      * @function init initialize tooltip monitoring.
28714      * @static
28715      */
28716     currentEl : false,
28717     currentTip : false,
28718     currentRegion : false,
28719     
28720     //  init : delay?
28721     
28722     init : function()
28723     {
28724         Roo.get(document).on('mouseover', this.enter ,this);
28725         Roo.get(document).on('mouseout', this.leave, this);
28726          
28727         
28728         this.currentTip = new Roo.bootstrap.Tooltip();
28729     },
28730     
28731     enter : function(ev)
28732     {
28733         var dom = ev.getTarget();
28734         
28735         //Roo.log(['enter',dom]);
28736         var el = Roo.fly(dom);
28737         if (this.currentEl) {
28738             //Roo.log(dom);
28739             //Roo.log(this.currentEl);
28740             //Roo.log(this.currentEl.contains(dom));
28741             if (this.currentEl == el) {
28742                 return;
28743             }
28744             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28745                 return;
28746             }
28747
28748         }
28749         
28750         if (this.currentTip.el) {
28751             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28752         }    
28753         //Roo.log(ev);
28754         
28755         if(!el || el.dom == document){
28756             return;
28757         }
28758         
28759         var bindEl = el;
28760         
28761         // you can not look for children, as if el is the body.. then everythign is the child..
28762         if (!el.attr('tooltip')) { //
28763             if (!el.select("[tooltip]").elements.length) {
28764                 return;
28765             }
28766             // is the mouse over this child...?
28767             bindEl = el.select("[tooltip]").first();
28768             var xy = ev.getXY();
28769             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28770                 //Roo.log("not in region.");
28771                 return;
28772             }
28773             //Roo.log("child element over..");
28774             
28775         }
28776         this.currentEl = bindEl;
28777         this.currentTip.bind(bindEl);
28778         this.currentRegion = Roo.lib.Region.getRegion(dom);
28779         this.currentTip.enter();
28780         
28781     },
28782     leave : function(ev)
28783     {
28784         var dom = ev.getTarget();
28785         //Roo.log(['leave',dom]);
28786         if (!this.currentEl) {
28787             return;
28788         }
28789         
28790         
28791         if (dom != this.currentEl.dom) {
28792             return;
28793         }
28794         var xy = ev.getXY();
28795         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28796             return;
28797         }
28798         // only activate leave if mouse cursor is outside... bounding box..
28799         
28800         
28801         
28802         
28803         if (this.currentTip) {
28804             this.currentTip.leave();
28805         }
28806         //Roo.log('clear currentEl');
28807         this.currentEl = false;
28808         
28809         
28810     },
28811     alignment : {
28812         'left' : ['r-l', [-2,0], 'right'],
28813         'right' : ['l-r', [2,0], 'left'],
28814         'bottom' : ['t-b', [0,2], 'top'],
28815         'top' : [ 'b-t', [0,-2], 'bottom']
28816     }
28817     
28818 });
28819
28820
28821 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28822     
28823     
28824     bindEl : false,
28825     
28826     delay : null, // can be { show : 300 , hide: 500}
28827     
28828     timeout : null,
28829     
28830     hoverState : null, //???
28831     
28832     placement : 'bottom', 
28833     
28834     alignment : false,
28835     
28836     getAutoCreate : function(){
28837     
28838         var cfg = {
28839            cls : 'tooltip',   
28840            role : 'tooltip',
28841            cn : [
28842                 {
28843                     cls : 'tooltip-arrow arrow'
28844                 },
28845                 {
28846                     cls : 'tooltip-inner'
28847                 }
28848            ]
28849         };
28850         
28851         return cfg;
28852     },
28853     bind : function(el)
28854     {
28855         this.bindEl = el;
28856     },
28857     
28858     initEvents : function()
28859     {
28860         this.arrowEl = this.el.select('.arrow', true).first();
28861         this.innerEl = this.el.select('.tooltip-inner', true).first();
28862     },
28863     
28864     enter : function () {
28865        
28866         if (this.timeout != null) {
28867             clearTimeout(this.timeout);
28868         }
28869         
28870         this.hoverState = 'in';
28871          //Roo.log("enter - show");
28872         if (!this.delay || !this.delay.show) {
28873             this.show();
28874             return;
28875         }
28876         var _t = this;
28877         this.timeout = setTimeout(function () {
28878             if (_t.hoverState == 'in') {
28879                 _t.show();
28880             }
28881         }, this.delay.show);
28882     },
28883     leave : function()
28884     {
28885         clearTimeout(this.timeout);
28886     
28887         this.hoverState = 'out';
28888          if (!this.delay || !this.delay.hide) {
28889             this.hide();
28890             return;
28891         }
28892        
28893         var _t = this;
28894         this.timeout = setTimeout(function () {
28895             //Roo.log("leave - timeout");
28896             
28897             if (_t.hoverState == 'out') {
28898                 _t.hide();
28899                 Roo.bootstrap.Tooltip.currentEl = false;
28900             }
28901         }, delay);
28902     },
28903     
28904     show : function (msg)
28905     {
28906         if (!this.el) {
28907             this.render(document.body);
28908         }
28909         // set content.
28910         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28911         
28912         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28913         
28914         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28915         
28916         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28917                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28918         
28919         var placement = typeof this.placement == 'function' ?
28920             this.placement.call(this, this.el, on_el) :
28921             this.placement;
28922             
28923         var autoToken = /\s?auto?\s?/i;
28924         var autoPlace = autoToken.test(placement);
28925         if (autoPlace) {
28926             placement = placement.replace(autoToken, '') || 'top';
28927         }
28928         
28929         //this.el.detach()
28930         //this.el.setXY([0,0]);
28931         this.el.show();
28932         //this.el.dom.style.display='block';
28933         
28934         //this.el.appendTo(on_el);
28935         
28936         var p = this.getPosition();
28937         var box = this.el.getBox();
28938         
28939         if (autoPlace) {
28940             // fixme..
28941         }
28942         
28943         var align = this.alignment[placement];
28944         
28945         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28946         
28947         if(placement == 'top' || placement == 'bottom'){
28948             if(xy[0] < 0){
28949                 placement = 'right';
28950             }
28951             
28952             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28953                 placement = 'left';
28954             }
28955             
28956             var scroll = Roo.select('body', true).first().getScroll();
28957             
28958             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28959                 placement = 'top';
28960             }
28961             
28962             align = this.alignment[placement];
28963             
28964             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28965             
28966         }
28967         
28968         this.el.alignTo(this.bindEl, align[0],align[1]);
28969         //var arrow = this.el.select('.arrow',true).first();
28970         //arrow.set(align[2], 
28971         
28972         this.el.addClass(placement);
28973         this.el.addClass("bs-tooltip-"+ placement);
28974         
28975         this.el.addClass('in fade show');
28976         
28977         this.hoverState = null;
28978         
28979         if (this.el.hasClass('fade')) {
28980             // fade it?
28981         }
28982         
28983         
28984         
28985         
28986         
28987     },
28988     hide : function()
28989     {
28990          
28991         if (!this.el) {
28992             return;
28993         }
28994         //this.el.setXY([0,0]);
28995         this.el.removeClass(['show', 'in']);
28996         //this.el.hide();
28997         
28998     }
28999     
29000 });
29001  
29002
29003  /*
29004  * - LGPL
29005  *
29006  * Location Picker
29007  * 
29008  */
29009
29010 /**
29011  * @class Roo.bootstrap.LocationPicker
29012  * @extends Roo.bootstrap.Component
29013  * Bootstrap LocationPicker class
29014  * @cfg {Number} latitude Position when init default 0
29015  * @cfg {Number} longitude Position when init default 0
29016  * @cfg {Number} zoom default 15
29017  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29018  * @cfg {Boolean} mapTypeControl default false
29019  * @cfg {Boolean} disableDoubleClickZoom default false
29020  * @cfg {Boolean} scrollwheel default true
29021  * @cfg {Boolean} streetViewControl default false
29022  * @cfg {Number} radius default 0
29023  * @cfg {String} locationName
29024  * @cfg {Boolean} draggable default true
29025  * @cfg {Boolean} enableAutocomplete default false
29026  * @cfg {Boolean} enableReverseGeocode default true
29027  * @cfg {String} markerTitle
29028  * 
29029  * @constructor
29030  * Create a new LocationPicker
29031  * @param {Object} config The config object
29032  */
29033
29034
29035 Roo.bootstrap.LocationPicker = function(config){
29036     
29037     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29038     
29039     this.addEvents({
29040         /**
29041          * @event initial
29042          * Fires when the picker initialized.
29043          * @param {Roo.bootstrap.LocationPicker} this
29044          * @param {Google Location} location
29045          */
29046         initial : true,
29047         /**
29048          * @event positionchanged
29049          * Fires when the picker position changed.
29050          * @param {Roo.bootstrap.LocationPicker} this
29051          * @param {Google Location} location
29052          */
29053         positionchanged : true,
29054         /**
29055          * @event resize
29056          * Fires when the map resize.
29057          * @param {Roo.bootstrap.LocationPicker} this
29058          */
29059         resize : true,
29060         /**
29061          * @event show
29062          * Fires when the map show.
29063          * @param {Roo.bootstrap.LocationPicker} this
29064          */
29065         show : true,
29066         /**
29067          * @event hide
29068          * Fires when the map hide.
29069          * @param {Roo.bootstrap.LocationPicker} this
29070          */
29071         hide : true,
29072         /**
29073          * @event mapClick
29074          * Fires when click the map.
29075          * @param {Roo.bootstrap.LocationPicker} this
29076          * @param {Map event} e
29077          */
29078         mapClick : true,
29079         /**
29080          * @event mapRightClick
29081          * Fires when right click the map.
29082          * @param {Roo.bootstrap.LocationPicker} this
29083          * @param {Map event} e
29084          */
29085         mapRightClick : true,
29086         /**
29087          * @event markerClick
29088          * Fires when click the marker.
29089          * @param {Roo.bootstrap.LocationPicker} this
29090          * @param {Map event} e
29091          */
29092         markerClick : true,
29093         /**
29094          * @event markerRightClick
29095          * Fires when right click the marker.
29096          * @param {Roo.bootstrap.LocationPicker} this
29097          * @param {Map event} e
29098          */
29099         markerRightClick : true,
29100         /**
29101          * @event OverlayViewDraw
29102          * Fires when OverlayView Draw
29103          * @param {Roo.bootstrap.LocationPicker} this
29104          */
29105         OverlayViewDraw : true,
29106         /**
29107          * @event OverlayViewOnAdd
29108          * Fires when OverlayView Draw
29109          * @param {Roo.bootstrap.LocationPicker} this
29110          */
29111         OverlayViewOnAdd : true,
29112         /**
29113          * @event OverlayViewOnRemove
29114          * Fires when OverlayView Draw
29115          * @param {Roo.bootstrap.LocationPicker} this
29116          */
29117         OverlayViewOnRemove : true,
29118         /**
29119          * @event OverlayViewShow
29120          * Fires when OverlayView Draw
29121          * @param {Roo.bootstrap.LocationPicker} this
29122          * @param {Pixel} cpx
29123          */
29124         OverlayViewShow : true,
29125         /**
29126          * @event OverlayViewHide
29127          * Fires when OverlayView Draw
29128          * @param {Roo.bootstrap.LocationPicker} this
29129          */
29130         OverlayViewHide : true,
29131         /**
29132          * @event loadexception
29133          * Fires when load google lib failed.
29134          * @param {Roo.bootstrap.LocationPicker} this
29135          */
29136         loadexception : true
29137     });
29138         
29139 };
29140
29141 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29142     
29143     gMapContext: false,
29144     
29145     latitude: 0,
29146     longitude: 0,
29147     zoom: 15,
29148     mapTypeId: false,
29149     mapTypeControl: false,
29150     disableDoubleClickZoom: false,
29151     scrollwheel: true,
29152     streetViewControl: false,
29153     radius: 0,
29154     locationName: '',
29155     draggable: true,
29156     enableAutocomplete: false,
29157     enableReverseGeocode: true,
29158     markerTitle: '',
29159     
29160     getAutoCreate: function()
29161     {
29162
29163         var cfg = {
29164             tag: 'div',
29165             cls: 'roo-location-picker'
29166         };
29167         
29168         return cfg
29169     },
29170     
29171     initEvents: function(ct, position)
29172     {       
29173         if(!this.el.getWidth() || this.isApplied()){
29174             return;
29175         }
29176         
29177         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29178         
29179         this.initial();
29180     },
29181     
29182     initial: function()
29183     {
29184         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29185             this.fireEvent('loadexception', this);
29186             return;
29187         }
29188         
29189         if(!this.mapTypeId){
29190             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29191         }
29192         
29193         this.gMapContext = this.GMapContext();
29194         
29195         this.initOverlayView();
29196         
29197         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29198         
29199         var _this = this;
29200                 
29201         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29202             _this.setPosition(_this.gMapContext.marker.position);
29203         });
29204         
29205         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29206             _this.fireEvent('mapClick', this, event);
29207             
29208         });
29209
29210         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29211             _this.fireEvent('mapRightClick', this, event);
29212             
29213         });
29214         
29215         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29216             _this.fireEvent('markerClick', this, event);
29217             
29218         });
29219
29220         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29221             _this.fireEvent('markerRightClick', this, event);
29222             
29223         });
29224         
29225         this.setPosition(this.gMapContext.location);
29226         
29227         this.fireEvent('initial', this, this.gMapContext.location);
29228     },
29229     
29230     initOverlayView: function()
29231     {
29232         var _this = this;
29233         
29234         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29235             
29236             draw: function()
29237             {
29238                 _this.fireEvent('OverlayViewDraw', _this);
29239             },
29240             
29241             onAdd: function()
29242             {
29243                 _this.fireEvent('OverlayViewOnAdd', _this);
29244             },
29245             
29246             onRemove: function()
29247             {
29248                 _this.fireEvent('OverlayViewOnRemove', _this);
29249             },
29250             
29251             show: function(cpx)
29252             {
29253                 _this.fireEvent('OverlayViewShow', _this, cpx);
29254             },
29255             
29256             hide: function()
29257             {
29258                 _this.fireEvent('OverlayViewHide', _this);
29259             }
29260             
29261         });
29262     },
29263     
29264     fromLatLngToContainerPixel: function(event)
29265     {
29266         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29267     },
29268     
29269     isApplied: function() 
29270     {
29271         return this.getGmapContext() == false ? false : true;
29272     },
29273     
29274     getGmapContext: function() 
29275     {
29276         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29277     },
29278     
29279     GMapContext: function() 
29280     {
29281         var position = new google.maps.LatLng(this.latitude, this.longitude);
29282         
29283         var _map = new google.maps.Map(this.el.dom, {
29284             center: position,
29285             zoom: this.zoom,
29286             mapTypeId: this.mapTypeId,
29287             mapTypeControl: this.mapTypeControl,
29288             disableDoubleClickZoom: this.disableDoubleClickZoom,
29289             scrollwheel: this.scrollwheel,
29290             streetViewControl: this.streetViewControl,
29291             locationName: this.locationName,
29292             draggable: this.draggable,
29293             enableAutocomplete: this.enableAutocomplete,
29294             enableReverseGeocode: this.enableReverseGeocode
29295         });
29296         
29297         var _marker = new google.maps.Marker({
29298             position: position,
29299             map: _map,
29300             title: this.markerTitle,
29301             draggable: this.draggable
29302         });
29303         
29304         return {
29305             map: _map,
29306             marker: _marker,
29307             circle: null,
29308             location: position,
29309             radius: this.radius,
29310             locationName: this.locationName,
29311             addressComponents: {
29312                 formatted_address: null,
29313                 addressLine1: null,
29314                 addressLine2: null,
29315                 streetName: null,
29316                 streetNumber: null,
29317                 city: null,
29318                 district: null,
29319                 state: null,
29320                 stateOrProvince: null
29321             },
29322             settings: this,
29323             domContainer: this.el.dom,
29324             geodecoder: new google.maps.Geocoder()
29325         };
29326     },
29327     
29328     drawCircle: function(center, radius, options) 
29329     {
29330         if (this.gMapContext.circle != null) {
29331             this.gMapContext.circle.setMap(null);
29332         }
29333         if (radius > 0) {
29334             radius *= 1;
29335             options = Roo.apply({}, options, {
29336                 strokeColor: "#0000FF",
29337                 strokeOpacity: .35,
29338                 strokeWeight: 2,
29339                 fillColor: "#0000FF",
29340                 fillOpacity: .2
29341             });
29342             
29343             options.map = this.gMapContext.map;
29344             options.radius = radius;
29345             options.center = center;
29346             this.gMapContext.circle = new google.maps.Circle(options);
29347             return this.gMapContext.circle;
29348         }
29349         
29350         return null;
29351     },
29352     
29353     setPosition: function(location) 
29354     {
29355         this.gMapContext.location = location;
29356         this.gMapContext.marker.setPosition(location);
29357         this.gMapContext.map.panTo(location);
29358         this.drawCircle(location, this.gMapContext.radius, {});
29359         
29360         var _this = this;
29361         
29362         if (this.gMapContext.settings.enableReverseGeocode) {
29363             this.gMapContext.geodecoder.geocode({
29364                 latLng: this.gMapContext.location
29365             }, function(results, status) {
29366                 
29367                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29368                     _this.gMapContext.locationName = results[0].formatted_address;
29369                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29370                     
29371                     _this.fireEvent('positionchanged', this, location);
29372                 }
29373             });
29374             
29375             return;
29376         }
29377         
29378         this.fireEvent('positionchanged', this, location);
29379     },
29380     
29381     resize: function()
29382     {
29383         google.maps.event.trigger(this.gMapContext.map, "resize");
29384         
29385         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29386         
29387         this.fireEvent('resize', this);
29388     },
29389     
29390     setPositionByLatLng: function(latitude, longitude)
29391     {
29392         this.setPosition(new google.maps.LatLng(latitude, longitude));
29393     },
29394     
29395     getCurrentPosition: function() 
29396     {
29397         return {
29398             latitude: this.gMapContext.location.lat(),
29399             longitude: this.gMapContext.location.lng()
29400         };
29401     },
29402     
29403     getAddressName: function() 
29404     {
29405         return this.gMapContext.locationName;
29406     },
29407     
29408     getAddressComponents: function() 
29409     {
29410         return this.gMapContext.addressComponents;
29411     },
29412     
29413     address_component_from_google_geocode: function(address_components) 
29414     {
29415         var result = {};
29416         
29417         for (var i = 0; i < address_components.length; i++) {
29418             var component = address_components[i];
29419             if (component.types.indexOf("postal_code") >= 0) {
29420                 result.postalCode = component.short_name;
29421             } else if (component.types.indexOf("street_number") >= 0) {
29422                 result.streetNumber = component.short_name;
29423             } else if (component.types.indexOf("route") >= 0) {
29424                 result.streetName = component.short_name;
29425             } else if (component.types.indexOf("neighborhood") >= 0) {
29426                 result.city = component.short_name;
29427             } else if (component.types.indexOf("locality") >= 0) {
29428                 result.city = component.short_name;
29429             } else if (component.types.indexOf("sublocality") >= 0) {
29430                 result.district = component.short_name;
29431             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29432                 result.stateOrProvince = component.short_name;
29433             } else if (component.types.indexOf("country") >= 0) {
29434                 result.country = component.short_name;
29435             }
29436         }
29437         
29438         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29439         result.addressLine2 = "";
29440         return result;
29441     },
29442     
29443     setZoomLevel: function(zoom)
29444     {
29445         this.gMapContext.map.setZoom(zoom);
29446     },
29447     
29448     show: function()
29449     {
29450         if(!this.el){
29451             return;
29452         }
29453         
29454         this.el.show();
29455         
29456         this.resize();
29457         
29458         this.fireEvent('show', this);
29459     },
29460     
29461     hide: function()
29462     {
29463         if(!this.el){
29464             return;
29465         }
29466         
29467         this.el.hide();
29468         
29469         this.fireEvent('hide', this);
29470     }
29471     
29472 });
29473
29474 Roo.apply(Roo.bootstrap.LocationPicker, {
29475     
29476     OverlayView : function(map, options)
29477     {
29478         options = options || {};
29479         
29480         this.setMap(map);
29481     }
29482     
29483     
29484 });/**
29485  * @class Roo.bootstrap.Alert
29486  * @extends Roo.bootstrap.Component
29487  * Bootstrap Alert class - shows an alert area box
29488  * eg
29489  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29490   Enter a valid email address
29491 </div>
29492  * @licence LGPL
29493  * @cfg {String} title The title of alert
29494  * @cfg {String} html The content of alert
29495  * @cfg {String} weight (  success | info | warning | danger )
29496  * @cfg {String} faicon font-awesomeicon
29497  * 
29498  * @constructor
29499  * Create a new alert
29500  * @param {Object} config The config object
29501  */
29502
29503
29504 Roo.bootstrap.Alert = function(config){
29505     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29506     
29507 };
29508
29509 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29510     
29511     title: '',
29512     html: '',
29513     weight: false,
29514     faicon: false,
29515     
29516     getAutoCreate : function()
29517     {
29518         
29519         var cfg = {
29520             tag : 'div',
29521             cls : 'alert',
29522             cn : [
29523                 {
29524                     tag : 'i',
29525                     cls : 'roo-alert-icon'
29526                     
29527                 },
29528                 {
29529                     tag : 'b',
29530                     cls : 'roo-alert-title',
29531                     html : this.title
29532                 },
29533                 {
29534                     tag : 'span',
29535                     cls : 'roo-alert-text',
29536                     html : this.html
29537                 }
29538             ]
29539         };
29540         
29541         if(this.faicon){
29542             cfg.cn[0].cls += ' fa ' + this.faicon;
29543         }
29544         
29545         if(this.weight){
29546             cfg.cls += ' alert-' + this.weight;
29547         }
29548         
29549         return cfg;
29550     },
29551     
29552     initEvents: function() 
29553     {
29554         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29555     },
29556     
29557     setTitle : function(str)
29558     {
29559         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29560     },
29561     
29562     setText : function(str)
29563     {
29564         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29565     },
29566     
29567     setWeight : function(weight)
29568     {
29569         if(this.weight){
29570             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29571         }
29572         
29573         this.weight = weight;
29574         
29575         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29576     },
29577     
29578     setIcon : function(icon)
29579     {
29580         if(this.faicon){
29581             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29582         }
29583         
29584         this.faicon = icon;
29585         
29586         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29587     },
29588     
29589     hide: function() 
29590     {
29591         this.el.hide();   
29592     },
29593     
29594     show: function() 
29595     {  
29596         this.el.show();   
29597     }
29598     
29599 });
29600
29601  
29602 /*
29603 * Licence: LGPL
29604 */
29605
29606 /**
29607  * @class Roo.bootstrap.UploadCropbox
29608  * @extends Roo.bootstrap.Component
29609  * Bootstrap UploadCropbox class
29610  * @cfg {String} emptyText show when image has been loaded
29611  * @cfg {String} rotateNotify show when image too small to rotate
29612  * @cfg {Number} errorTimeout default 3000
29613  * @cfg {Number} minWidth default 300
29614  * @cfg {Number} minHeight default 300
29615  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29616  * @cfg {Boolean} isDocument (true|false) default false
29617  * @cfg {String} url action url
29618  * @cfg {String} paramName default 'imageUpload'
29619  * @cfg {String} method default POST
29620  * @cfg {Boolean} loadMask (true|false) default true
29621  * @cfg {Boolean} loadingText default 'Loading...'
29622  * 
29623  * @constructor
29624  * Create a new UploadCropbox
29625  * @param {Object} config The config object
29626  */
29627
29628 Roo.bootstrap.UploadCropbox = function(config){
29629     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29630     
29631     this.addEvents({
29632         /**
29633          * @event beforeselectfile
29634          * Fire before select file
29635          * @param {Roo.bootstrap.UploadCropbox} this
29636          */
29637         "beforeselectfile" : true,
29638         /**
29639          * @event initial
29640          * Fire after initEvent
29641          * @param {Roo.bootstrap.UploadCropbox} this
29642          */
29643         "initial" : true,
29644         /**
29645          * @event crop
29646          * Fire after initEvent
29647          * @param {Roo.bootstrap.UploadCropbox} this
29648          * @param {String} data
29649          */
29650         "crop" : true,
29651         /**
29652          * @event prepare
29653          * Fire when preparing the file data
29654          * @param {Roo.bootstrap.UploadCropbox} this
29655          * @param {Object} file
29656          */
29657         "prepare" : true,
29658         /**
29659          * @event exception
29660          * Fire when get exception
29661          * @param {Roo.bootstrap.UploadCropbox} this
29662          * @param {XMLHttpRequest} xhr
29663          */
29664         "exception" : true,
29665         /**
29666          * @event beforeloadcanvas
29667          * Fire before load the canvas
29668          * @param {Roo.bootstrap.UploadCropbox} this
29669          * @param {String} src
29670          */
29671         "beforeloadcanvas" : true,
29672         /**
29673          * @event trash
29674          * Fire when trash image
29675          * @param {Roo.bootstrap.UploadCropbox} this
29676          */
29677         "trash" : true,
29678         /**
29679          * @event download
29680          * Fire when download the image
29681          * @param {Roo.bootstrap.UploadCropbox} this
29682          */
29683         "download" : true,
29684         /**
29685          * @event footerbuttonclick
29686          * Fire when footerbuttonclick
29687          * @param {Roo.bootstrap.UploadCropbox} this
29688          * @param {String} type
29689          */
29690         "footerbuttonclick" : true,
29691         /**
29692          * @event resize
29693          * Fire when resize
29694          * @param {Roo.bootstrap.UploadCropbox} this
29695          */
29696         "resize" : true,
29697         /**
29698          * @event rotate
29699          * Fire when rotate the image
29700          * @param {Roo.bootstrap.UploadCropbox} this
29701          * @param {String} pos
29702          */
29703         "rotate" : true,
29704         /**
29705          * @event inspect
29706          * Fire when inspect the file
29707          * @param {Roo.bootstrap.UploadCropbox} this
29708          * @param {Object} file
29709          */
29710         "inspect" : true,
29711         /**
29712          * @event upload
29713          * Fire when xhr upload the file
29714          * @param {Roo.bootstrap.UploadCropbox} this
29715          * @param {Object} data
29716          */
29717         "upload" : true,
29718         /**
29719          * @event arrange
29720          * Fire when arrange the file data
29721          * @param {Roo.bootstrap.UploadCropbox} this
29722          * @param {Object} formData
29723          */
29724         "arrange" : true
29725     });
29726     
29727     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29728 };
29729
29730 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29731     
29732     emptyText : 'Click to upload image',
29733     rotateNotify : 'Image is too small to rotate',
29734     errorTimeout : 3000,
29735     scale : 0,
29736     baseScale : 1,
29737     rotate : 0,
29738     dragable : false,
29739     pinching : false,
29740     mouseX : 0,
29741     mouseY : 0,
29742     cropData : false,
29743     minWidth : 300,
29744     minHeight : 300,
29745     file : false,
29746     exif : {},
29747     baseRotate : 1,
29748     cropType : 'image/jpeg',
29749     buttons : false,
29750     canvasLoaded : false,
29751     isDocument : false,
29752     method : 'POST',
29753     paramName : 'imageUpload',
29754     loadMask : true,
29755     loadingText : 'Loading...',
29756     maskEl : false,
29757     
29758     getAutoCreate : function()
29759     {
29760         var cfg = {
29761             tag : 'div',
29762             cls : 'roo-upload-cropbox',
29763             cn : [
29764                 {
29765                     tag : 'input',
29766                     cls : 'roo-upload-cropbox-selector',
29767                     type : 'file'
29768                 },
29769                 {
29770                     tag : 'div',
29771                     cls : 'roo-upload-cropbox-body',
29772                     style : 'cursor:pointer',
29773                     cn : [
29774                         {
29775                             tag : 'div',
29776                             cls : 'roo-upload-cropbox-preview'
29777                         },
29778                         {
29779                             tag : 'div',
29780                             cls : 'roo-upload-cropbox-thumb'
29781                         },
29782                         {
29783                             tag : 'div',
29784                             cls : 'roo-upload-cropbox-empty-notify',
29785                             html : this.emptyText
29786                         },
29787                         {
29788                             tag : 'div',
29789                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29790                             html : this.rotateNotify
29791                         }
29792                     ]
29793                 },
29794                 {
29795                     tag : 'div',
29796                     cls : 'roo-upload-cropbox-footer',
29797                     cn : {
29798                         tag : 'div',
29799                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29800                         cn : []
29801                     }
29802                 }
29803             ]
29804         };
29805         
29806         return cfg;
29807     },
29808     
29809     onRender : function(ct, position)
29810     {
29811         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29812         
29813         if (this.buttons.length) {
29814             
29815             Roo.each(this.buttons, function(bb) {
29816                 
29817                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29818                 
29819                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29820                 
29821             }, this);
29822         }
29823         
29824         if(this.loadMask){
29825             this.maskEl = this.el;
29826         }
29827     },
29828     
29829     initEvents : function()
29830     {
29831         this.urlAPI = (window.createObjectURL && window) || 
29832                                 (window.URL && URL.revokeObjectURL && URL) || 
29833                                 (window.webkitURL && webkitURL);
29834                         
29835         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29836         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29837         
29838         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29839         this.selectorEl.hide();
29840         
29841         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29842         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29843         
29844         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29845         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29846         this.thumbEl.hide();
29847         
29848         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29849         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29850         
29851         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29852         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29853         this.errorEl.hide();
29854         
29855         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29856         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29857         this.footerEl.hide();
29858         
29859         this.setThumbBoxSize();
29860         
29861         this.bind();
29862         
29863         this.resize();
29864         
29865         this.fireEvent('initial', this);
29866     },
29867
29868     bind : function()
29869     {
29870         var _this = this;
29871         
29872         window.addEventListener("resize", function() { _this.resize(); } );
29873         
29874         this.bodyEl.on('click', this.beforeSelectFile, this);
29875         
29876         if(Roo.isTouch){
29877             this.bodyEl.on('touchstart', this.onTouchStart, this);
29878             this.bodyEl.on('touchmove', this.onTouchMove, this);
29879             this.bodyEl.on('touchend', this.onTouchEnd, this);
29880         }
29881         
29882         if(!Roo.isTouch){
29883             this.bodyEl.on('mousedown', this.onMouseDown, this);
29884             this.bodyEl.on('mousemove', this.onMouseMove, this);
29885             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29886             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29887             Roo.get(document).on('mouseup', this.onMouseUp, this);
29888         }
29889         
29890         this.selectorEl.on('change', this.onFileSelected, this);
29891     },
29892     
29893     reset : function()
29894     {    
29895         this.scale = 0;
29896         this.baseScale = 1;
29897         this.rotate = 0;
29898         this.baseRotate = 1;
29899         this.dragable = false;
29900         this.pinching = false;
29901         this.mouseX = 0;
29902         this.mouseY = 0;
29903         this.cropData = false;
29904         this.notifyEl.dom.innerHTML = this.emptyText;
29905         
29906         this.selectorEl.dom.value = '';
29907         
29908     },
29909     
29910     resize : function()
29911     {
29912         if(this.fireEvent('resize', this) != false){
29913             this.setThumbBoxPosition();
29914             this.setCanvasPosition();
29915         }
29916     },
29917     
29918     onFooterButtonClick : function(e, el, o, type)
29919     {
29920         switch (type) {
29921             case 'rotate-left' :
29922                 this.onRotateLeft(e);
29923                 break;
29924             case 'rotate-right' :
29925                 this.onRotateRight(e);
29926                 break;
29927             case 'picture' :
29928                 this.beforeSelectFile(e);
29929                 break;
29930             case 'trash' :
29931                 this.trash(e);
29932                 break;
29933             case 'crop' :
29934                 this.crop(e);
29935                 break;
29936             case 'download' :
29937                 this.download(e);
29938                 break;
29939             default :
29940                 break;
29941         }
29942         
29943         this.fireEvent('footerbuttonclick', this, type);
29944     },
29945     
29946     beforeSelectFile : function(e)
29947     {
29948         e.preventDefault();
29949         
29950         if(this.fireEvent('beforeselectfile', this) != false){
29951             this.selectorEl.dom.click();
29952         }
29953     },
29954     
29955     onFileSelected : function(e)
29956     {
29957         e.preventDefault();
29958         
29959         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29960             return;
29961         }
29962         
29963         var file = this.selectorEl.dom.files[0];
29964         
29965         if(this.fireEvent('inspect', this, file) != false){
29966             this.prepare(file);
29967         }
29968         
29969     },
29970     
29971     trash : function(e)
29972     {
29973         this.fireEvent('trash', this);
29974     },
29975     
29976     download : function(e)
29977     {
29978         this.fireEvent('download', this);
29979     },
29980     
29981     loadCanvas : function(src)
29982     {   
29983         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29984             
29985             this.reset();
29986             
29987             this.imageEl = document.createElement('img');
29988             
29989             var _this = this;
29990             
29991             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29992             
29993             this.imageEl.src = src;
29994         }
29995     },
29996     
29997     onLoadCanvas : function()
29998     {   
29999         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30000         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30001         
30002         this.bodyEl.un('click', this.beforeSelectFile, this);
30003         
30004         this.notifyEl.hide();
30005         this.thumbEl.show();
30006         this.footerEl.show();
30007         
30008         this.baseRotateLevel();
30009         
30010         if(this.isDocument){
30011             this.setThumbBoxSize();
30012         }
30013         
30014         this.setThumbBoxPosition();
30015         
30016         this.baseScaleLevel();
30017         
30018         this.draw();
30019         
30020         this.resize();
30021         
30022         this.canvasLoaded = true;
30023         
30024         if(this.loadMask){
30025             this.maskEl.unmask();
30026         }
30027         
30028     },
30029     
30030     setCanvasPosition : function()
30031     {   
30032         if(!this.canvasEl){
30033             return;
30034         }
30035         
30036         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30037         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30038         
30039         this.previewEl.setLeft(pw);
30040         this.previewEl.setTop(ph);
30041         
30042     },
30043     
30044     onMouseDown : function(e)
30045     {   
30046         e.stopEvent();
30047         
30048         this.dragable = true;
30049         this.pinching = false;
30050         
30051         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30052             this.dragable = false;
30053             return;
30054         }
30055         
30056         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30057         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30058         
30059     },
30060     
30061     onMouseMove : function(e)
30062     {   
30063         e.stopEvent();
30064         
30065         if(!this.canvasLoaded){
30066             return;
30067         }
30068         
30069         if (!this.dragable){
30070             return;
30071         }
30072         
30073         var minX = Math.ceil(this.thumbEl.getLeft(true));
30074         var minY = Math.ceil(this.thumbEl.getTop(true));
30075         
30076         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30077         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30078         
30079         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30080         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30081         
30082         x = x - this.mouseX;
30083         y = y - this.mouseY;
30084         
30085         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30086         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30087         
30088         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30089         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30090         
30091         this.previewEl.setLeft(bgX);
30092         this.previewEl.setTop(bgY);
30093         
30094         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30095         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30096     },
30097     
30098     onMouseUp : function(e)
30099     {   
30100         e.stopEvent();
30101         
30102         this.dragable = false;
30103     },
30104     
30105     onMouseWheel : function(e)
30106     {   
30107         e.stopEvent();
30108         
30109         this.startScale = this.scale;
30110         
30111         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30112         
30113         if(!this.zoomable()){
30114             this.scale = this.startScale;
30115             return;
30116         }
30117         
30118         this.draw();
30119         
30120         return;
30121     },
30122     
30123     zoomable : function()
30124     {
30125         var minScale = this.thumbEl.getWidth() / this.minWidth;
30126         
30127         if(this.minWidth < this.minHeight){
30128             minScale = this.thumbEl.getHeight() / this.minHeight;
30129         }
30130         
30131         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30132         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30133         
30134         if(
30135                 this.isDocument &&
30136                 (this.rotate == 0 || this.rotate == 180) && 
30137                 (
30138                     width > this.imageEl.OriginWidth || 
30139                     height > this.imageEl.OriginHeight ||
30140                     (width < this.minWidth && height < this.minHeight)
30141                 )
30142         ){
30143             return false;
30144         }
30145         
30146         if(
30147                 this.isDocument &&
30148                 (this.rotate == 90 || this.rotate == 270) && 
30149                 (
30150                     width > this.imageEl.OriginWidth || 
30151                     height > this.imageEl.OriginHeight ||
30152                     (width < this.minHeight && height < this.minWidth)
30153                 )
30154         ){
30155             return false;
30156         }
30157         
30158         if(
30159                 !this.isDocument &&
30160                 (this.rotate == 0 || this.rotate == 180) && 
30161                 (
30162                     width < this.minWidth || 
30163                     width > this.imageEl.OriginWidth || 
30164                     height < this.minHeight || 
30165                     height > this.imageEl.OriginHeight
30166                 )
30167         ){
30168             return false;
30169         }
30170         
30171         if(
30172                 !this.isDocument &&
30173                 (this.rotate == 90 || this.rotate == 270) && 
30174                 (
30175                     width < this.minHeight || 
30176                     width > this.imageEl.OriginWidth || 
30177                     height < this.minWidth || 
30178                     height > this.imageEl.OriginHeight
30179                 )
30180         ){
30181             return false;
30182         }
30183         
30184         return true;
30185         
30186     },
30187     
30188     onRotateLeft : function(e)
30189     {   
30190         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30191             
30192             var minScale = this.thumbEl.getWidth() / this.minWidth;
30193             
30194             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30195             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30196             
30197             this.startScale = this.scale;
30198             
30199             while (this.getScaleLevel() < minScale){
30200             
30201                 this.scale = this.scale + 1;
30202                 
30203                 if(!this.zoomable()){
30204                     break;
30205                 }
30206                 
30207                 if(
30208                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30209                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30210                 ){
30211                     continue;
30212                 }
30213                 
30214                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30215
30216                 this.draw();
30217                 
30218                 return;
30219             }
30220             
30221             this.scale = this.startScale;
30222             
30223             this.onRotateFail();
30224             
30225             return false;
30226         }
30227         
30228         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30229
30230         if(this.isDocument){
30231             this.setThumbBoxSize();
30232             this.setThumbBoxPosition();
30233             this.setCanvasPosition();
30234         }
30235         
30236         this.draw();
30237         
30238         this.fireEvent('rotate', this, 'left');
30239         
30240     },
30241     
30242     onRotateRight : function(e)
30243     {
30244         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30245             
30246             var minScale = this.thumbEl.getWidth() / this.minWidth;
30247         
30248             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30249             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30250             
30251             this.startScale = this.scale;
30252             
30253             while (this.getScaleLevel() < minScale){
30254             
30255                 this.scale = this.scale + 1;
30256                 
30257                 if(!this.zoomable()){
30258                     break;
30259                 }
30260                 
30261                 if(
30262                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30263                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30264                 ){
30265                     continue;
30266                 }
30267                 
30268                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30269
30270                 this.draw();
30271                 
30272                 return;
30273             }
30274             
30275             this.scale = this.startScale;
30276             
30277             this.onRotateFail();
30278             
30279             return false;
30280         }
30281         
30282         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30283
30284         if(this.isDocument){
30285             this.setThumbBoxSize();
30286             this.setThumbBoxPosition();
30287             this.setCanvasPosition();
30288         }
30289         
30290         this.draw();
30291         
30292         this.fireEvent('rotate', this, 'right');
30293     },
30294     
30295     onRotateFail : function()
30296     {
30297         this.errorEl.show(true);
30298         
30299         var _this = this;
30300         
30301         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30302     },
30303     
30304     draw : function()
30305     {
30306         this.previewEl.dom.innerHTML = '';
30307         
30308         var canvasEl = document.createElement("canvas");
30309         
30310         var contextEl = canvasEl.getContext("2d");
30311         
30312         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30313         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30314         var center = this.imageEl.OriginWidth / 2;
30315         
30316         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30317             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30318             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30319             center = this.imageEl.OriginHeight / 2;
30320         }
30321         
30322         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30323         
30324         contextEl.translate(center, center);
30325         contextEl.rotate(this.rotate * Math.PI / 180);
30326
30327         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30328         
30329         this.canvasEl = document.createElement("canvas");
30330         
30331         this.contextEl = this.canvasEl.getContext("2d");
30332         
30333         switch (this.rotate) {
30334             case 0 :
30335                 
30336                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30337                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30338                 
30339                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30340                 
30341                 break;
30342             case 90 : 
30343                 
30344                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30345                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30346                 
30347                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30348                     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);
30349                     break;
30350                 }
30351                 
30352                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30353                 
30354                 break;
30355             case 180 :
30356                 
30357                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30358                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30359                 
30360                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30361                     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);
30362                     break;
30363                 }
30364                 
30365                 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);
30366                 
30367                 break;
30368             case 270 :
30369                 
30370                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30371                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30372         
30373                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30374                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30375                     break;
30376                 }
30377                 
30378                 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);
30379                 
30380                 break;
30381             default : 
30382                 break;
30383         }
30384         
30385         this.previewEl.appendChild(this.canvasEl);
30386         
30387         this.setCanvasPosition();
30388     },
30389     
30390     crop : function()
30391     {
30392         if(!this.canvasLoaded){
30393             return;
30394         }
30395         
30396         var imageCanvas = document.createElement("canvas");
30397         
30398         var imageContext = imageCanvas.getContext("2d");
30399         
30400         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30401         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30402         
30403         var center = imageCanvas.width / 2;
30404         
30405         imageContext.translate(center, center);
30406         
30407         imageContext.rotate(this.rotate * Math.PI / 180);
30408         
30409         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30410         
30411         var canvas = document.createElement("canvas");
30412         
30413         var context = canvas.getContext("2d");
30414                 
30415         canvas.width = this.minWidth;
30416         canvas.height = this.minHeight;
30417
30418         switch (this.rotate) {
30419             case 0 :
30420                 
30421                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30422                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30423                 
30424                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30425                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30426                 
30427                 var targetWidth = this.minWidth - 2 * x;
30428                 var targetHeight = this.minHeight - 2 * y;
30429                 
30430                 var scale = 1;
30431                 
30432                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30433                     scale = targetWidth / width;
30434                 }
30435                 
30436                 if(x > 0 && y == 0){
30437                     scale = targetHeight / height;
30438                 }
30439                 
30440                 if(x > 0 && y > 0){
30441                     scale = targetWidth / width;
30442                     
30443                     if(width < height){
30444                         scale = targetHeight / height;
30445                     }
30446                 }
30447                 
30448                 context.scale(scale, scale);
30449                 
30450                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30451                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30452
30453                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30454                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30455
30456                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30457                 
30458                 break;
30459             case 90 : 
30460                 
30461                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30462                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30463                 
30464                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30465                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30466                 
30467                 var targetWidth = this.minWidth - 2 * x;
30468                 var targetHeight = this.minHeight - 2 * y;
30469                 
30470                 var scale = 1;
30471                 
30472                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30473                     scale = targetWidth / width;
30474                 }
30475                 
30476                 if(x > 0 && y == 0){
30477                     scale = targetHeight / height;
30478                 }
30479                 
30480                 if(x > 0 && y > 0){
30481                     scale = targetWidth / width;
30482                     
30483                     if(width < height){
30484                         scale = targetHeight / height;
30485                     }
30486                 }
30487                 
30488                 context.scale(scale, scale);
30489                 
30490                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30491                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30492
30493                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30494                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30495                 
30496                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30497                 
30498                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30499                 
30500                 break;
30501             case 180 :
30502                 
30503                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30504                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30505                 
30506                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30507                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30508                 
30509                 var targetWidth = this.minWidth - 2 * x;
30510                 var targetHeight = this.minHeight - 2 * y;
30511                 
30512                 var scale = 1;
30513                 
30514                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30515                     scale = targetWidth / width;
30516                 }
30517                 
30518                 if(x > 0 && y == 0){
30519                     scale = targetHeight / height;
30520                 }
30521                 
30522                 if(x > 0 && y > 0){
30523                     scale = targetWidth / width;
30524                     
30525                     if(width < height){
30526                         scale = targetHeight / height;
30527                     }
30528                 }
30529                 
30530                 context.scale(scale, scale);
30531                 
30532                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30533                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30534
30535                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30536                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30537
30538                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30539                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30540                 
30541                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30542                 
30543                 break;
30544             case 270 :
30545                 
30546                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30547                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30548                 
30549                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30550                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30551                 
30552                 var targetWidth = this.minWidth - 2 * x;
30553                 var targetHeight = this.minHeight - 2 * y;
30554                 
30555                 var scale = 1;
30556                 
30557                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30558                     scale = targetWidth / width;
30559                 }
30560                 
30561                 if(x > 0 && y == 0){
30562                     scale = targetHeight / height;
30563                 }
30564                 
30565                 if(x > 0 && y > 0){
30566                     scale = targetWidth / width;
30567                     
30568                     if(width < height){
30569                         scale = targetHeight / height;
30570                     }
30571                 }
30572                 
30573                 context.scale(scale, scale);
30574                 
30575                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30576                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30577
30578                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30579                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30580                 
30581                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30582                 
30583                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30584                 
30585                 break;
30586             default : 
30587                 break;
30588         }
30589         
30590         this.cropData = canvas.toDataURL(this.cropType);
30591         
30592         if(this.fireEvent('crop', this, this.cropData) !== false){
30593             this.process(this.file, this.cropData);
30594         }
30595         
30596         return;
30597         
30598     },
30599     
30600     setThumbBoxSize : function()
30601     {
30602         var width, height;
30603         
30604         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30605             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30606             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30607             
30608             this.minWidth = width;
30609             this.minHeight = height;
30610             
30611             if(this.rotate == 90 || this.rotate == 270){
30612                 this.minWidth = height;
30613                 this.minHeight = width;
30614             }
30615         }
30616         
30617         height = 300;
30618         width = Math.ceil(this.minWidth * height / this.minHeight);
30619         
30620         if(this.minWidth > this.minHeight){
30621             width = 300;
30622             height = Math.ceil(this.minHeight * width / this.minWidth);
30623         }
30624         
30625         this.thumbEl.setStyle({
30626             width : width + 'px',
30627             height : height + 'px'
30628         });
30629
30630         return;
30631             
30632     },
30633     
30634     setThumbBoxPosition : function()
30635     {
30636         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30637         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30638         
30639         this.thumbEl.setLeft(x);
30640         this.thumbEl.setTop(y);
30641         
30642     },
30643     
30644     baseRotateLevel : function()
30645     {
30646         this.baseRotate = 1;
30647         
30648         if(
30649                 typeof(this.exif) != 'undefined' &&
30650                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30651                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30652         ){
30653             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30654         }
30655         
30656         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30657         
30658     },
30659     
30660     baseScaleLevel : function()
30661     {
30662         var width, height;
30663         
30664         if(this.isDocument){
30665             
30666             if(this.baseRotate == 6 || this.baseRotate == 8){
30667             
30668                 height = this.thumbEl.getHeight();
30669                 this.baseScale = height / this.imageEl.OriginWidth;
30670
30671                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30672                     width = this.thumbEl.getWidth();
30673                     this.baseScale = width / this.imageEl.OriginHeight;
30674                 }
30675
30676                 return;
30677             }
30678
30679             height = this.thumbEl.getHeight();
30680             this.baseScale = height / this.imageEl.OriginHeight;
30681
30682             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30683                 width = this.thumbEl.getWidth();
30684                 this.baseScale = width / this.imageEl.OriginWidth;
30685             }
30686
30687             return;
30688         }
30689         
30690         if(this.baseRotate == 6 || this.baseRotate == 8){
30691             
30692             width = this.thumbEl.getHeight();
30693             this.baseScale = width / this.imageEl.OriginHeight;
30694             
30695             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30696                 height = this.thumbEl.getWidth();
30697                 this.baseScale = height / this.imageEl.OriginHeight;
30698             }
30699             
30700             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30701                 height = this.thumbEl.getWidth();
30702                 this.baseScale = height / this.imageEl.OriginHeight;
30703                 
30704                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30705                     width = this.thumbEl.getHeight();
30706                     this.baseScale = width / this.imageEl.OriginWidth;
30707                 }
30708             }
30709             
30710             return;
30711         }
30712         
30713         width = this.thumbEl.getWidth();
30714         this.baseScale = width / this.imageEl.OriginWidth;
30715         
30716         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30717             height = this.thumbEl.getHeight();
30718             this.baseScale = height / this.imageEl.OriginHeight;
30719         }
30720         
30721         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30722             
30723             height = this.thumbEl.getHeight();
30724             this.baseScale = height / this.imageEl.OriginHeight;
30725             
30726             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30727                 width = this.thumbEl.getWidth();
30728                 this.baseScale = width / this.imageEl.OriginWidth;
30729             }
30730             
30731         }
30732         
30733         return;
30734     },
30735     
30736     getScaleLevel : function()
30737     {
30738         return this.baseScale * Math.pow(1.1, this.scale);
30739     },
30740     
30741     onTouchStart : function(e)
30742     {
30743         if(!this.canvasLoaded){
30744             this.beforeSelectFile(e);
30745             return;
30746         }
30747         
30748         var touches = e.browserEvent.touches;
30749         
30750         if(!touches){
30751             return;
30752         }
30753         
30754         if(touches.length == 1){
30755             this.onMouseDown(e);
30756             return;
30757         }
30758         
30759         if(touches.length != 2){
30760             return;
30761         }
30762         
30763         var coords = [];
30764         
30765         for(var i = 0, finger; finger = touches[i]; i++){
30766             coords.push(finger.pageX, finger.pageY);
30767         }
30768         
30769         var x = Math.pow(coords[0] - coords[2], 2);
30770         var y = Math.pow(coords[1] - coords[3], 2);
30771         
30772         this.startDistance = Math.sqrt(x + y);
30773         
30774         this.startScale = this.scale;
30775         
30776         this.pinching = true;
30777         this.dragable = false;
30778         
30779     },
30780     
30781     onTouchMove : function(e)
30782     {
30783         if(!this.pinching && !this.dragable){
30784             return;
30785         }
30786         
30787         var touches = e.browserEvent.touches;
30788         
30789         if(!touches){
30790             return;
30791         }
30792         
30793         if(this.dragable){
30794             this.onMouseMove(e);
30795             return;
30796         }
30797         
30798         var coords = [];
30799         
30800         for(var i = 0, finger; finger = touches[i]; i++){
30801             coords.push(finger.pageX, finger.pageY);
30802         }
30803         
30804         var x = Math.pow(coords[0] - coords[2], 2);
30805         var y = Math.pow(coords[1] - coords[3], 2);
30806         
30807         this.endDistance = Math.sqrt(x + y);
30808         
30809         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30810         
30811         if(!this.zoomable()){
30812             this.scale = this.startScale;
30813             return;
30814         }
30815         
30816         this.draw();
30817         
30818     },
30819     
30820     onTouchEnd : function(e)
30821     {
30822         this.pinching = false;
30823         this.dragable = false;
30824         
30825     },
30826     
30827     process : function(file, crop)
30828     {
30829         if(this.loadMask){
30830             this.maskEl.mask(this.loadingText);
30831         }
30832         
30833         this.xhr = new XMLHttpRequest();
30834         
30835         file.xhr = this.xhr;
30836
30837         this.xhr.open(this.method, this.url, true);
30838         
30839         var headers = {
30840             "Accept": "application/json",
30841             "Cache-Control": "no-cache",
30842             "X-Requested-With": "XMLHttpRequest"
30843         };
30844         
30845         for (var headerName in headers) {
30846             var headerValue = headers[headerName];
30847             if (headerValue) {
30848                 this.xhr.setRequestHeader(headerName, headerValue);
30849             }
30850         }
30851         
30852         var _this = this;
30853         
30854         this.xhr.onload = function()
30855         {
30856             _this.xhrOnLoad(_this.xhr);
30857         }
30858         
30859         this.xhr.onerror = function()
30860         {
30861             _this.xhrOnError(_this.xhr);
30862         }
30863         
30864         var formData = new FormData();
30865
30866         formData.append('returnHTML', 'NO');
30867         
30868         if(crop){
30869             formData.append('crop', crop);
30870         }
30871         
30872         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30873             formData.append(this.paramName, file, file.name);
30874         }
30875         
30876         if(typeof(file.filename) != 'undefined'){
30877             formData.append('filename', file.filename);
30878         }
30879         
30880         if(typeof(file.mimetype) != 'undefined'){
30881             formData.append('mimetype', file.mimetype);
30882         }
30883         
30884         if(this.fireEvent('arrange', this, formData) != false){
30885             this.xhr.send(formData);
30886         };
30887     },
30888     
30889     xhrOnLoad : function(xhr)
30890     {
30891         if(this.loadMask){
30892             this.maskEl.unmask();
30893         }
30894         
30895         if (xhr.readyState !== 4) {
30896             this.fireEvent('exception', this, xhr);
30897             return;
30898         }
30899
30900         var response = Roo.decode(xhr.responseText);
30901         
30902         if(!response.success){
30903             this.fireEvent('exception', this, xhr);
30904             return;
30905         }
30906         
30907         var response = Roo.decode(xhr.responseText);
30908         
30909         this.fireEvent('upload', this, response);
30910         
30911     },
30912     
30913     xhrOnError : function()
30914     {
30915         if(this.loadMask){
30916             this.maskEl.unmask();
30917         }
30918         
30919         Roo.log('xhr on error');
30920         
30921         var response = Roo.decode(xhr.responseText);
30922           
30923         Roo.log(response);
30924         
30925     },
30926     
30927     prepare : function(file)
30928     {   
30929         if(this.loadMask){
30930             this.maskEl.mask(this.loadingText);
30931         }
30932         
30933         this.file = false;
30934         this.exif = {};
30935         
30936         if(typeof(file) === 'string'){
30937             this.loadCanvas(file);
30938             return;
30939         }
30940         
30941         if(!file || !this.urlAPI){
30942             return;
30943         }
30944         
30945         this.file = file;
30946         this.cropType = file.type;
30947         
30948         var _this = this;
30949         
30950         if(this.fireEvent('prepare', this, this.file) != false){
30951             
30952             var reader = new FileReader();
30953             
30954             reader.onload = function (e) {
30955                 if (e.target.error) {
30956                     Roo.log(e.target.error);
30957                     return;
30958                 }
30959                 
30960                 var buffer = e.target.result,
30961                     dataView = new DataView(buffer),
30962                     offset = 2,
30963                     maxOffset = dataView.byteLength - 4,
30964                     markerBytes,
30965                     markerLength;
30966                 
30967                 if (dataView.getUint16(0) === 0xffd8) {
30968                     while (offset < maxOffset) {
30969                         markerBytes = dataView.getUint16(offset);
30970                         
30971                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30972                             markerLength = dataView.getUint16(offset + 2) + 2;
30973                             if (offset + markerLength > dataView.byteLength) {
30974                                 Roo.log('Invalid meta data: Invalid segment size.');
30975                                 break;
30976                             }
30977                             
30978                             if(markerBytes == 0xffe1){
30979                                 _this.parseExifData(
30980                                     dataView,
30981                                     offset,
30982                                     markerLength
30983                                 );
30984                             }
30985                             
30986                             offset += markerLength;
30987                             
30988                             continue;
30989                         }
30990                         
30991                         break;
30992                     }
30993                     
30994                 }
30995                 
30996                 var url = _this.urlAPI.createObjectURL(_this.file);
30997                 
30998                 _this.loadCanvas(url);
30999                 
31000                 return;
31001             }
31002             
31003             reader.readAsArrayBuffer(this.file);
31004             
31005         }
31006         
31007     },
31008     
31009     parseExifData : function(dataView, offset, length)
31010     {
31011         var tiffOffset = offset + 10,
31012             littleEndian,
31013             dirOffset;
31014     
31015         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31016             // No Exif data, might be XMP data instead
31017             return;
31018         }
31019         
31020         // Check for the ASCII code for "Exif" (0x45786966):
31021         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31022             // No Exif data, might be XMP data instead
31023             return;
31024         }
31025         if (tiffOffset + 8 > dataView.byteLength) {
31026             Roo.log('Invalid Exif data: Invalid segment size.');
31027             return;
31028         }
31029         // Check for the two null bytes:
31030         if (dataView.getUint16(offset + 8) !== 0x0000) {
31031             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31032             return;
31033         }
31034         // Check the byte alignment:
31035         switch (dataView.getUint16(tiffOffset)) {
31036         case 0x4949:
31037             littleEndian = true;
31038             break;
31039         case 0x4D4D:
31040             littleEndian = false;
31041             break;
31042         default:
31043             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31044             return;
31045         }
31046         // Check for the TIFF tag marker (0x002A):
31047         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31048             Roo.log('Invalid Exif data: Missing TIFF marker.');
31049             return;
31050         }
31051         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31052         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31053         
31054         this.parseExifTags(
31055             dataView,
31056             tiffOffset,
31057             tiffOffset + dirOffset,
31058             littleEndian
31059         );
31060     },
31061     
31062     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31063     {
31064         var tagsNumber,
31065             dirEndOffset,
31066             i;
31067         if (dirOffset + 6 > dataView.byteLength) {
31068             Roo.log('Invalid Exif data: Invalid directory offset.');
31069             return;
31070         }
31071         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31072         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31073         if (dirEndOffset + 4 > dataView.byteLength) {
31074             Roo.log('Invalid Exif data: Invalid directory size.');
31075             return;
31076         }
31077         for (i = 0; i < tagsNumber; i += 1) {
31078             this.parseExifTag(
31079                 dataView,
31080                 tiffOffset,
31081                 dirOffset + 2 + 12 * i, // tag offset
31082                 littleEndian
31083             );
31084         }
31085         // Return the offset to the next directory:
31086         return dataView.getUint32(dirEndOffset, littleEndian);
31087     },
31088     
31089     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31090     {
31091         var tag = dataView.getUint16(offset, littleEndian);
31092         
31093         this.exif[tag] = this.getExifValue(
31094             dataView,
31095             tiffOffset,
31096             offset,
31097             dataView.getUint16(offset + 2, littleEndian), // tag type
31098             dataView.getUint32(offset + 4, littleEndian), // tag length
31099             littleEndian
31100         );
31101     },
31102     
31103     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31104     {
31105         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31106             tagSize,
31107             dataOffset,
31108             values,
31109             i,
31110             str,
31111             c;
31112     
31113         if (!tagType) {
31114             Roo.log('Invalid Exif data: Invalid tag type.');
31115             return;
31116         }
31117         
31118         tagSize = tagType.size * length;
31119         // Determine if the value is contained in the dataOffset bytes,
31120         // or if the value at the dataOffset is a pointer to the actual data:
31121         dataOffset = tagSize > 4 ?
31122                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31123         if (dataOffset + tagSize > dataView.byteLength) {
31124             Roo.log('Invalid Exif data: Invalid data offset.');
31125             return;
31126         }
31127         if (length === 1) {
31128             return tagType.getValue(dataView, dataOffset, littleEndian);
31129         }
31130         values = [];
31131         for (i = 0; i < length; i += 1) {
31132             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31133         }
31134         
31135         if (tagType.ascii) {
31136             str = '';
31137             // Concatenate the chars:
31138             for (i = 0; i < values.length; i += 1) {
31139                 c = values[i];
31140                 // Ignore the terminating NULL byte(s):
31141                 if (c === '\u0000') {
31142                     break;
31143                 }
31144                 str += c;
31145             }
31146             return str;
31147         }
31148         return values;
31149     }
31150     
31151 });
31152
31153 Roo.apply(Roo.bootstrap.UploadCropbox, {
31154     tags : {
31155         'Orientation': 0x0112
31156     },
31157     
31158     Orientation: {
31159             1: 0, //'top-left',
31160 //            2: 'top-right',
31161             3: 180, //'bottom-right',
31162 //            4: 'bottom-left',
31163 //            5: 'left-top',
31164             6: 90, //'right-top',
31165 //            7: 'right-bottom',
31166             8: 270 //'left-bottom'
31167     },
31168     
31169     exifTagTypes : {
31170         // byte, 8-bit unsigned int:
31171         1: {
31172             getValue: function (dataView, dataOffset) {
31173                 return dataView.getUint8(dataOffset);
31174             },
31175             size: 1
31176         },
31177         // ascii, 8-bit byte:
31178         2: {
31179             getValue: function (dataView, dataOffset) {
31180                 return String.fromCharCode(dataView.getUint8(dataOffset));
31181             },
31182             size: 1,
31183             ascii: true
31184         },
31185         // short, 16 bit int:
31186         3: {
31187             getValue: function (dataView, dataOffset, littleEndian) {
31188                 return dataView.getUint16(dataOffset, littleEndian);
31189             },
31190             size: 2
31191         },
31192         // long, 32 bit int:
31193         4: {
31194             getValue: function (dataView, dataOffset, littleEndian) {
31195                 return dataView.getUint32(dataOffset, littleEndian);
31196             },
31197             size: 4
31198         },
31199         // rational = two long values, first is numerator, second is denominator:
31200         5: {
31201             getValue: function (dataView, dataOffset, littleEndian) {
31202                 return dataView.getUint32(dataOffset, littleEndian) /
31203                     dataView.getUint32(dataOffset + 4, littleEndian);
31204             },
31205             size: 8
31206         },
31207         // slong, 32 bit signed int:
31208         9: {
31209             getValue: function (dataView, dataOffset, littleEndian) {
31210                 return dataView.getInt32(dataOffset, littleEndian);
31211             },
31212             size: 4
31213         },
31214         // srational, two slongs, first is numerator, second is denominator:
31215         10: {
31216             getValue: function (dataView, dataOffset, littleEndian) {
31217                 return dataView.getInt32(dataOffset, littleEndian) /
31218                     dataView.getInt32(dataOffset + 4, littleEndian);
31219             },
31220             size: 8
31221         }
31222     },
31223     
31224     footer : {
31225         STANDARD : [
31226             {
31227                 tag : 'div',
31228                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31229                 action : 'rotate-left',
31230                 cn : [
31231                     {
31232                         tag : 'button',
31233                         cls : 'btn btn-default',
31234                         html : '<i class="fa fa-undo"></i>'
31235                     }
31236                 ]
31237             },
31238             {
31239                 tag : 'div',
31240                 cls : 'btn-group roo-upload-cropbox-picture',
31241                 action : 'picture',
31242                 cn : [
31243                     {
31244                         tag : 'button',
31245                         cls : 'btn btn-default',
31246                         html : '<i class="fa fa-picture-o"></i>'
31247                     }
31248                 ]
31249             },
31250             {
31251                 tag : 'div',
31252                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31253                 action : 'rotate-right',
31254                 cn : [
31255                     {
31256                         tag : 'button',
31257                         cls : 'btn btn-default',
31258                         html : '<i class="fa fa-repeat"></i>'
31259                     }
31260                 ]
31261             }
31262         ],
31263         DOCUMENT : [
31264             {
31265                 tag : 'div',
31266                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31267                 action : 'rotate-left',
31268                 cn : [
31269                     {
31270                         tag : 'button',
31271                         cls : 'btn btn-default',
31272                         html : '<i class="fa fa-undo"></i>'
31273                     }
31274                 ]
31275             },
31276             {
31277                 tag : 'div',
31278                 cls : 'btn-group roo-upload-cropbox-download',
31279                 action : 'download',
31280                 cn : [
31281                     {
31282                         tag : 'button',
31283                         cls : 'btn btn-default',
31284                         html : '<i class="fa fa-download"></i>'
31285                     }
31286                 ]
31287             },
31288             {
31289                 tag : 'div',
31290                 cls : 'btn-group roo-upload-cropbox-crop',
31291                 action : 'crop',
31292                 cn : [
31293                     {
31294                         tag : 'button',
31295                         cls : 'btn btn-default',
31296                         html : '<i class="fa fa-crop"></i>'
31297                     }
31298                 ]
31299             },
31300             {
31301                 tag : 'div',
31302                 cls : 'btn-group roo-upload-cropbox-trash',
31303                 action : 'trash',
31304                 cn : [
31305                     {
31306                         tag : 'button',
31307                         cls : 'btn btn-default',
31308                         html : '<i class="fa fa-trash"></i>'
31309                     }
31310                 ]
31311             },
31312             {
31313                 tag : 'div',
31314                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31315                 action : 'rotate-right',
31316                 cn : [
31317                     {
31318                         tag : 'button',
31319                         cls : 'btn btn-default',
31320                         html : '<i class="fa fa-repeat"></i>'
31321                     }
31322                 ]
31323             }
31324         ],
31325         ROTATOR : [
31326             {
31327                 tag : 'div',
31328                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31329                 action : 'rotate-left',
31330                 cn : [
31331                     {
31332                         tag : 'button',
31333                         cls : 'btn btn-default',
31334                         html : '<i class="fa fa-undo"></i>'
31335                     }
31336                 ]
31337             },
31338             {
31339                 tag : 'div',
31340                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31341                 action : 'rotate-right',
31342                 cn : [
31343                     {
31344                         tag : 'button',
31345                         cls : 'btn btn-default',
31346                         html : '<i class="fa fa-repeat"></i>'
31347                     }
31348                 ]
31349             }
31350         ]
31351     }
31352 });
31353
31354 /*
31355 * Licence: LGPL
31356 */
31357
31358 /**
31359  * @class Roo.bootstrap.DocumentManager
31360  * @extends Roo.bootstrap.Component
31361  * Bootstrap DocumentManager class
31362  * @cfg {String} paramName default 'imageUpload'
31363  * @cfg {String} toolTipName default 'filename'
31364  * @cfg {String} method default POST
31365  * @cfg {String} url action url
31366  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31367  * @cfg {Boolean} multiple multiple upload default true
31368  * @cfg {Number} thumbSize default 300
31369  * @cfg {String} fieldLabel
31370  * @cfg {Number} labelWidth default 4
31371  * @cfg {String} labelAlign (left|top) default left
31372  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31373 * @cfg {Number} labellg set the width of label (1-12)
31374  * @cfg {Number} labelmd set the width of label (1-12)
31375  * @cfg {Number} labelsm set the width of label (1-12)
31376  * @cfg {Number} labelxs set the width of label (1-12)
31377  * 
31378  * @constructor
31379  * Create a new DocumentManager
31380  * @param {Object} config The config object
31381  */
31382
31383 Roo.bootstrap.DocumentManager = function(config){
31384     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31385     
31386     this.files = [];
31387     this.delegates = [];
31388     
31389     this.addEvents({
31390         /**
31391          * @event initial
31392          * Fire when initial the DocumentManager
31393          * @param {Roo.bootstrap.DocumentManager} this
31394          */
31395         "initial" : true,
31396         /**
31397          * @event inspect
31398          * inspect selected file
31399          * @param {Roo.bootstrap.DocumentManager} this
31400          * @param {File} file
31401          */
31402         "inspect" : true,
31403         /**
31404          * @event exception
31405          * Fire when xhr load exception
31406          * @param {Roo.bootstrap.DocumentManager} this
31407          * @param {XMLHttpRequest} xhr
31408          */
31409         "exception" : true,
31410         /**
31411          * @event afterupload
31412          * Fire when xhr load exception
31413          * @param {Roo.bootstrap.DocumentManager} this
31414          * @param {XMLHttpRequest} xhr
31415          */
31416         "afterupload" : true,
31417         /**
31418          * @event prepare
31419          * prepare the form data
31420          * @param {Roo.bootstrap.DocumentManager} this
31421          * @param {Object} formData
31422          */
31423         "prepare" : true,
31424         /**
31425          * @event remove
31426          * Fire when remove the file
31427          * @param {Roo.bootstrap.DocumentManager} this
31428          * @param {Object} file
31429          */
31430         "remove" : true,
31431         /**
31432          * @event refresh
31433          * Fire after refresh the file
31434          * @param {Roo.bootstrap.DocumentManager} this
31435          */
31436         "refresh" : true,
31437         /**
31438          * @event click
31439          * Fire after click the image
31440          * @param {Roo.bootstrap.DocumentManager} this
31441          * @param {Object} file
31442          */
31443         "click" : true,
31444         /**
31445          * @event edit
31446          * Fire when upload a image and editable set to true
31447          * @param {Roo.bootstrap.DocumentManager} this
31448          * @param {Object} file
31449          */
31450         "edit" : true,
31451         /**
31452          * @event beforeselectfile
31453          * Fire before select file
31454          * @param {Roo.bootstrap.DocumentManager} this
31455          */
31456         "beforeselectfile" : true,
31457         /**
31458          * @event process
31459          * Fire before process file
31460          * @param {Roo.bootstrap.DocumentManager} this
31461          * @param {Object} file
31462          */
31463         "process" : true,
31464         /**
31465          * @event previewrendered
31466          * Fire when preview rendered
31467          * @param {Roo.bootstrap.DocumentManager} this
31468          * @param {Object} file
31469          */
31470         "previewrendered" : true,
31471         /**
31472          */
31473         "previewResize" : true
31474         
31475     });
31476 };
31477
31478 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31479     
31480     boxes : 0,
31481     inputName : '',
31482     thumbSize : 300,
31483     multiple : true,
31484     files : false,
31485     method : 'POST',
31486     url : '',
31487     paramName : 'imageUpload',
31488     toolTipName : 'filename',
31489     fieldLabel : '',
31490     labelWidth : 4,
31491     labelAlign : 'left',
31492     editable : true,
31493     delegates : false,
31494     xhr : false, 
31495     
31496     labellg : 0,
31497     labelmd : 0,
31498     labelsm : 0,
31499     labelxs : 0,
31500     
31501     getAutoCreate : function()
31502     {   
31503         var managerWidget = {
31504             tag : 'div',
31505             cls : 'roo-document-manager',
31506             cn : [
31507                 {
31508                     tag : 'input',
31509                     cls : 'roo-document-manager-selector',
31510                     type : 'file'
31511                 },
31512                 {
31513                     tag : 'div',
31514                     cls : 'roo-document-manager-uploader',
31515                     cn : [
31516                         {
31517                             tag : 'div',
31518                             cls : 'roo-document-manager-upload-btn',
31519                             html : '<i class="fa fa-plus"></i>'
31520                         }
31521                     ]
31522                     
31523                 }
31524             ]
31525         };
31526         
31527         var content = [
31528             {
31529                 tag : 'div',
31530                 cls : 'column col-md-12',
31531                 cn : managerWidget
31532             }
31533         ];
31534         
31535         if(this.fieldLabel.length){
31536             
31537             content = [
31538                 {
31539                     tag : 'div',
31540                     cls : 'column col-md-12',
31541                     html : this.fieldLabel
31542                 },
31543                 {
31544                     tag : 'div',
31545                     cls : 'column col-md-12',
31546                     cn : managerWidget
31547                 }
31548             ];
31549
31550             if(this.labelAlign == 'left'){
31551                 content = [
31552                     {
31553                         tag : 'div',
31554                         cls : 'column',
31555                         html : this.fieldLabel
31556                     },
31557                     {
31558                         tag : 'div',
31559                         cls : 'column',
31560                         cn : managerWidget
31561                     }
31562                 ];
31563                 
31564                 if(this.labelWidth > 12){
31565                     content[0].style = "width: " + this.labelWidth + 'px';
31566                 }
31567
31568                 if(this.labelWidth < 13 && this.labelmd == 0){
31569                     this.labelmd = this.labelWidth;
31570                 }
31571
31572                 if(this.labellg > 0){
31573                     content[0].cls += ' col-lg-' + this.labellg;
31574                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31575                 }
31576
31577                 if(this.labelmd > 0){
31578                     content[0].cls += ' col-md-' + this.labelmd;
31579                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31580                 }
31581
31582                 if(this.labelsm > 0){
31583                     content[0].cls += ' col-sm-' + this.labelsm;
31584                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31585                 }
31586
31587                 if(this.labelxs > 0){
31588                     content[0].cls += ' col-xs-' + this.labelxs;
31589                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31590                 }
31591                 
31592             }
31593         }
31594         
31595         var cfg = {
31596             tag : 'div',
31597             cls : 'row clearfix',
31598             cn : content
31599         };
31600         
31601         return cfg;
31602         
31603     },
31604     
31605     initEvents : function()
31606     {
31607         this.managerEl = this.el.select('.roo-document-manager', true).first();
31608         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31609         
31610         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31611         this.selectorEl.hide();
31612         
31613         if(this.multiple){
31614             this.selectorEl.attr('multiple', 'multiple');
31615         }
31616         
31617         this.selectorEl.on('change', this.onFileSelected, this);
31618         
31619         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31620         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31621         
31622         this.uploader.on('click', this.onUploaderClick, this);
31623         
31624         this.renderProgressDialog();
31625         
31626         var _this = this;
31627         
31628         window.addEventListener("resize", function() { _this.refresh(); } );
31629         
31630         this.fireEvent('initial', this);
31631     },
31632     
31633     renderProgressDialog : function()
31634     {
31635         var _this = this;
31636         
31637         this.progressDialog = new Roo.bootstrap.Modal({
31638             cls : 'roo-document-manager-progress-dialog',
31639             allow_close : false,
31640             animate : false,
31641             title : '',
31642             buttons : [
31643                 {
31644                     name  :'cancel',
31645                     weight : 'danger',
31646                     html : 'Cancel'
31647                 }
31648             ], 
31649             listeners : { 
31650                 btnclick : function() {
31651                     _this.uploadCancel();
31652                     this.hide();
31653                 }
31654             }
31655         });
31656          
31657         this.progressDialog.render(Roo.get(document.body));
31658          
31659         this.progress = new Roo.bootstrap.Progress({
31660             cls : 'roo-document-manager-progress',
31661             active : true,
31662             striped : true
31663         });
31664         
31665         this.progress.render(this.progressDialog.getChildContainer());
31666         
31667         this.progressBar = new Roo.bootstrap.ProgressBar({
31668             cls : 'roo-document-manager-progress-bar',
31669             aria_valuenow : 0,
31670             aria_valuemin : 0,
31671             aria_valuemax : 12,
31672             panel : 'success'
31673         });
31674         
31675         this.progressBar.render(this.progress.getChildContainer());
31676     },
31677     
31678     onUploaderClick : function(e)
31679     {
31680         e.preventDefault();
31681      
31682         if(this.fireEvent('beforeselectfile', this) != false){
31683             this.selectorEl.dom.click();
31684         }
31685         
31686     },
31687     
31688     onFileSelected : function(e)
31689     {
31690         e.preventDefault();
31691         
31692         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31693             return;
31694         }
31695         
31696         Roo.each(this.selectorEl.dom.files, function(file){
31697             if(this.fireEvent('inspect', this, file) != false){
31698                 this.files.push(file);
31699             }
31700         }, this);
31701         
31702         this.queue();
31703         
31704     },
31705     
31706     queue : function()
31707     {
31708         this.selectorEl.dom.value = '';
31709         
31710         if(!this.files || !this.files.length){
31711             return;
31712         }
31713         
31714         if(this.boxes > 0 && this.files.length > this.boxes){
31715             this.files = this.files.slice(0, this.boxes);
31716         }
31717         
31718         this.uploader.show();
31719         
31720         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31721             this.uploader.hide();
31722         }
31723         
31724         var _this = this;
31725         
31726         var files = [];
31727         
31728         var docs = [];
31729         
31730         Roo.each(this.files, function(file){
31731             
31732             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31733                 var f = this.renderPreview(file);
31734                 files.push(f);
31735                 return;
31736             }
31737             
31738             if(file.type.indexOf('image') != -1){
31739                 this.delegates.push(
31740                     (function(){
31741                         _this.process(file);
31742                     }).createDelegate(this)
31743                 );
31744         
31745                 return;
31746             }
31747             
31748             docs.push(
31749                 (function(){
31750                     _this.process(file);
31751                 }).createDelegate(this)
31752             );
31753             
31754         }, this);
31755         
31756         this.files = files;
31757         
31758         this.delegates = this.delegates.concat(docs);
31759         
31760         if(!this.delegates.length){
31761             this.refresh();
31762             return;
31763         }
31764         
31765         this.progressBar.aria_valuemax = this.delegates.length;
31766         
31767         this.arrange();
31768         
31769         return;
31770     },
31771     
31772     arrange : function()
31773     {
31774         if(!this.delegates.length){
31775             this.progressDialog.hide();
31776             this.refresh();
31777             return;
31778         }
31779         
31780         var delegate = this.delegates.shift();
31781         
31782         this.progressDialog.show();
31783         
31784         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31785         
31786         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31787         
31788         delegate();
31789     },
31790     
31791     refresh : function()
31792     {
31793         this.uploader.show();
31794         
31795         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31796             this.uploader.hide();
31797         }
31798         
31799         Roo.isTouch ? this.closable(false) : this.closable(true);
31800         
31801         this.fireEvent('refresh', this);
31802     },
31803     
31804     onRemove : function(e, el, o)
31805     {
31806         e.preventDefault();
31807         
31808         this.fireEvent('remove', this, o);
31809         
31810     },
31811     
31812     remove : function(o)
31813     {
31814         var files = [];
31815         
31816         Roo.each(this.files, function(file){
31817             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31818                 files.push(file);
31819                 return;
31820             }
31821
31822             o.target.remove();
31823
31824         }, this);
31825         
31826         this.files = files;
31827         
31828         this.refresh();
31829     },
31830     
31831     clear : function()
31832     {
31833         Roo.each(this.files, function(file){
31834             if(!file.target){
31835                 return;
31836             }
31837             
31838             file.target.remove();
31839
31840         }, this);
31841         
31842         this.files = [];
31843         
31844         this.refresh();
31845     },
31846     
31847     onClick : function(e, el, o)
31848     {
31849         e.preventDefault();
31850         
31851         this.fireEvent('click', this, o);
31852         
31853     },
31854     
31855     closable : function(closable)
31856     {
31857         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31858             
31859             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31860             
31861             if(closable){
31862                 el.show();
31863                 return;
31864             }
31865             
31866             el.hide();
31867             
31868         }, this);
31869     },
31870     
31871     xhrOnLoad : function(xhr)
31872     {
31873         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31874             el.remove();
31875         }, this);
31876         
31877         if (xhr.readyState !== 4) {
31878             this.arrange();
31879             this.fireEvent('exception', this, xhr);
31880             return;
31881         }
31882
31883         var response = Roo.decode(xhr.responseText);
31884         
31885         if(!response.success){
31886             this.arrange();
31887             this.fireEvent('exception', this, xhr);
31888             return;
31889         }
31890         
31891         var file = this.renderPreview(response.data);
31892         
31893         this.files.push(file);
31894         
31895         this.arrange();
31896         
31897         this.fireEvent('afterupload', this, xhr);
31898         
31899     },
31900     
31901     xhrOnError : function(xhr)
31902     {
31903         Roo.log('xhr on error');
31904         
31905         var response = Roo.decode(xhr.responseText);
31906           
31907         Roo.log(response);
31908         
31909         this.arrange();
31910     },
31911     
31912     process : function(file)
31913     {
31914         if(this.fireEvent('process', this, file) !== false){
31915             if(this.editable && file.type.indexOf('image') != -1){
31916                 this.fireEvent('edit', this, file);
31917                 return;
31918             }
31919
31920             this.uploadStart(file, false);
31921
31922             return;
31923         }
31924         
31925     },
31926     
31927     uploadStart : function(file, crop)
31928     {
31929         this.xhr = new XMLHttpRequest();
31930         
31931         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31932             this.arrange();
31933             return;
31934         }
31935         
31936         file.xhr = this.xhr;
31937             
31938         this.managerEl.createChild({
31939             tag : 'div',
31940             cls : 'roo-document-manager-loading',
31941             cn : [
31942                 {
31943                     tag : 'div',
31944                     tooltip : file.name,
31945                     cls : 'roo-document-manager-thumb',
31946                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31947                 }
31948             ]
31949
31950         });
31951
31952         this.xhr.open(this.method, this.url, true);
31953         
31954         var headers = {
31955             "Accept": "application/json",
31956             "Cache-Control": "no-cache",
31957             "X-Requested-With": "XMLHttpRequest"
31958         };
31959         
31960         for (var headerName in headers) {
31961             var headerValue = headers[headerName];
31962             if (headerValue) {
31963                 this.xhr.setRequestHeader(headerName, headerValue);
31964             }
31965         }
31966         
31967         var _this = this;
31968         
31969         this.xhr.onload = function()
31970         {
31971             _this.xhrOnLoad(_this.xhr);
31972         }
31973         
31974         this.xhr.onerror = function()
31975         {
31976             _this.xhrOnError(_this.xhr);
31977         }
31978         
31979         var formData = new FormData();
31980
31981         formData.append('returnHTML', 'NO');
31982         
31983         if(crop){
31984             formData.append('crop', crop);
31985         }
31986         
31987         formData.append(this.paramName, file, file.name);
31988         
31989         var options = {
31990             file : file, 
31991             manually : false
31992         };
31993         
31994         if(this.fireEvent('prepare', this, formData, options) != false){
31995             
31996             if(options.manually){
31997                 return;
31998             }
31999             
32000             this.xhr.send(formData);
32001             return;
32002         };
32003         
32004         this.uploadCancel();
32005     },
32006     
32007     uploadCancel : function()
32008     {
32009         if (this.xhr) {
32010             this.xhr.abort();
32011         }
32012         
32013         this.delegates = [];
32014         
32015         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32016             el.remove();
32017         }, this);
32018         
32019         this.arrange();
32020     },
32021     
32022     renderPreview : function(file)
32023     {
32024         if(typeof(file.target) != 'undefined' && file.target){
32025             return file;
32026         }
32027         
32028         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32029         
32030         var previewEl = this.managerEl.createChild({
32031             tag : 'div',
32032             cls : 'roo-document-manager-preview',
32033             cn : [
32034                 {
32035                     tag : 'div',
32036                     tooltip : file[this.toolTipName],
32037                     cls : 'roo-document-manager-thumb',
32038                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32039                 },
32040                 {
32041                     tag : 'button',
32042                     cls : 'close',
32043                     html : '<i class="fa fa-times-circle"></i>'
32044                 }
32045             ]
32046         });
32047
32048         var close = previewEl.select('button.close', true).first();
32049
32050         close.on('click', this.onRemove, this, file);
32051
32052         file.target = previewEl;
32053
32054         var image = previewEl.select('img', true).first();
32055         
32056         var _this = this;
32057         
32058         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32059         
32060         image.on('click', this.onClick, this, file);
32061         
32062         this.fireEvent('previewrendered', this, file);
32063         
32064         return file;
32065         
32066     },
32067     
32068     onPreviewLoad : function(file, image)
32069     {
32070         if(typeof(file.target) == 'undefined' || !file.target){
32071             return;
32072         }
32073         
32074         var width = image.dom.naturalWidth || image.dom.width;
32075         var height = image.dom.naturalHeight || image.dom.height;
32076         
32077         if(!this.previewResize) {
32078             return;
32079         }
32080         
32081         if(width > height){
32082             file.target.addClass('wide');
32083             return;
32084         }
32085         
32086         file.target.addClass('tall');
32087         return;
32088         
32089     },
32090     
32091     uploadFromSource : function(file, crop)
32092     {
32093         this.xhr = new XMLHttpRequest();
32094         
32095         this.managerEl.createChild({
32096             tag : 'div',
32097             cls : 'roo-document-manager-loading',
32098             cn : [
32099                 {
32100                     tag : 'div',
32101                     tooltip : file.name,
32102                     cls : 'roo-document-manager-thumb',
32103                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32104                 }
32105             ]
32106
32107         });
32108
32109         this.xhr.open(this.method, this.url, true);
32110         
32111         var headers = {
32112             "Accept": "application/json",
32113             "Cache-Control": "no-cache",
32114             "X-Requested-With": "XMLHttpRequest"
32115         };
32116         
32117         for (var headerName in headers) {
32118             var headerValue = headers[headerName];
32119             if (headerValue) {
32120                 this.xhr.setRequestHeader(headerName, headerValue);
32121             }
32122         }
32123         
32124         var _this = this;
32125         
32126         this.xhr.onload = function()
32127         {
32128             _this.xhrOnLoad(_this.xhr);
32129         }
32130         
32131         this.xhr.onerror = function()
32132         {
32133             _this.xhrOnError(_this.xhr);
32134         }
32135         
32136         var formData = new FormData();
32137
32138         formData.append('returnHTML', 'NO');
32139         
32140         formData.append('crop', crop);
32141         
32142         if(typeof(file.filename) != 'undefined'){
32143             formData.append('filename', file.filename);
32144         }
32145         
32146         if(typeof(file.mimetype) != 'undefined'){
32147             formData.append('mimetype', file.mimetype);
32148         }
32149         
32150         Roo.log(formData);
32151         
32152         if(this.fireEvent('prepare', this, formData) != false){
32153             this.xhr.send(formData);
32154         };
32155     }
32156 });
32157
32158 /*
32159 * Licence: LGPL
32160 */
32161
32162 /**
32163  * @class Roo.bootstrap.DocumentViewer
32164  * @extends Roo.bootstrap.Component
32165  * Bootstrap DocumentViewer class
32166  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32167  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32168  * 
32169  * @constructor
32170  * Create a new DocumentViewer
32171  * @param {Object} config The config object
32172  */
32173
32174 Roo.bootstrap.DocumentViewer = function(config){
32175     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32176     
32177     this.addEvents({
32178         /**
32179          * @event initial
32180          * Fire after initEvent
32181          * @param {Roo.bootstrap.DocumentViewer} this
32182          */
32183         "initial" : true,
32184         /**
32185          * @event click
32186          * Fire after click
32187          * @param {Roo.bootstrap.DocumentViewer} this
32188          */
32189         "click" : true,
32190         /**
32191          * @event download
32192          * Fire after download button
32193          * @param {Roo.bootstrap.DocumentViewer} this
32194          */
32195         "download" : true,
32196         /**
32197          * @event trash
32198          * Fire after trash button
32199          * @param {Roo.bootstrap.DocumentViewer} this
32200          */
32201         "trash" : true
32202         
32203     });
32204 };
32205
32206 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32207     
32208     showDownload : true,
32209     
32210     showTrash : true,
32211     
32212     getAutoCreate : function()
32213     {
32214         var cfg = {
32215             tag : 'div',
32216             cls : 'roo-document-viewer',
32217             cn : [
32218                 {
32219                     tag : 'div',
32220                     cls : 'roo-document-viewer-body',
32221                     cn : [
32222                         {
32223                             tag : 'div',
32224                             cls : 'roo-document-viewer-thumb',
32225                             cn : [
32226                                 {
32227                                     tag : 'img',
32228                                     cls : 'roo-document-viewer-image'
32229                                 }
32230                             ]
32231                         }
32232                     ]
32233                 },
32234                 {
32235                     tag : 'div',
32236                     cls : 'roo-document-viewer-footer',
32237                     cn : {
32238                         tag : 'div',
32239                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32240                         cn : [
32241                             {
32242                                 tag : 'div',
32243                                 cls : 'btn-group roo-document-viewer-download',
32244                                 cn : [
32245                                     {
32246                                         tag : 'button',
32247                                         cls : 'btn btn-default',
32248                                         html : '<i class="fa fa-download"></i>'
32249                                     }
32250                                 ]
32251                             },
32252                             {
32253                                 tag : 'div',
32254                                 cls : 'btn-group roo-document-viewer-trash',
32255                                 cn : [
32256                                     {
32257                                         tag : 'button',
32258                                         cls : 'btn btn-default',
32259                                         html : '<i class="fa fa-trash"></i>'
32260                                     }
32261                                 ]
32262                             }
32263                         ]
32264                     }
32265                 }
32266             ]
32267         };
32268         
32269         return cfg;
32270     },
32271     
32272     initEvents : function()
32273     {
32274         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32275         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32276         
32277         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32278         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32279         
32280         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32281         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32282         
32283         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32284         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32285         
32286         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32287         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32288         
32289         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32290         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32291         
32292         this.bodyEl.on('click', this.onClick, this);
32293         this.downloadBtn.on('click', this.onDownload, this);
32294         this.trashBtn.on('click', this.onTrash, this);
32295         
32296         this.downloadBtn.hide();
32297         this.trashBtn.hide();
32298         
32299         if(this.showDownload){
32300             this.downloadBtn.show();
32301         }
32302         
32303         if(this.showTrash){
32304             this.trashBtn.show();
32305         }
32306         
32307         if(!this.showDownload && !this.showTrash) {
32308             this.footerEl.hide();
32309         }
32310         
32311     },
32312     
32313     initial : function()
32314     {
32315         this.fireEvent('initial', this);
32316         
32317     },
32318     
32319     onClick : function(e)
32320     {
32321         e.preventDefault();
32322         
32323         this.fireEvent('click', this);
32324     },
32325     
32326     onDownload : function(e)
32327     {
32328         e.preventDefault();
32329         
32330         this.fireEvent('download', this);
32331     },
32332     
32333     onTrash : function(e)
32334     {
32335         e.preventDefault();
32336         
32337         this.fireEvent('trash', this);
32338     }
32339     
32340 });
32341 /*
32342  * - LGPL
32343  *
32344  * nav progress bar
32345  * 
32346  */
32347
32348 /**
32349  * @class Roo.bootstrap.NavProgressBar
32350  * @extends Roo.bootstrap.Component
32351  * Bootstrap NavProgressBar class
32352  * 
32353  * @constructor
32354  * Create a new nav progress bar
32355  * @param {Object} config The config object
32356  */
32357
32358 Roo.bootstrap.NavProgressBar = function(config){
32359     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32360
32361     this.bullets = this.bullets || [];
32362    
32363 //    Roo.bootstrap.NavProgressBar.register(this);
32364      this.addEvents({
32365         /**
32366              * @event changed
32367              * Fires when the active item changes
32368              * @param {Roo.bootstrap.NavProgressBar} this
32369              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32370              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32371          */
32372         'changed': true
32373      });
32374     
32375 };
32376
32377 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32378     
32379     bullets : [],
32380     barItems : [],
32381     
32382     getAutoCreate : function()
32383     {
32384         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32385         
32386         cfg = {
32387             tag : 'div',
32388             cls : 'roo-navigation-bar-group',
32389             cn : [
32390                 {
32391                     tag : 'div',
32392                     cls : 'roo-navigation-top-bar'
32393                 },
32394                 {
32395                     tag : 'div',
32396                     cls : 'roo-navigation-bullets-bar',
32397                     cn : [
32398                         {
32399                             tag : 'ul',
32400                             cls : 'roo-navigation-bar'
32401                         }
32402                     ]
32403                 },
32404                 
32405                 {
32406                     tag : 'div',
32407                     cls : 'roo-navigation-bottom-bar'
32408                 }
32409             ]
32410             
32411         };
32412         
32413         return cfg;
32414         
32415     },
32416     
32417     initEvents: function() 
32418     {
32419         
32420     },
32421     
32422     onRender : function(ct, position) 
32423     {
32424         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32425         
32426         if(this.bullets.length){
32427             Roo.each(this.bullets, function(b){
32428                this.addItem(b);
32429             }, this);
32430         }
32431         
32432         this.format();
32433         
32434     },
32435     
32436     addItem : function(cfg)
32437     {
32438         var item = new Roo.bootstrap.NavProgressItem(cfg);
32439         
32440         item.parentId = this.id;
32441         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32442         
32443         if(cfg.html){
32444             var top = new Roo.bootstrap.Element({
32445                 tag : 'div',
32446                 cls : 'roo-navigation-bar-text'
32447             });
32448             
32449             var bottom = new Roo.bootstrap.Element({
32450                 tag : 'div',
32451                 cls : 'roo-navigation-bar-text'
32452             });
32453             
32454             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32455             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32456             
32457             var topText = new Roo.bootstrap.Element({
32458                 tag : 'span',
32459                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32460             });
32461             
32462             var bottomText = new Roo.bootstrap.Element({
32463                 tag : 'span',
32464                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32465             });
32466             
32467             topText.onRender(top.el, null);
32468             bottomText.onRender(bottom.el, null);
32469             
32470             item.topEl = top;
32471             item.bottomEl = bottom;
32472         }
32473         
32474         this.barItems.push(item);
32475         
32476         return item;
32477     },
32478     
32479     getActive : function()
32480     {
32481         var active = false;
32482         
32483         Roo.each(this.barItems, function(v){
32484             
32485             if (!v.isActive()) {
32486                 return;
32487             }
32488             
32489             active = v;
32490             return false;
32491             
32492         });
32493         
32494         return active;
32495     },
32496     
32497     setActiveItem : function(item)
32498     {
32499         var prev = false;
32500         
32501         Roo.each(this.barItems, function(v){
32502             if (v.rid == item.rid) {
32503                 return ;
32504             }
32505             
32506             if (v.isActive()) {
32507                 v.setActive(false);
32508                 prev = v;
32509             }
32510         });
32511
32512         item.setActive(true);
32513         
32514         this.fireEvent('changed', this, item, prev);
32515     },
32516     
32517     getBarItem: function(rid)
32518     {
32519         var ret = false;
32520         
32521         Roo.each(this.barItems, function(e) {
32522             if (e.rid != rid) {
32523                 return;
32524             }
32525             
32526             ret =  e;
32527             return false;
32528         });
32529         
32530         return ret;
32531     },
32532     
32533     indexOfItem : function(item)
32534     {
32535         var index = false;
32536         
32537         Roo.each(this.barItems, function(v, i){
32538             
32539             if (v.rid != item.rid) {
32540                 return;
32541             }
32542             
32543             index = i;
32544             return false
32545         });
32546         
32547         return index;
32548     },
32549     
32550     setActiveNext : function()
32551     {
32552         var i = this.indexOfItem(this.getActive());
32553         
32554         if (i > this.barItems.length) {
32555             return;
32556         }
32557         
32558         this.setActiveItem(this.barItems[i+1]);
32559     },
32560     
32561     setActivePrev : function()
32562     {
32563         var i = this.indexOfItem(this.getActive());
32564         
32565         if (i  < 1) {
32566             return;
32567         }
32568         
32569         this.setActiveItem(this.barItems[i-1]);
32570     },
32571     
32572     format : function()
32573     {
32574         if(!this.barItems.length){
32575             return;
32576         }
32577      
32578         var width = 100 / this.barItems.length;
32579         
32580         Roo.each(this.barItems, function(i){
32581             i.el.setStyle('width', width + '%');
32582             i.topEl.el.setStyle('width', width + '%');
32583             i.bottomEl.el.setStyle('width', width + '%');
32584         }, this);
32585         
32586     }
32587     
32588 });
32589 /*
32590  * - LGPL
32591  *
32592  * Nav Progress Item
32593  * 
32594  */
32595
32596 /**
32597  * @class Roo.bootstrap.NavProgressItem
32598  * @extends Roo.bootstrap.Component
32599  * Bootstrap NavProgressItem class
32600  * @cfg {String} rid the reference id
32601  * @cfg {Boolean} active (true|false) Is item active default false
32602  * @cfg {Boolean} disabled (true|false) Is item active default false
32603  * @cfg {String} html
32604  * @cfg {String} position (top|bottom) text position default bottom
32605  * @cfg {String} icon show icon instead of number
32606  * 
32607  * @constructor
32608  * Create a new NavProgressItem
32609  * @param {Object} config The config object
32610  */
32611 Roo.bootstrap.NavProgressItem = function(config){
32612     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32613     this.addEvents({
32614         // raw events
32615         /**
32616          * @event click
32617          * The raw click event for the entire grid.
32618          * @param {Roo.bootstrap.NavProgressItem} this
32619          * @param {Roo.EventObject} e
32620          */
32621         "click" : true
32622     });
32623    
32624 };
32625
32626 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32627     
32628     rid : '',
32629     active : false,
32630     disabled : false,
32631     html : '',
32632     position : 'bottom',
32633     icon : false,
32634     
32635     getAutoCreate : function()
32636     {
32637         var iconCls = 'roo-navigation-bar-item-icon';
32638         
32639         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32640         
32641         var cfg = {
32642             tag: 'li',
32643             cls: 'roo-navigation-bar-item',
32644             cn : [
32645                 {
32646                     tag : 'i',
32647                     cls : iconCls
32648                 }
32649             ]
32650         };
32651         
32652         if(this.active){
32653             cfg.cls += ' active';
32654         }
32655         if(this.disabled){
32656             cfg.cls += ' disabled';
32657         }
32658         
32659         return cfg;
32660     },
32661     
32662     disable : function()
32663     {
32664         this.setDisabled(true);
32665     },
32666     
32667     enable : function()
32668     {
32669         this.setDisabled(false);
32670     },
32671     
32672     initEvents: function() 
32673     {
32674         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32675         
32676         this.iconEl.on('click', this.onClick, this);
32677     },
32678     
32679     onClick : function(e)
32680     {
32681         e.preventDefault();
32682         
32683         if(this.disabled){
32684             return;
32685         }
32686         
32687         if(this.fireEvent('click', this, e) === false){
32688             return;
32689         };
32690         
32691         this.parent().setActiveItem(this);
32692     },
32693     
32694     isActive: function () 
32695     {
32696         return this.active;
32697     },
32698     
32699     setActive : function(state)
32700     {
32701         if(this.active == state){
32702             return;
32703         }
32704         
32705         this.active = state;
32706         
32707         if (state) {
32708             this.el.addClass('active');
32709             return;
32710         }
32711         
32712         this.el.removeClass('active');
32713         
32714         return;
32715     },
32716     
32717     setDisabled : function(state)
32718     {
32719         if(this.disabled == state){
32720             return;
32721         }
32722         
32723         this.disabled = state;
32724         
32725         if (state) {
32726             this.el.addClass('disabled');
32727             return;
32728         }
32729         
32730         this.el.removeClass('disabled');
32731     },
32732     
32733     tooltipEl : function()
32734     {
32735         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32736     }
32737 });
32738  
32739
32740  /*
32741  * - LGPL
32742  *
32743  * FieldLabel
32744  * 
32745  */
32746
32747 /**
32748  * @class Roo.bootstrap.FieldLabel
32749  * @extends Roo.bootstrap.Component
32750  * Bootstrap FieldLabel class
32751  * @cfg {String} html contents of the element
32752  * @cfg {String} tag tag of the element default label
32753  * @cfg {String} cls class of the element
32754  * @cfg {String} target label target 
32755  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32756  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32757  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32758  * @cfg {String} iconTooltip default "This field is required"
32759  * @cfg {String} indicatorpos (left|right) default left
32760  * 
32761  * @constructor
32762  * Create a new FieldLabel
32763  * @param {Object} config The config object
32764  */
32765
32766 Roo.bootstrap.FieldLabel = function(config){
32767     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32768     
32769     this.addEvents({
32770             /**
32771              * @event invalid
32772              * Fires after the field has been marked as invalid.
32773              * @param {Roo.form.FieldLabel} this
32774              * @param {String} msg The validation message
32775              */
32776             invalid : true,
32777             /**
32778              * @event valid
32779              * Fires after the field has been validated with no errors.
32780              * @param {Roo.form.FieldLabel} this
32781              */
32782             valid : true
32783         });
32784 };
32785
32786 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32787     
32788     tag: 'label',
32789     cls: '',
32790     html: '',
32791     target: '',
32792     allowBlank : true,
32793     invalidClass : 'has-warning',
32794     validClass : 'has-success',
32795     iconTooltip : 'This field is required',
32796     indicatorpos : 'left',
32797     
32798     getAutoCreate : function(){
32799         
32800         var cls = "";
32801         if (!this.allowBlank) {
32802             cls  = "visible";
32803         }
32804         
32805         var cfg = {
32806             tag : this.tag,
32807             cls : 'roo-bootstrap-field-label ' + this.cls,
32808             for : this.target,
32809             cn : [
32810                 {
32811                     tag : 'i',
32812                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32813                     tooltip : this.iconTooltip
32814                 },
32815                 {
32816                     tag : 'span',
32817                     html : this.html
32818                 }
32819             ] 
32820         };
32821         
32822         if(this.indicatorpos == 'right'){
32823             var cfg = {
32824                 tag : this.tag,
32825                 cls : 'roo-bootstrap-field-label ' + this.cls,
32826                 for : this.target,
32827                 cn : [
32828                     {
32829                         tag : 'span',
32830                         html : this.html
32831                     },
32832                     {
32833                         tag : 'i',
32834                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32835                         tooltip : this.iconTooltip
32836                     }
32837                 ] 
32838             };
32839         }
32840         
32841         return cfg;
32842     },
32843     
32844     initEvents: function() 
32845     {
32846         Roo.bootstrap.Element.superclass.initEvents.call(this);
32847         
32848         this.indicator = this.indicatorEl();
32849         
32850         if(this.indicator){
32851             this.indicator.removeClass('visible');
32852             this.indicator.addClass('invisible');
32853         }
32854         
32855         Roo.bootstrap.FieldLabel.register(this);
32856     },
32857     
32858     indicatorEl : function()
32859     {
32860         var indicator = this.el.select('i.roo-required-indicator',true).first();
32861         
32862         if(!indicator){
32863             return false;
32864         }
32865         
32866         return indicator;
32867         
32868     },
32869     
32870     /**
32871      * Mark this field as valid
32872      */
32873     markValid : function()
32874     {
32875         if(this.indicator){
32876             this.indicator.removeClass('visible');
32877             this.indicator.addClass('invisible');
32878         }
32879         if (Roo.bootstrap.version == 3) {
32880             this.el.removeClass(this.invalidClass);
32881             this.el.addClass(this.validClass);
32882         } else {
32883             this.el.removeClass('is-invalid');
32884             this.el.addClass('is-valid');
32885         }
32886         
32887         
32888         this.fireEvent('valid', this);
32889     },
32890     
32891     /**
32892      * Mark this field as invalid
32893      * @param {String} msg The validation message
32894      */
32895     markInvalid : function(msg)
32896     {
32897         if(this.indicator){
32898             this.indicator.removeClass('invisible');
32899             this.indicator.addClass('visible');
32900         }
32901           if (Roo.bootstrap.version == 3) {
32902             this.el.removeClass(this.validClass);
32903             this.el.addClass(this.invalidClass);
32904         } else {
32905             this.el.removeClass('is-valid');
32906             this.el.addClass('is-invalid');
32907         }
32908         
32909         
32910         this.fireEvent('invalid', this, msg);
32911     }
32912     
32913    
32914 });
32915
32916 Roo.apply(Roo.bootstrap.FieldLabel, {
32917     
32918     groups: {},
32919     
32920      /**
32921     * register a FieldLabel Group
32922     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32923     */
32924     register : function(label)
32925     {
32926         if(this.groups.hasOwnProperty(label.target)){
32927             return;
32928         }
32929      
32930         this.groups[label.target] = label;
32931         
32932     },
32933     /**
32934     * fetch a FieldLabel Group based on the target
32935     * @param {string} target
32936     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32937     */
32938     get: function(target) {
32939         if (typeof(this.groups[target]) == 'undefined') {
32940             return false;
32941         }
32942         
32943         return this.groups[target] ;
32944     }
32945 });
32946
32947  
32948
32949  /*
32950  * - LGPL
32951  *
32952  * page DateSplitField.
32953  * 
32954  */
32955
32956
32957 /**
32958  * @class Roo.bootstrap.DateSplitField
32959  * @extends Roo.bootstrap.Component
32960  * Bootstrap DateSplitField class
32961  * @cfg {string} fieldLabel - the label associated
32962  * @cfg {Number} labelWidth set the width of label (0-12)
32963  * @cfg {String} labelAlign (top|left)
32964  * @cfg {Boolean} dayAllowBlank (true|false) default false
32965  * @cfg {Boolean} monthAllowBlank (true|false) default false
32966  * @cfg {Boolean} yearAllowBlank (true|false) default false
32967  * @cfg {string} dayPlaceholder 
32968  * @cfg {string} monthPlaceholder
32969  * @cfg {string} yearPlaceholder
32970  * @cfg {string} dayFormat default 'd'
32971  * @cfg {string} monthFormat default 'm'
32972  * @cfg {string} yearFormat default 'Y'
32973  * @cfg {Number} labellg set the width of label (1-12)
32974  * @cfg {Number} labelmd set the width of label (1-12)
32975  * @cfg {Number} labelsm set the width of label (1-12)
32976  * @cfg {Number} labelxs set the width of label (1-12)
32977
32978  *     
32979  * @constructor
32980  * Create a new DateSplitField
32981  * @param {Object} config The config object
32982  */
32983
32984 Roo.bootstrap.DateSplitField = function(config){
32985     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32986     
32987     this.addEvents({
32988         // raw events
32989          /**
32990          * @event years
32991          * getting the data of years
32992          * @param {Roo.bootstrap.DateSplitField} this
32993          * @param {Object} years
32994          */
32995         "years" : true,
32996         /**
32997          * @event days
32998          * getting the data of days
32999          * @param {Roo.bootstrap.DateSplitField} this
33000          * @param {Object} days
33001          */
33002         "days" : true,
33003         /**
33004          * @event invalid
33005          * Fires after the field has been marked as invalid.
33006          * @param {Roo.form.Field} this
33007          * @param {String} msg The validation message
33008          */
33009         invalid : true,
33010        /**
33011          * @event valid
33012          * Fires after the field has been validated with no errors.
33013          * @param {Roo.form.Field} this
33014          */
33015         valid : true
33016     });
33017 };
33018
33019 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33020     
33021     fieldLabel : '',
33022     labelAlign : 'top',
33023     labelWidth : 3,
33024     dayAllowBlank : false,
33025     monthAllowBlank : false,
33026     yearAllowBlank : false,
33027     dayPlaceholder : '',
33028     monthPlaceholder : '',
33029     yearPlaceholder : '',
33030     dayFormat : 'd',
33031     monthFormat : 'm',
33032     yearFormat : 'Y',
33033     isFormField : true,
33034     labellg : 0,
33035     labelmd : 0,
33036     labelsm : 0,
33037     labelxs : 0,
33038     
33039     getAutoCreate : function()
33040     {
33041         var cfg = {
33042             tag : 'div',
33043             cls : 'row roo-date-split-field-group',
33044             cn : [
33045                 {
33046                     tag : 'input',
33047                     type : 'hidden',
33048                     cls : 'form-hidden-field roo-date-split-field-group-value',
33049                     name : this.name
33050                 }
33051             ]
33052         };
33053         
33054         var labelCls = 'col-md-12';
33055         var contentCls = 'col-md-4';
33056         
33057         if(this.fieldLabel){
33058             
33059             var label = {
33060                 tag : 'div',
33061                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33062                 cn : [
33063                     {
33064                         tag : 'label',
33065                         html : this.fieldLabel
33066                     }
33067                 ]
33068             };
33069             
33070             if(this.labelAlign == 'left'){
33071             
33072                 if(this.labelWidth > 12){
33073                     label.style = "width: " + this.labelWidth + 'px';
33074                 }
33075
33076                 if(this.labelWidth < 13 && this.labelmd == 0){
33077                     this.labelmd = this.labelWidth;
33078                 }
33079
33080                 if(this.labellg > 0){
33081                     labelCls = ' col-lg-' + this.labellg;
33082                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33083                 }
33084
33085                 if(this.labelmd > 0){
33086                     labelCls = ' col-md-' + this.labelmd;
33087                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33088                 }
33089
33090                 if(this.labelsm > 0){
33091                     labelCls = ' col-sm-' + this.labelsm;
33092                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33093                 }
33094
33095                 if(this.labelxs > 0){
33096                     labelCls = ' col-xs-' + this.labelxs;
33097                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33098                 }
33099             }
33100             
33101             label.cls += ' ' + labelCls;
33102             
33103             cfg.cn.push(label);
33104         }
33105         
33106         Roo.each(['day', 'month', 'year'], function(t){
33107             cfg.cn.push({
33108                 tag : 'div',
33109                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33110             });
33111         }, this);
33112         
33113         return cfg;
33114     },
33115     
33116     inputEl: function ()
33117     {
33118         return this.el.select('.roo-date-split-field-group-value', true).first();
33119     },
33120     
33121     onRender : function(ct, position) 
33122     {
33123         var _this = this;
33124         
33125         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33126         
33127         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33128         
33129         this.dayField = new Roo.bootstrap.ComboBox({
33130             allowBlank : this.dayAllowBlank,
33131             alwaysQuery : true,
33132             displayField : 'value',
33133             editable : false,
33134             fieldLabel : '',
33135             forceSelection : true,
33136             mode : 'local',
33137             placeholder : this.dayPlaceholder,
33138             selectOnFocus : true,
33139             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33140             triggerAction : 'all',
33141             typeAhead : true,
33142             valueField : 'value',
33143             store : new Roo.data.SimpleStore({
33144                 data : (function() {    
33145                     var days = [];
33146                     _this.fireEvent('days', _this, days);
33147                     return days;
33148                 })(),
33149                 fields : [ 'value' ]
33150             }),
33151             listeners : {
33152                 select : function (_self, record, index)
33153                 {
33154                     _this.setValue(_this.getValue());
33155                 }
33156             }
33157         });
33158
33159         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33160         
33161         this.monthField = new Roo.bootstrap.MonthField({
33162             after : '<i class=\"fa fa-calendar\"></i>',
33163             allowBlank : this.monthAllowBlank,
33164             placeholder : this.monthPlaceholder,
33165             readOnly : true,
33166             listeners : {
33167                 render : function (_self)
33168                 {
33169                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33170                         e.preventDefault();
33171                         _self.focus();
33172                     });
33173                 },
33174                 select : function (_self, oldvalue, newvalue)
33175                 {
33176                     _this.setValue(_this.getValue());
33177                 }
33178             }
33179         });
33180         
33181         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33182         
33183         this.yearField = new Roo.bootstrap.ComboBox({
33184             allowBlank : this.yearAllowBlank,
33185             alwaysQuery : true,
33186             displayField : 'value',
33187             editable : false,
33188             fieldLabel : '',
33189             forceSelection : true,
33190             mode : 'local',
33191             placeholder : this.yearPlaceholder,
33192             selectOnFocus : true,
33193             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33194             triggerAction : 'all',
33195             typeAhead : true,
33196             valueField : 'value',
33197             store : new Roo.data.SimpleStore({
33198                 data : (function() {
33199                     var years = [];
33200                     _this.fireEvent('years', _this, years);
33201                     return years;
33202                 })(),
33203                 fields : [ 'value' ]
33204             }),
33205             listeners : {
33206                 select : function (_self, record, index)
33207                 {
33208                     _this.setValue(_this.getValue());
33209                 }
33210             }
33211         });
33212
33213         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33214     },
33215     
33216     setValue : function(v, format)
33217     {
33218         this.inputEl.dom.value = v;
33219         
33220         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33221         
33222         var d = Date.parseDate(v, f);
33223         
33224         if(!d){
33225             this.validate();
33226             return;
33227         }
33228         
33229         this.setDay(d.format(this.dayFormat));
33230         this.setMonth(d.format(this.monthFormat));
33231         this.setYear(d.format(this.yearFormat));
33232         
33233         this.validate();
33234         
33235         return;
33236     },
33237     
33238     setDay : function(v)
33239     {
33240         this.dayField.setValue(v);
33241         this.inputEl.dom.value = this.getValue();
33242         this.validate();
33243         return;
33244     },
33245     
33246     setMonth : function(v)
33247     {
33248         this.monthField.setValue(v, true);
33249         this.inputEl.dom.value = this.getValue();
33250         this.validate();
33251         return;
33252     },
33253     
33254     setYear : function(v)
33255     {
33256         this.yearField.setValue(v);
33257         this.inputEl.dom.value = this.getValue();
33258         this.validate();
33259         return;
33260     },
33261     
33262     getDay : function()
33263     {
33264         return this.dayField.getValue();
33265     },
33266     
33267     getMonth : function()
33268     {
33269         return this.monthField.getValue();
33270     },
33271     
33272     getYear : function()
33273     {
33274         return this.yearField.getValue();
33275     },
33276     
33277     getValue : function()
33278     {
33279         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33280         
33281         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33282         
33283         return date;
33284     },
33285     
33286     reset : function()
33287     {
33288         this.setDay('');
33289         this.setMonth('');
33290         this.setYear('');
33291         this.inputEl.dom.value = '';
33292         this.validate();
33293         return;
33294     },
33295     
33296     validate : function()
33297     {
33298         var d = this.dayField.validate();
33299         var m = this.monthField.validate();
33300         var y = this.yearField.validate();
33301         
33302         var valid = true;
33303         
33304         if(
33305                 (!this.dayAllowBlank && !d) ||
33306                 (!this.monthAllowBlank && !m) ||
33307                 (!this.yearAllowBlank && !y)
33308         ){
33309             valid = false;
33310         }
33311         
33312         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33313             return valid;
33314         }
33315         
33316         if(valid){
33317             this.markValid();
33318             return valid;
33319         }
33320         
33321         this.markInvalid();
33322         
33323         return valid;
33324     },
33325     
33326     markValid : function()
33327     {
33328         
33329         var label = this.el.select('label', true).first();
33330         var icon = this.el.select('i.fa-star', true).first();
33331
33332         if(label && icon){
33333             icon.remove();
33334         }
33335         
33336         this.fireEvent('valid', this);
33337     },
33338     
33339      /**
33340      * Mark this field as invalid
33341      * @param {String} msg The validation message
33342      */
33343     markInvalid : function(msg)
33344     {
33345         
33346         var label = this.el.select('label', true).first();
33347         var icon = this.el.select('i.fa-star', true).first();
33348
33349         if(label && !icon){
33350             this.el.select('.roo-date-split-field-label', true).createChild({
33351                 tag : 'i',
33352                 cls : 'text-danger fa fa-lg fa-star',
33353                 tooltip : 'This field is required',
33354                 style : 'margin-right:5px;'
33355             }, label, true);
33356         }
33357         
33358         this.fireEvent('invalid', this, msg);
33359     },
33360     
33361     clearInvalid : function()
33362     {
33363         var label = this.el.select('label', true).first();
33364         var icon = this.el.select('i.fa-star', true).first();
33365
33366         if(label && icon){
33367             icon.remove();
33368         }
33369         
33370         this.fireEvent('valid', this);
33371     },
33372     
33373     getName: function()
33374     {
33375         return this.name;
33376     }
33377     
33378 });
33379
33380  /**
33381  *
33382  * This is based on 
33383  * http://masonry.desandro.com
33384  *
33385  * The idea is to render all the bricks based on vertical width...
33386  *
33387  * The original code extends 'outlayer' - we might need to use that....
33388  * 
33389  */
33390
33391
33392 /**
33393  * @class Roo.bootstrap.LayoutMasonry
33394  * @extends Roo.bootstrap.Component
33395  * Bootstrap Layout Masonry class
33396  * 
33397  * @constructor
33398  * Create a new Element
33399  * @param {Object} config The config object
33400  */
33401
33402 Roo.bootstrap.LayoutMasonry = function(config){
33403     
33404     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33405     
33406     this.bricks = [];
33407     
33408     Roo.bootstrap.LayoutMasonry.register(this);
33409     
33410     this.addEvents({
33411         // raw events
33412         /**
33413          * @event layout
33414          * Fire after layout the items
33415          * @param {Roo.bootstrap.LayoutMasonry} this
33416          * @param {Roo.EventObject} e
33417          */
33418         "layout" : true
33419     });
33420     
33421 };
33422
33423 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33424     
33425     /**
33426      * @cfg {Boolean} isLayoutInstant = no animation?
33427      */   
33428     isLayoutInstant : false, // needed?
33429    
33430     /**
33431      * @cfg {Number} boxWidth  width of the columns
33432      */   
33433     boxWidth : 450,
33434     
33435       /**
33436      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33437      */   
33438     boxHeight : 0,
33439     
33440     /**
33441      * @cfg {Number} padWidth padding below box..
33442      */   
33443     padWidth : 10, 
33444     
33445     /**
33446      * @cfg {Number} gutter gutter width..
33447      */   
33448     gutter : 10,
33449     
33450      /**
33451      * @cfg {Number} maxCols maximum number of columns
33452      */   
33453     
33454     maxCols: 0,
33455     
33456     /**
33457      * @cfg {Boolean} isAutoInitial defalut true
33458      */   
33459     isAutoInitial : true, 
33460     
33461     containerWidth: 0,
33462     
33463     /**
33464      * @cfg {Boolean} isHorizontal defalut false
33465      */   
33466     isHorizontal : false, 
33467
33468     currentSize : null,
33469     
33470     tag: 'div',
33471     
33472     cls: '',
33473     
33474     bricks: null, //CompositeElement
33475     
33476     cols : 1,
33477     
33478     _isLayoutInited : false,
33479     
33480 //    isAlternative : false, // only use for vertical layout...
33481     
33482     /**
33483      * @cfg {Number} alternativePadWidth padding below box..
33484      */   
33485     alternativePadWidth : 50,
33486     
33487     selectedBrick : [],
33488     
33489     getAutoCreate : function(){
33490         
33491         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33492         
33493         var cfg = {
33494             tag: this.tag,
33495             cls: 'blog-masonary-wrapper ' + this.cls,
33496             cn : {
33497                 cls : 'mas-boxes masonary'
33498             }
33499         };
33500         
33501         return cfg;
33502     },
33503     
33504     getChildContainer: function( )
33505     {
33506         if (this.boxesEl) {
33507             return this.boxesEl;
33508         }
33509         
33510         this.boxesEl = this.el.select('.mas-boxes').first();
33511         
33512         return this.boxesEl;
33513     },
33514     
33515     
33516     initEvents : function()
33517     {
33518         var _this = this;
33519         
33520         if(this.isAutoInitial){
33521             Roo.log('hook children rendered');
33522             this.on('childrenrendered', function() {
33523                 Roo.log('children rendered');
33524                 _this.initial();
33525             } ,this);
33526         }
33527     },
33528     
33529     initial : function()
33530     {
33531         this.selectedBrick = [];
33532         
33533         this.currentSize = this.el.getBox(true);
33534         
33535         Roo.EventManager.onWindowResize(this.resize, this); 
33536
33537         if(!this.isAutoInitial){
33538             this.layout();
33539             return;
33540         }
33541         
33542         this.layout();
33543         
33544         return;
33545         //this.layout.defer(500,this);
33546         
33547     },
33548     
33549     resize : function()
33550     {
33551         var cs = this.el.getBox(true);
33552         
33553         if (
33554                 this.currentSize.width == cs.width && 
33555                 this.currentSize.x == cs.x && 
33556                 this.currentSize.height == cs.height && 
33557                 this.currentSize.y == cs.y 
33558         ) {
33559             Roo.log("no change in with or X or Y");
33560             return;
33561         }
33562         
33563         this.currentSize = cs;
33564         
33565         this.layout();
33566         
33567     },
33568     
33569     layout : function()
33570     {   
33571         this._resetLayout();
33572         
33573         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33574         
33575         this.layoutItems( isInstant );
33576       
33577         this._isLayoutInited = true;
33578         
33579         this.fireEvent('layout', this);
33580         
33581     },
33582     
33583     _resetLayout : function()
33584     {
33585         if(this.isHorizontal){
33586             this.horizontalMeasureColumns();
33587             return;
33588         }
33589         
33590         this.verticalMeasureColumns();
33591         
33592     },
33593     
33594     verticalMeasureColumns : function()
33595     {
33596         this.getContainerWidth();
33597         
33598 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33599 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33600 //            return;
33601 //        }
33602         
33603         var boxWidth = this.boxWidth + this.padWidth;
33604         
33605         if(this.containerWidth < this.boxWidth){
33606             boxWidth = this.containerWidth
33607         }
33608         
33609         var containerWidth = this.containerWidth;
33610         
33611         var cols = Math.floor(containerWidth / boxWidth);
33612         
33613         this.cols = Math.max( cols, 1 );
33614         
33615         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33616         
33617         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33618         
33619         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33620         
33621         this.colWidth = boxWidth + avail - this.padWidth;
33622         
33623         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33624         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33625     },
33626     
33627     horizontalMeasureColumns : function()
33628     {
33629         this.getContainerWidth();
33630         
33631         var boxWidth = this.boxWidth;
33632         
33633         if(this.containerWidth < boxWidth){
33634             boxWidth = this.containerWidth;
33635         }
33636         
33637         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33638         
33639         this.el.setHeight(boxWidth);
33640         
33641     },
33642     
33643     getContainerWidth : function()
33644     {
33645         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33646     },
33647     
33648     layoutItems : function( isInstant )
33649     {
33650         Roo.log(this.bricks);
33651         
33652         var items = Roo.apply([], this.bricks);
33653         
33654         if(this.isHorizontal){
33655             this._horizontalLayoutItems( items , isInstant );
33656             return;
33657         }
33658         
33659 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33660 //            this._verticalAlternativeLayoutItems( items , isInstant );
33661 //            return;
33662 //        }
33663         
33664         this._verticalLayoutItems( items , isInstant );
33665         
33666     },
33667     
33668     _verticalLayoutItems : function ( items , isInstant)
33669     {
33670         if ( !items || !items.length ) {
33671             return;
33672         }
33673         
33674         var standard = [
33675             ['xs', 'xs', 'xs', 'tall'],
33676             ['xs', 'xs', 'tall'],
33677             ['xs', 'xs', 'sm'],
33678             ['xs', 'xs', 'xs'],
33679             ['xs', 'tall'],
33680             ['xs', 'sm'],
33681             ['xs', 'xs'],
33682             ['xs'],
33683             
33684             ['sm', 'xs', 'xs'],
33685             ['sm', 'xs'],
33686             ['sm'],
33687             
33688             ['tall', 'xs', 'xs', 'xs'],
33689             ['tall', 'xs', 'xs'],
33690             ['tall', 'xs'],
33691             ['tall']
33692             
33693         ];
33694         
33695         var queue = [];
33696         
33697         var boxes = [];
33698         
33699         var box = [];
33700         
33701         Roo.each(items, function(item, k){
33702             
33703             switch (item.size) {
33704                 // these layouts take up a full box,
33705                 case 'md' :
33706                 case 'md-left' :
33707                 case 'md-right' :
33708                 case 'wide' :
33709                     
33710                     if(box.length){
33711                         boxes.push(box);
33712                         box = [];
33713                     }
33714                     
33715                     boxes.push([item]);
33716                     
33717                     break;
33718                     
33719                 case 'xs' :
33720                 case 'sm' :
33721                 case 'tall' :
33722                     
33723                     box.push(item);
33724                     
33725                     break;
33726                 default :
33727                     break;
33728                     
33729             }
33730             
33731         }, this);
33732         
33733         if(box.length){
33734             boxes.push(box);
33735             box = [];
33736         }
33737         
33738         var filterPattern = function(box, length)
33739         {
33740             if(!box.length){
33741                 return;
33742             }
33743             
33744             var match = false;
33745             
33746             var pattern = box.slice(0, length);
33747             
33748             var format = [];
33749             
33750             Roo.each(pattern, function(i){
33751                 format.push(i.size);
33752             }, this);
33753             
33754             Roo.each(standard, function(s){
33755                 
33756                 if(String(s) != String(format)){
33757                     return;
33758                 }
33759                 
33760                 match = true;
33761                 return false;
33762                 
33763             }, this);
33764             
33765             if(!match && length == 1){
33766                 return;
33767             }
33768             
33769             if(!match){
33770                 filterPattern(box, length - 1);
33771                 return;
33772             }
33773                 
33774             queue.push(pattern);
33775
33776             box = box.slice(length, box.length);
33777
33778             filterPattern(box, 4);
33779
33780             return;
33781             
33782         }
33783         
33784         Roo.each(boxes, function(box, k){
33785             
33786             if(!box.length){
33787                 return;
33788             }
33789             
33790             if(box.length == 1){
33791                 queue.push(box);
33792                 return;
33793             }
33794             
33795             filterPattern(box, 4);
33796             
33797         }, this);
33798         
33799         this._processVerticalLayoutQueue( queue, isInstant );
33800         
33801     },
33802     
33803 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33804 //    {
33805 //        if ( !items || !items.length ) {
33806 //            return;
33807 //        }
33808 //
33809 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33810 //        
33811 //    },
33812     
33813     _horizontalLayoutItems : function ( items , isInstant)
33814     {
33815         if ( !items || !items.length || items.length < 3) {
33816             return;
33817         }
33818         
33819         items.reverse();
33820         
33821         var eItems = items.slice(0, 3);
33822         
33823         items = items.slice(3, items.length);
33824         
33825         var standard = [
33826             ['xs', 'xs', 'xs', 'wide'],
33827             ['xs', 'xs', 'wide'],
33828             ['xs', 'xs', 'sm'],
33829             ['xs', 'xs', 'xs'],
33830             ['xs', 'wide'],
33831             ['xs', 'sm'],
33832             ['xs', 'xs'],
33833             ['xs'],
33834             
33835             ['sm', 'xs', 'xs'],
33836             ['sm', 'xs'],
33837             ['sm'],
33838             
33839             ['wide', 'xs', 'xs', 'xs'],
33840             ['wide', 'xs', 'xs'],
33841             ['wide', 'xs'],
33842             ['wide'],
33843             
33844             ['wide-thin']
33845         ];
33846         
33847         var queue = [];
33848         
33849         var boxes = [];
33850         
33851         var box = [];
33852         
33853         Roo.each(items, function(item, k){
33854             
33855             switch (item.size) {
33856                 case 'md' :
33857                 case 'md-left' :
33858                 case 'md-right' :
33859                 case 'tall' :
33860                     
33861                     if(box.length){
33862                         boxes.push(box);
33863                         box = [];
33864                     }
33865                     
33866                     boxes.push([item]);
33867                     
33868                     break;
33869                     
33870                 case 'xs' :
33871                 case 'sm' :
33872                 case 'wide' :
33873                 case 'wide-thin' :
33874                     
33875                     box.push(item);
33876                     
33877                     break;
33878                 default :
33879                     break;
33880                     
33881             }
33882             
33883         }, this);
33884         
33885         if(box.length){
33886             boxes.push(box);
33887             box = [];
33888         }
33889         
33890         var filterPattern = function(box, length)
33891         {
33892             if(!box.length){
33893                 return;
33894             }
33895             
33896             var match = false;
33897             
33898             var pattern = box.slice(0, length);
33899             
33900             var format = [];
33901             
33902             Roo.each(pattern, function(i){
33903                 format.push(i.size);
33904             }, this);
33905             
33906             Roo.each(standard, function(s){
33907                 
33908                 if(String(s) != String(format)){
33909                     return;
33910                 }
33911                 
33912                 match = true;
33913                 return false;
33914                 
33915             }, this);
33916             
33917             if(!match && length == 1){
33918                 return;
33919             }
33920             
33921             if(!match){
33922                 filterPattern(box, length - 1);
33923                 return;
33924             }
33925                 
33926             queue.push(pattern);
33927
33928             box = box.slice(length, box.length);
33929
33930             filterPattern(box, 4);
33931
33932             return;
33933             
33934         }
33935         
33936         Roo.each(boxes, function(box, k){
33937             
33938             if(!box.length){
33939                 return;
33940             }
33941             
33942             if(box.length == 1){
33943                 queue.push(box);
33944                 return;
33945             }
33946             
33947             filterPattern(box, 4);
33948             
33949         }, this);
33950         
33951         
33952         var prune = [];
33953         
33954         var pos = this.el.getBox(true);
33955         
33956         var minX = pos.x;
33957         
33958         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33959         
33960         var hit_end = false;
33961         
33962         Roo.each(queue, function(box){
33963             
33964             if(hit_end){
33965                 
33966                 Roo.each(box, function(b){
33967                 
33968                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33969                     b.el.hide();
33970
33971                 }, this);
33972
33973                 return;
33974             }
33975             
33976             var mx = 0;
33977             
33978             Roo.each(box, function(b){
33979                 
33980                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33981                 b.el.show();
33982
33983                 mx = Math.max(mx, b.x);
33984                 
33985             }, this);
33986             
33987             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33988             
33989             if(maxX < minX){
33990                 
33991                 Roo.each(box, function(b){
33992                 
33993                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33994                     b.el.hide();
33995                     
33996                 }, this);
33997                 
33998                 hit_end = true;
33999                 
34000                 return;
34001             }
34002             
34003             prune.push(box);
34004             
34005         }, this);
34006         
34007         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34008     },
34009     
34010     /** Sets position of item in DOM
34011     * @param {Element} item
34012     * @param {Number} x - horizontal position
34013     * @param {Number} y - vertical position
34014     * @param {Boolean} isInstant - disables transitions
34015     */
34016     _processVerticalLayoutQueue : function( queue, isInstant )
34017     {
34018         var pos = this.el.getBox(true);
34019         var x = pos.x;
34020         var y = pos.y;
34021         var maxY = [];
34022         
34023         for (var i = 0; i < this.cols; i++){
34024             maxY[i] = pos.y;
34025         }
34026         
34027         Roo.each(queue, function(box, k){
34028             
34029             var col = k % this.cols;
34030             
34031             Roo.each(box, function(b,kk){
34032                 
34033                 b.el.position('absolute');
34034                 
34035                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34036                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34037                 
34038                 if(b.size == 'md-left' || b.size == 'md-right'){
34039                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34040                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34041                 }
34042                 
34043                 b.el.setWidth(width);
34044                 b.el.setHeight(height);
34045                 // iframe?
34046                 b.el.select('iframe',true).setSize(width,height);
34047                 
34048             }, this);
34049             
34050             for (var i = 0; i < this.cols; i++){
34051                 
34052                 if(maxY[i] < maxY[col]){
34053                     col = i;
34054                     continue;
34055                 }
34056                 
34057                 col = Math.min(col, i);
34058                 
34059             }
34060             
34061             x = pos.x + col * (this.colWidth + this.padWidth);
34062             
34063             y = maxY[col];
34064             
34065             var positions = [];
34066             
34067             switch (box.length){
34068                 case 1 :
34069                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34070                     break;
34071                 case 2 :
34072                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34073                     break;
34074                 case 3 :
34075                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34076                     break;
34077                 case 4 :
34078                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34079                     break;
34080                 default :
34081                     break;
34082             }
34083             
34084             Roo.each(box, function(b,kk){
34085                 
34086                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34087                 
34088                 var sz = b.el.getSize();
34089                 
34090                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34091                 
34092             }, this);
34093             
34094         }, this);
34095         
34096         var mY = 0;
34097         
34098         for (var i = 0; i < this.cols; i++){
34099             mY = Math.max(mY, maxY[i]);
34100         }
34101         
34102         this.el.setHeight(mY - pos.y);
34103         
34104     },
34105     
34106 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34107 //    {
34108 //        var pos = this.el.getBox(true);
34109 //        var x = pos.x;
34110 //        var y = pos.y;
34111 //        var maxX = pos.right;
34112 //        
34113 //        var maxHeight = 0;
34114 //        
34115 //        Roo.each(items, function(item, k){
34116 //            
34117 //            var c = k % 2;
34118 //            
34119 //            item.el.position('absolute');
34120 //                
34121 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34122 //
34123 //            item.el.setWidth(width);
34124 //
34125 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34126 //
34127 //            item.el.setHeight(height);
34128 //            
34129 //            if(c == 0){
34130 //                item.el.setXY([x, y], isInstant ? false : true);
34131 //            } else {
34132 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34133 //            }
34134 //            
34135 //            y = y + height + this.alternativePadWidth;
34136 //            
34137 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34138 //            
34139 //        }, this);
34140 //        
34141 //        this.el.setHeight(maxHeight);
34142 //        
34143 //    },
34144     
34145     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34146     {
34147         var pos = this.el.getBox(true);
34148         
34149         var minX = pos.x;
34150         var minY = pos.y;
34151         
34152         var maxX = pos.right;
34153         
34154         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34155         
34156         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34157         
34158         Roo.each(queue, function(box, k){
34159             
34160             Roo.each(box, function(b, kk){
34161                 
34162                 b.el.position('absolute');
34163                 
34164                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34165                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34166                 
34167                 if(b.size == 'md-left' || b.size == 'md-right'){
34168                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34169                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34170                 }
34171                 
34172                 b.el.setWidth(width);
34173                 b.el.setHeight(height);
34174                 
34175             }, this);
34176             
34177             if(!box.length){
34178                 return;
34179             }
34180             
34181             var positions = [];
34182             
34183             switch (box.length){
34184                 case 1 :
34185                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34186                     break;
34187                 case 2 :
34188                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34189                     break;
34190                 case 3 :
34191                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34192                     break;
34193                 case 4 :
34194                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34195                     break;
34196                 default :
34197                     break;
34198             }
34199             
34200             Roo.each(box, function(b,kk){
34201                 
34202                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34203                 
34204                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34205                 
34206             }, this);
34207             
34208         }, this);
34209         
34210     },
34211     
34212     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34213     {
34214         Roo.each(eItems, function(b,k){
34215             
34216             b.size = (k == 0) ? 'sm' : 'xs';
34217             b.x = (k == 0) ? 2 : 1;
34218             b.y = (k == 0) ? 2 : 1;
34219             
34220             b.el.position('absolute');
34221             
34222             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34223                 
34224             b.el.setWidth(width);
34225             
34226             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34227             
34228             b.el.setHeight(height);
34229             
34230         }, this);
34231
34232         var positions = [];
34233         
34234         positions.push({
34235             x : maxX - this.unitWidth * 2 - this.gutter,
34236             y : minY
34237         });
34238         
34239         positions.push({
34240             x : maxX - this.unitWidth,
34241             y : minY + (this.unitWidth + this.gutter) * 2
34242         });
34243         
34244         positions.push({
34245             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34246             y : minY
34247         });
34248         
34249         Roo.each(eItems, function(b,k){
34250             
34251             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34252
34253         }, this);
34254         
34255     },
34256     
34257     getVerticalOneBoxColPositions : function(x, y, box)
34258     {
34259         var pos = [];
34260         
34261         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34262         
34263         if(box[0].size == 'md-left'){
34264             rand = 0;
34265         }
34266         
34267         if(box[0].size == 'md-right'){
34268             rand = 1;
34269         }
34270         
34271         pos.push({
34272             x : x + (this.unitWidth + this.gutter) * rand,
34273             y : y
34274         });
34275         
34276         return pos;
34277     },
34278     
34279     getVerticalTwoBoxColPositions : function(x, y, box)
34280     {
34281         var pos = [];
34282         
34283         if(box[0].size == 'xs'){
34284             
34285             pos.push({
34286                 x : x,
34287                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34288             });
34289
34290             pos.push({
34291                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34292                 y : y
34293             });
34294             
34295             return pos;
34296             
34297         }
34298         
34299         pos.push({
34300             x : x,
34301             y : y
34302         });
34303
34304         pos.push({
34305             x : x + (this.unitWidth + this.gutter) * 2,
34306             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34307         });
34308         
34309         return pos;
34310         
34311     },
34312     
34313     getVerticalThreeBoxColPositions : function(x, y, box)
34314     {
34315         var pos = [];
34316         
34317         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34318             
34319             pos.push({
34320                 x : x,
34321                 y : y
34322             });
34323
34324             pos.push({
34325                 x : x + (this.unitWidth + this.gutter) * 1,
34326                 y : y
34327             });
34328             
34329             pos.push({
34330                 x : x + (this.unitWidth + this.gutter) * 2,
34331                 y : y
34332             });
34333             
34334             return pos;
34335             
34336         }
34337         
34338         if(box[0].size == 'xs' && box[1].size == 'xs'){
34339             
34340             pos.push({
34341                 x : x,
34342                 y : y
34343             });
34344
34345             pos.push({
34346                 x : x,
34347                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34348             });
34349             
34350             pos.push({
34351                 x : x + (this.unitWidth + this.gutter) * 1,
34352                 y : y
34353             });
34354             
34355             return pos;
34356             
34357         }
34358         
34359         pos.push({
34360             x : x,
34361             y : y
34362         });
34363
34364         pos.push({
34365             x : x + (this.unitWidth + this.gutter) * 2,
34366             y : y
34367         });
34368
34369         pos.push({
34370             x : x + (this.unitWidth + this.gutter) * 2,
34371             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34372         });
34373             
34374         return pos;
34375         
34376     },
34377     
34378     getVerticalFourBoxColPositions : function(x, y, box)
34379     {
34380         var pos = [];
34381         
34382         if(box[0].size == 'xs'){
34383             
34384             pos.push({
34385                 x : x,
34386                 y : y
34387             });
34388
34389             pos.push({
34390                 x : x,
34391                 y : y + (this.unitHeight + this.gutter) * 1
34392             });
34393             
34394             pos.push({
34395                 x : x,
34396                 y : y + (this.unitHeight + this.gutter) * 2
34397             });
34398             
34399             pos.push({
34400                 x : x + (this.unitWidth + this.gutter) * 1,
34401                 y : y
34402             });
34403             
34404             return pos;
34405             
34406         }
34407         
34408         pos.push({
34409             x : x,
34410             y : y
34411         });
34412
34413         pos.push({
34414             x : x + (this.unitWidth + this.gutter) * 2,
34415             y : y
34416         });
34417
34418         pos.push({
34419             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34420             y : y + (this.unitHeight + this.gutter) * 1
34421         });
34422
34423         pos.push({
34424             x : x + (this.unitWidth + this.gutter) * 2,
34425             y : y + (this.unitWidth + this.gutter) * 2
34426         });
34427
34428         return pos;
34429         
34430     },
34431     
34432     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34433     {
34434         var pos = [];
34435         
34436         if(box[0].size == 'md-left'){
34437             pos.push({
34438                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34439                 y : minY
34440             });
34441             
34442             return pos;
34443         }
34444         
34445         if(box[0].size == 'md-right'){
34446             pos.push({
34447                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34448                 y : minY + (this.unitWidth + this.gutter) * 1
34449             });
34450             
34451             return pos;
34452         }
34453         
34454         var rand = Math.floor(Math.random() * (4 - box[0].y));
34455         
34456         pos.push({
34457             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34458             y : minY + (this.unitWidth + this.gutter) * rand
34459         });
34460         
34461         return pos;
34462         
34463     },
34464     
34465     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34466     {
34467         var pos = [];
34468         
34469         if(box[0].size == 'xs'){
34470             
34471             pos.push({
34472                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34473                 y : minY
34474             });
34475
34476             pos.push({
34477                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34478                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34479             });
34480             
34481             return pos;
34482             
34483         }
34484         
34485         pos.push({
34486             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34487             y : minY
34488         });
34489
34490         pos.push({
34491             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34492             y : minY + (this.unitWidth + this.gutter) * 2
34493         });
34494         
34495         return pos;
34496         
34497     },
34498     
34499     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34500     {
34501         var pos = [];
34502         
34503         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34504             
34505             pos.push({
34506                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34507                 y : minY
34508             });
34509
34510             pos.push({
34511                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34512                 y : minY + (this.unitWidth + this.gutter) * 1
34513             });
34514             
34515             pos.push({
34516                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34517                 y : minY + (this.unitWidth + this.gutter) * 2
34518             });
34519             
34520             return pos;
34521             
34522         }
34523         
34524         if(box[0].size == 'xs' && box[1].size == 'xs'){
34525             
34526             pos.push({
34527                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34528                 y : minY
34529             });
34530
34531             pos.push({
34532                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34533                 y : minY
34534             });
34535             
34536             pos.push({
34537                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34538                 y : minY + (this.unitWidth + this.gutter) * 1
34539             });
34540             
34541             return pos;
34542             
34543         }
34544         
34545         pos.push({
34546             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34547             y : minY
34548         });
34549
34550         pos.push({
34551             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34552             y : minY + (this.unitWidth + this.gutter) * 2
34553         });
34554
34555         pos.push({
34556             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34557             y : minY + (this.unitWidth + this.gutter) * 2
34558         });
34559             
34560         return pos;
34561         
34562     },
34563     
34564     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34565     {
34566         var pos = [];
34567         
34568         if(box[0].size == 'xs'){
34569             
34570             pos.push({
34571                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34572                 y : minY
34573             });
34574
34575             pos.push({
34576                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34577                 y : minY
34578             });
34579             
34580             pos.push({
34581                 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),
34582                 y : minY
34583             });
34584             
34585             pos.push({
34586                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34587                 y : minY + (this.unitWidth + this.gutter) * 1
34588             });
34589             
34590             return pos;
34591             
34592         }
34593         
34594         pos.push({
34595             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34596             y : minY
34597         });
34598         
34599         pos.push({
34600             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34601             y : minY + (this.unitWidth + this.gutter) * 2
34602         });
34603         
34604         pos.push({
34605             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34606             y : minY + (this.unitWidth + this.gutter) * 2
34607         });
34608         
34609         pos.push({
34610             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),
34611             y : minY + (this.unitWidth + this.gutter) * 2
34612         });
34613
34614         return pos;
34615         
34616     },
34617     
34618     /**
34619     * remove a Masonry Brick
34620     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34621     */
34622     removeBrick : function(brick_id)
34623     {
34624         if (!brick_id) {
34625             return;
34626         }
34627         
34628         for (var i = 0; i<this.bricks.length; i++) {
34629             if (this.bricks[i].id == brick_id) {
34630                 this.bricks.splice(i,1);
34631                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34632                 this.initial();
34633             }
34634         }
34635     },
34636     
34637     /**
34638     * adds a Masonry Brick
34639     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34640     */
34641     addBrick : function(cfg)
34642     {
34643         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34644         //this.register(cn);
34645         cn.parentId = this.id;
34646         cn.render(this.el);
34647         return cn;
34648     },
34649     
34650     /**
34651     * register a Masonry Brick
34652     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34653     */
34654     
34655     register : function(brick)
34656     {
34657         this.bricks.push(brick);
34658         brick.masonryId = this.id;
34659     },
34660     
34661     /**
34662     * clear all the Masonry Brick
34663     */
34664     clearAll : function()
34665     {
34666         this.bricks = [];
34667         //this.getChildContainer().dom.innerHTML = "";
34668         this.el.dom.innerHTML = '';
34669     },
34670     
34671     getSelected : function()
34672     {
34673         if (!this.selectedBrick) {
34674             return false;
34675         }
34676         
34677         return this.selectedBrick;
34678     }
34679 });
34680
34681 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34682     
34683     groups: {},
34684      /**
34685     * register a Masonry Layout
34686     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34687     */
34688     
34689     register : function(layout)
34690     {
34691         this.groups[layout.id] = layout;
34692     },
34693     /**
34694     * fetch a  Masonry Layout based on the masonry layout ID
34695     * @param {string} the masonry layout to add
34696     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34697     */
34698     
34699     get: function(layout_id) {
34700         if (typeof(this.groups[layout_id]) == 'undefined') {
34701             return false;
34702         }
34703         return this.groups[layout_id] ;
34704     }
34705     
34706     
34707     
34708 });
34709
34710  
34711
34712  /**
34713  *
34714  * This is based on 
34715  * http://masonry.desandro.com
34716  *
34717  * The idea is to render all the bricks based on vertical width...
34718  *
34719  * The original code extends 'outlayer' - we might need to use that....
34720  * 
34721  */
34722
34723
34724 /**
34725  * @class Roo.bootstrap.LayoutMasonryAuto
34726  * @extends Roo.bootstrap.Component
34727  * Bootstrap Layout Masonry class
34728  * 
34729  * @constructor
34730  * Create a new Element
34731  * @param {Object} config The config object
34732  */
34733
34734 Roo.bootstrap.LayoutMasonryAuto = function(config){
34735     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34736 };
34737
34738 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34739     
34740       /**
34741      * @cfg {Boolean} isFitWidth  - resize the width..
34742      */   
34743     isFitWidth : false,  // options..
34744     /**
34745      * @cfg {Boolean} isOriginLeft = left align?
34746      */   
34747     isOriginLeft : true,
34748     /**
34749      * @cfg {Boolean} isOriginTop = top align?
34750      */   
34751     isOriginTop : false,
34752     /**
34753      * @cfg {Boolean} isLayoutInstant = no animation?
34754      */   
34755     isLayoutInstant : false, // needed?
34756     /**
34757      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34758      */   
34759     isResizingContainer : true,
34760     /**
34761      * @cfg {Number} columnWidth  width of the columns 
34762      */   
34763     
34764     columnWidth : 0,
34765     
34766     /**
34767      * @cfg {Number} maxCols maximum number of columns
34768      */   
34769     
34770     maxCols: 0,
34771     /**
34772      * @cfg {Number} padHeight padding below box..
34773      */   
34774     
34775     padHeight : 10, 
34776     
34777     /**
34778      * @cfg {Boolean} isAutoInitial defalut true
34779      */   
34780     
34781     isAutoInitial : true, 
34782     
34783     // private?
34784     gutter : 0,
34785     
34786     containerWidth: 0,
34787     initialColumnWidth : 0,
34788     currentSize : null,
34789     
34790     colYs : null, // array.
34791     maxY : 0,
34792     padWidth: 10,
34793     
34794     
34795     tag: 'div',
34796     cls: '',
34797     bricks: null, //CompositeElement
34798     cols : 0, // array?
34799     // element : null, // wrapped now this.el
34800     _isLayoutInited : null, 
34801     
34802     
34803     getAutoCreate : function(){
34804         
34805         var cfg = {
34806             tag: this.tag,
34807             cls: 'blog-masonary-wrapper ' + this.cls,
34808             cn : {
34809                 cls : 'mas-boxes masonary'
34810             }
34811         };
34812         
34813         return cfg;
34814     },
34815     
34816     getChildContainer: function( )
34817     {
34818         if (this.boxesEl) {
34819             return this.boxesEl;
34820         }
34821         
34822         this.boxesEl = this.el.select('.mas-boxes').first();
34823         
34824         return this.boxesEl;
34825     },
34826     
34827     
34828     initEvents : function()
34829     {
34830         var _this = this;
34831         
34832         if(this.isAutoInitial){
34833             Roo.log('hook children rendered');
34834             this.on('childrenrendered', function() {
34835                 Roo.log('children rendered');
34836                 _this.initial();
34837             } ,this);
34838         }
34839         
34840     },
34841     
34842     initial : function()
34843     {
34844         this.reloadItems();
34845
34846         this.currentSize = this.el.getBox(true);
34847
34848         /// was window resize... - let's see if this works..
34849         Roo.EventManager.onWindowResize(this.resize, this); 
34850
34851         if(!this.isAutoInitial){
34852             this.layout();
34853             return;
34854         }
34855         
34856         this.layout.defer(500,this);
34857     },
34858     
34859     reloadItems: function()
34860     {
34861         this.bricks = this.el.select('.masonry-brick', true);
34862         
34863         this.bricks.each(function(b) {
34864             //Roo.log(b.getSize());
34865             if (!b.attr('originalwidth')) {
34866                 b.attr('originalwidth',  b.getSize().width);
34867             }
34868             
34869         });
34870         
34871         Roo.log(this.bricks.elements.length);
34872     },
34873     
34874     resize : function()
34875     {
34876         Roo.log('resize');
34877         var cs = this.el.getBox(true);
34878         
34879         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34880             Roo.log("no change in with or X");
34881             return;
34882         }
34883         this.currentSize = cs;
34884         this.layout();
34885     },
34886     
34887     layout : function()
34888     {
34889          Roo.log('layout');
34890         this._resetLayout();
34891         //this._manageStamps();
34892       
34893         // don't animate first layout
34894         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34895         this.layoutItems( isInstant );
34896       
34897         // flag for initalized
34898         this._isLayoutInited = true;
34899     },
34900     
34901     layoutItems : function( isInstant )
34902     {
34903         //var items = this._getItemsForLayout( this.items );
34904         // original code supports filtering layout items.. we just ignore it..
34905         
34906         this._layoutItems( this.bricks , isInstant );
34907       
34908         this._postLayout();
34909     },
34910     _layoutItems : function ( items , isInstant)
34911     {
34912        //this.fireEvent( 'layout', this, items );
34913     
34914
34915         if ( !items || !items.elements.length ) {
34916           // no items, emit event with empty array
34917             return;
34918         }
34919
34920         var queue = [];
34921         items.each(function(item) {
34922             Roo.log("layout item");
34923             Roo.log(item);
34924             // get x/y object from method
34925             var position = this._getItemLayoutPosition( item );
34926             // enqueue
34927             position.item = item;
34928             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34929             queue.push( position );
34930         }, this);
34931       
34932         this._processLayoutQueue( queue );
34933     },
34934     /** Sets position of item in DOM
34935     * @param {Element} item
34936     * @param {Number} x - horizontal position
34937     * @param {Number} y - vertical position
34938     * @param {Boolean} isInstant - disables transitions
34939     */
34940     _processLayoutQueue : function( queue )
34941     {
34942         for ( var i=0, len = queue.length; i < len; i++ ) {
34943             var obj = queue[i];
34944             obj.item.position('absolute');
34945             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34946         }
34947     },
34948       
34949     
34950     /**
34951     * Any logic you want to do after each layout,
34952     * i.e. size the container
34953     */
34954     _postLayout : function()
34955     {
34956         this.resizeContainer();
34957     },
34958     
34959     resizeContainer : function()
34960     {
34961         if ( !this.isResizingContainer ) {
34962             return;
34963         }
34964         var size = this._getContainerSize();
34965         if ( size ) {
34966             this.el.setSize(size.width,size.height);
34967             this.boxesEl.setSize(size.width,size.height);
34968         }
34969     },
34970     
34971     
34972     
34973     _resetLayout : function()
34974     {
34975         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34976         this.colWidth = this.el.getWidth();
34977         //this.gutter = this.el.getWidth(); 
34978         
34979         this.measureColumns();
34980
34981         // reset column Y
34982         var i = this.cols;
34983         this.colYs = [];
34984         while (i--) {
34985             this.colYs.push( 0 );
34986         }
34987     
34988         this.maxY = 0;
34989     },
34990
34991     measureColumns : function()
34992     {
34993         this.getContainerWidth();
34994       // if columnWidth is 0, default to outerWidth of first item
34995         if ( !this.columnWidth ) {
34996             var firstItem = this.bricks.first();
34997             Roo.log(firstItem);
34998             this.columnWidth  = this.containerWidth;
34999             if (firstItem && firstItem.attr('originalwidth') ) {
35000                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35001             }
35002             // columnWidth fall back to item of first element
35003             Roo.log("set column width?");
35004                         this.initialColumnWidth = this.columnWidth  ;
35005
35006             // if first elem has no width, default to size of container
35007             
35008         }
35009         
35010         
35011         if (this.initialColumnWidth) {
35012             this.columnWidth = this.initialColumnWidth;
35013         }
35014         
35015         
35016             
35017         // column width is fixed at the top - however if container width get's smaller we should
35018         // reduce it...
35019         
35020         // this bit calcs how man columns..
35021             
35022         var columnWidth = this.columnWidth += this.gutter;
35023       
35024         // calculate columns
35025         var containerWidth = this.containerWidth + this.gutter;
35026         
35027         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35028         // fix rounding errors, typically with gutters
35029         var excess = columnWidth - containerWidth % columnWidth;
35030         
35031         
35032         // if overshoot is less than a pixel, round up, otherwise floor it
35033         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35034         cols = Math[ mathMethod ]( cols );
35035         this.cols = Math.max( cols, 1 );
35036         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35037         
35038          // padding positioning..
35039         var totalColWidth = this.cols * this.columnWidth;
35040         var padavail = this.containerWidth - totalColWidth;
35041         // so for 2 columns - we need 3 'pads'
35042         
35043         var padNeeded = (1+this.cols) * this.padWidth;
35044         
35045         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35046         
35047         this.columnWidth += padExtra
35048         //this.padWidth = Math.floor(padavail /  ( this.cols));
35049         
35050         // adjust colum width so that padding is fixed??
35051         
35052         // we have 3 columns ... total = width * 3
35053         // we have X left over... that should be used by 
35054         
35055         //if (this.expandC) {
35056             
35057         //}
35058         
35059         
35060         
35061     },
35062     
35063     getContainerWidth : function()
35064     {
35065        /* // container is parent if fit width
35066         var container = this.isFitWidth ? this.element.parentNode : this.element;
35067         // check that this.size and size are there
35068         // IE8 triggers resize on body size change, so they might not be
35069         
35070         var size = getSize( container );  //FIXME
35071         this.containerWidth = size && size.innerWidth; //FIXME
35072         */
35073          
35074         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35075         
35076     },
35077     
35078     _getItemLayoutPosition : function( item )  // what is item?
35079     {
35080         // we resize the item to our columnWidth..
35081       
35082         item.setWidth(this.columnWidth);
35083         item.autoBoxAdjust  = false;
35084         
35085         var sz = item.getSize();
35086  
35087         // how many columns does this brick span
35088         var remainder = this.containerWidth % this.columnWidth;
35089         
35090         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35091         // round if off by 1 pixel, otherwise use ceil
35092         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35093         colSpan = Math.min( colSpan, this.cols );
35094         
35095         // normally this should be '1' as we dont' currently allow multi width columns..
35096         
35097         var colGroup = this._getColGroup( colSpan );
35098         // get the minimum Y value from the columns
35099         var minimumY = Math.min.apply( Math, colGroup );
35100         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35101         
35102         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35103          
35104         // position the brick
35105         var position = {
35106             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35107             y: this.currentSize.y + minimumY + this.padHeight
35108         };
35109         
35110         Roo.log(position);
35111         // apply setHeight to necessary columns
35112         var setHeight = minimumY + sz.height + this.padHeight;
35113         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35114         
35115         var setSpan = this.cols + 1 - colGroup.length;
35116         for ( var i = 0; i < setSpan; i++ ) {
35117           this.colYs[ shortColIndex + i ] = setHeight ;
35118         }
35119       
35120         return position;
35121     },
35122     
35123     /**
35124      * @param {Number} colSpan - number of columns the element spans
35125      * @returns {Array} colGroup
35126      */
35127     _getColGroup : function( colSpan )
35128     {
35129         if ( colSpan < 2 ) {
35130           // if brick spans only one column, use all the column Ys
35131           return this.colYs;
35132         }
35133       
35134         var colGroup = [];
35135         // how many different places could this brick fit horizontally
35136         var groupCount = this.cols + 1 - colSpan;
35137         // for each group potential horizontal position
35138         for ( var i = 0; i < groupCount; i++ ) {
35139           // make an array of colY values for that one group
35140           var groupColYs = this.colYs.slice( i, i + colSpan );
35141           // and get the max value of the array
35142           colGroup[i] = Math.max.apply( Math, groupColYs );
35143         }
35144         return colGroup;
35145     },
35146     /*
35147     _manageStamp : function( stamp )
35148     {
35149         var stampSize =  stamp.getSize();
35150         var offset = stamp.getBox();
35151         // get the columns that this stamp affects
35152         var firstX = this.isOriginLeft ? offset.x : offset.right;
35153         var lastX = firstX + stampSize.width;
35154         var firstCol = Math.floor( firstX / this.columnWidth );
35155         firstCol = Math.max( 0, firstCol );
35156         
35157         var lastCol = Math.floor( lastX / this.columnWidth );
35158         // lastCol should not go over if multiple of columnWidth #425
35159         lastCol -= lastX % this.columnWidth ? 0 : 1;
35160         lastCol = Math.min( this.cols - 1, lastCol );
35161         
35162         // set colYs to bottom of the stamp
35163         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35164             stampSize.height;
35165             
35166         for ( var i = firstCol; i <= lastCol; i++ ) {
35167           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35168         }
35169     },
35170     */
35171     
35172     _getContainerSize : function()
35173     {
35174         this.maxY = Math.max.apply( Math, this.colYs );
35175         var size = {
35176             height: this.maxY
35177         };
35178       
35179         if ( this.isFitWidth ) {
35180             size.width = this._getContainerFitWidth();
35181         }
35182       
35183         return size;
35184     },
35185     
35186     _getContainerFitWidth : function()
35187     {
35188         var unusedCols = 0;
35189         // count unused columns
35190         var i = this.cols;
35191         while ( --i ) {
35192           if ( this.colYs[i] !== 0 ) {
35193             break;
35194           }
35195           unusedCols++;
35196         }
35197         // fit container to columns that have been used
35198         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35199     },
35200     
35201     needsResizeLayout : function()
35202     {
35203         var previousWidth = this.containerWidth;
35204         this.getContainerWidth();
35205         return previousWidth !== this.containerWidth;
35206     }
35207  
35208 });
35209
35210  
35211
35212  /*
35213  * - LGPL
35214  *
35215  * element
35216  * 
35217  */
35218
35219 /**
35220  * @class Roo.bootstrap.MasonryBrick
35221  * @extends Roo.bootstrap.Component
35222  * Bootstrap MasonryBrick class
35223  * 
35224  * @constructor
35225  * Create a new MasonryBrick
35226  * @param {Object} config The config object
35227  */
35228
35229 Roo.bootstrap.MasonryBrick = function(config){
35230     
35231     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35232     
35233     Roo.bootstrap.MasonryBrick.register(this);
35234     
35235     this.addEvents({
35236         // raw events
35237         /**
35238          * @event click
35239          * When a MasonryBrick is clcik
35240          * @param {Roo.bootstrap.MasonryBrick} this
35241          * @param {Roo.EventObject} e
35242          */
35243         "click" : true
35244     });
35245 };
35246
35247 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35248     
35249     /**
35250      * @cfg {String} title
35251      */   
35252     title : '',
35253     /**
35254      * @cfg {String} html
35255      */   
35256     html : '',
35257     /**
35258      * @cfg {String} bgimage
35259      */   
35260     bgimage : '',
35261     /**
35262      * @cfg {String} videourl
35263      */   
35264     videourl : '',
35265     /**
35266      * @cfg {String} cls
35267      */   
35268     cls : '',
35269     /**
35270      * @cfg {String} href
35271      */   
35272     href : '',
35273     /**
35274      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35275      */   
35276     size : 'xs',
35277     
35278     /**
35279      * @cfg {String} placetitle (center|bottom)
35280      */   
35281     placetitle : '',
35282     
35283     /**
35284      * @cfg {Boolean} isFitContainer defalut true
35285      */   
35286     isFitContainer : true, 
35287     
35288     /**
35289      * @cfg {Boolean} preventDefault defalut false
35290      */   
35291     preventDefault : false, 
35292     
35293     /**
35294      * @cfg {Boolean} inverse defalut false
35295      */   
35296     maskInverse : false, 
35297     
35298     getAutoCreate : function()
35299     {
35300         if(!this.isFitContainer){
35301             return this.getSplitAutoCreate();
35302         }
35303         
35304         var cls = 'masonry-brick masonry-brick-full';
35305         
35306         if(this.href.length){
35307             cls += ' masonry-brick-link';
35308         }
35309         
35310         if(this.bgimage.length){
35311             cls += ' masonry-brick-image';
35312         }
35313         
35314         if(this.maskInverse){
35315             cls += ' mask-inverse';
35316         }
35317         
35318         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35319             cls += ' enable-mask';
35320         }
35321         
35322         if(this.size){
35323             cls += ' masonry-' + this.size + '-brick';
35324         }
35325         
35326         if(this.placetitle.length){
35327             
35328             switch (this.placetitle) {
35329                 case 'center' :
35330                     cls += ' masonry-center-title';
35331                     break;
35332                 case 'bottom' :
35333                     cls += ' masonry-bottom-title';
35334                     break;
35335                 default:
35336                     break;
35337             }
35338             
35339         } else {
35340             if(!this.html.length && !this.bgimage.length){
35341                 cls += ' masonry-center-title';
35342             }
35343
35344             if(!this.html.length && this.bgimage.length){
35345                 cls += ' masonry-bottom-title';
35346             }
35347         }
35348         
35349         if(this.cls){
35350             cls += ' ' + this.cls;
35351         }
35352         
35353         var cfg = {
35354             tag: (this.href.length) ? 'a' : 'div',
35355             cls: cls,
35356             cn: [
35357                 {
35358                     tag: 'div',
35359                     cls: 'masonry-brick-mask'
35360                 },
35361                 {
35362                     tag: 'div',
35363                     cls: 'masonry-brick-paragraph',
35364                     cn: []
35365                 }
35366             ]
35367         };
35368         
35369         if(this.href.length){
35370             cfg.href = this.href;
35371         }
35372         
35373         var cn = cfg.cn[1].cn;
35374         
35375         if(this.title.length){
35376             cn.push({
35377                 tag: 'h4',
35378                 cls: 'masonry-brick-title',
35379                 html: this.title
35380             });
35381         }
35382         
35383         if(this.html.length){
35384             cn.push({
35385                 tag: 'p',
35386                 cls: 'masonry-brick-text',
35387                 html: this.html
35388             });
35389         }
35390         
35391         if (!this.title.length && !this.html.length) {
35392             cfg.cn[1].cls += ' hide';
35393         }
35394         
35395         if(this.bgimage.length){
35396             cfg.cn.push({
35397                 tag: 'img',
35398                 cls: 'masonry-brick-image-view',
35399                 src: this.bgimage
35400             });
35401         }
35402         
35403         if(this.videourl.length){
35404             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35405             // youtube support only?
35406             cfg.cn.push({
35407                 tag: 'iframe',
35408                 cls: 'masonry-brick-image-view',
35409                 src: vurl,
35410                 frameborder : 0,
35411                 allowfullscreen : true
35412             });
35413         }
35414         
35415         return cfg;
35416         
35417     },
35418     
35419     getSplitAutoCreate : function()
35420     {
35421         var cls = 'masonry-brick masonry-brick-split';
35422         
35423         if(this.href.length){
35424             cls += ' masonry-brick-link';
35425         }
35426         
35427         if(this.bgimage.length){
35428             cls += ' masonry-brick-image';
35429         }
35430         
35431         if(this.size){
35432             cls += ' masonry-' + this.size + '-brick';
35433         }
35434         
35435         switch (this.placetitle) {
35436             case 'center' :
35437                 cls += ' masonry-center-title';
35438                 break;
35439             case 'bottom' :
35440                 cls += ' masonry-bottom-title';
35441                 break;
35442             default:
35443                 if(!this.bgimage.length){
35444                     cls += ' masonry-center-title';
35445                 }
35446
35447                 if(this.bgimage.length){
35448                     cls += ' masonry-bottom-title';
35449                 }
35450                 break;
35451         }
35452         
35453         if(this.cls){
35454             cls += ' ' + this.cls;
35455         }
35456         
35457         var cfg = {
35458             tag: (this.href.length) ? 'a' : 'div',
35459             cls: cls,
35460             cn: [
35461                 {
35462                     tag: 'div',
35463                     cls: 'masonry-brick-split-head',
35464                     cn: [
35465                         {
35466                             tag: 'div',
35467                             cls: 'masonry-brick-paragraph',
35468                             cn: []
35469                         }
35470                     ]
35471                 },
35472                 {
35473                     tag: 'div',
35474                     cls: 'masonry-brick-split-body',
35475                     cn: []
35476                 }
35477             ]
35478         };
35479         
35480         if(this.href.length){
35481             cfg.href = this.href;
35482         }
35483         
35484         if(this.title.length){
35485             cfg.cn[0].cn[0].cn.push({
35486                 tag: 'h4',
35487                 cls: 'masonry-brick-title',
35488                 html: this.title
35489             });
35490         }
35491         
35492         if(this.html.length){
35493             cfg.cn[1].cn.push({
35494                 tag: 'p',
35495                 cls: 'masonry-brick-text',
35496                 html: this.html
35497             });
35498         }
35499
35500         if(this.bgimage.length){
35501             cfg.cn[0].cn.push({
35502                 tag: 'img',
35503                 cls: 'masonry-brick-image-view',
35504                 src: this.bgimage
35505             });
35506         }
35507         
35508         if(this.videourl.length){
35509             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35510             // youtube support only?
35511             cfg.cn[0].cn.cn.push({
35512                 tag: 'iframe',
35513                 cls: 'masonry-brick-image-view',
35514                 src: vurl,
35515                 frameborder : 0,
35516                 allowfullscreen : true
35517             });
35518         }
35519         
35520         return cfg;
35521     },
35522     
35523     initEvents: function() 
35524     {
35525         switch (this.size) {
35526             case 'xs' :
35527                 this.x = 1;
35528                 this.y = 1;
35529                 break;
35530             case 'sm' :
35531                 this.x = 2;
35532                 this.y = 2;
35533                 break;
35534             case 'md' :
35535             case 'md-left' :
35536             case 'md-right' :
35537                 this.x = 3;
35538                 this.y = 3;
35539                 break;
35540             case 'tall' :
35541                 this.x = 2;
35542                 this.y = 3;
35543                 break;
35544             case 'wide' :
35545                 this.x = 3;
35546                 this.y = 2;
35547                 break;
35548             case 'wide-thin' :
35549                 this.x = 3;
35550                 this.y = 1;
35551                 break;
35552                         
35553             default :
35554                 break;
35555         }
35556         
35557         if(Roo.isTouch){
35558             this.el.on('touchstart', this.onTouchStart, this);
35559             this.el.on('touchmove', this.onTouchMove, this);
35560             this.el.on('touchend', this.onTouchEnd, this);
35561             this.el.on('contextmenu', this.onContextMenu, this);
35562         } else {
35563             this.el.on('mouseenter'  ,this.enter, this);
35564             this.el.on('mouseleave', this.leave, this);
35565             this.el.on('click', this.onClick, this);
35566         }
35567         
35568         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35569             this.parent().bricks.push(this);   
35570         }
35571         
35572     },
35573     
35574     onClick: function(e, el)
35575     {
35576         var time = this.endTimer - this.startTimer;
35577         // Roo.log(e.preventDefault());
35578         if(Roo.isTouch){
35579             if(time > 1000){
35580                 e.preventDefault();
35581                 return;
35582             }
35583         }
35584         
35585         if(!this.preventDefault){
35586             return;
35587         }
35588         
35589         e.preventDefault();
35590         
35591         if (this.activeClass != '') {
35592             this.selectBrick();
35593         }
35594         
35595         this.fireEvent('click', this, e);
35596     },
35597     
35598     enter: function(e, el)
35599     {
35600         e.preventDefault();
35601         
35602         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35603             return;
35604         }
35605         
35606         if(this.bgimage.length && this.html.length){
35607             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35608         }
35609     },
35610     
35611     leave: function(e, el)
35612     {
35613         e.preventDefault();
35614         
35615         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35616             return;
35617         }
35618         
35619         if(this.bgimage.length && this.html.length){
35620             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35621         }
35622     },
35623     
35624     onTouchStart: function(e, el)
35625     {
35626 //        e.preventDefault();
35627         
35628         this.touchmoved = false;
35629         
35630         if(!this.isFitContainer){
35631             return;
35632         }
35633         
35634         if(!this.bgimage.length || !this.html.length){
35635             return;
35636         }
35637         
35638         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35639         
35640         this.timer = new Date().getTime();
35641         
35642     },
35643     
35644     onTouchMove: function(e, el)
35645     {
35646         this.touchmoved = true;
35647     },
35648     
35649     onContextMenu : function(e,el)
35650     {
35651         e.preventDefault();
35652         e.stopPropagation();
35653         return false;
35654     },
35655     
35656     onTouchEnd: function(e, el)
35657     {
35658 //        e.preventDefault();
35659         
35660         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35661         
35662             this.leave(e,el);
35663             
35664             return;
35665         }
35666         
35667         if(!this.bgimage.length || !this.html.length){
35668             
35669             if(this.href.length){
35670                 window.location.href = this.href;
35671             }
35672             
35673             return;
35674         }
35675         
35676         if(!this.isFitContainer){
35677             return;
35678         }
35679         
35680         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35681         
35682         window.location.href = this.href;
35683     },
35684     
35685     //selection on single brick only
35686     selectBrick : function() {
35687         
35688         if (!this.parentId) {
35689             return;
35690         }
35691         
35692         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35693         var index = m.selectedBrick.indexOf(this.id);
35694         
35695         if ( index > -1) {
35696             m.selectedBrick.splice(index,1);
35697             this.el.removeClass(this.activeClass);
35698             return;
35699         }
35700         
35701         for(var i = 0; i < m.selectedBrick.length; i++) {
35702             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35703             b.el.removeClass(b.activeClass);
35704         }
35705         
35706         m.selectedBrick = [];
35707         
35708         m.selectedBrick.push(this.id);
35709         this.el.addClass(this.activeClass);
35710         return;
35711     },
35712     
35713     isSelected : function(){
35714         return this.el.hasClass(this.activeClass);
35715         
35716     }
35717 });
35718
35719 Roo.apply(Roo.bootstrap.MasonryBrick, {
35720     
35721     //groups: {},
35722     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35723      /**
35724     * register a Masonry Brick
35725     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35726     */
35727     
35728     register : function(brick)
35729     {
35730         //this.groups[brick.id] = brick;
35731         this.groups.add(brick.id, brick);
35732     },
35733     /**
35734     * fetch a  masonry brick based on the masonry brick ID
35735     * @param {string} the masonry brick to add
35736     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35737     */
35738     
35739     get: function(brick_id) 
35740     {
35741         // if (typeof(this.groups[brick_id]) == 'undefined') {
35742         //     return false;
35743         // }
35744         // return this.groups[brick_id] ;
35745         
35746         if(this.groups.key(brick_id)) {
35747             return this.groups.key(brick_id);
35748         }
35749         
35750         return false;
35751     }
35752     
35753     
35754     
35755 });
35756
35757  /*
35758  * - LGPL
35759  *
35760  * element
35761  * 
35762  */
35763
35764 /**
35765  * @class Roo.bootstrap.Brick
35766  * @extends Roo.bootstrap.Component
35767  * Bootstrap Brick class
35768  * 
35769  * @constructor
35770  * Create a new Brick
35771  * @param {Object} config The config object
35772  */
35773
35774 Roo.bootstrap.Brick = function(config){
35775     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35776     
35777     this.addEvents({
35778         // raw events
35779         /**
35780          * @event click
35781          * When a Brick is click
35782          * @param {Roo.bootstrap.Brick} this
35783          * @param {Roo.EventObject} e
35784          */
35785         "click" : true
35786     });
35787 };
35788
35789 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35790     
35791     /**
35792      * @cfg {String} title
35793      */   
35794     title : '',
35795     /**
35796      * @cfg {String} html
35797      */   
35798     html : '',
35799     /**
35800      * @cfg {String} bgimage
35801      */   
35802     bgimage : '',
35803     /**
35804      * @cfg {String} cls
35805      */   
35806     cls : '',
35807     /**
35808      * @cfg {String} href
35809      */   
35810     href : '',
35811     /**
35812      * @cfg {String} video
35813      */   
35814     video : '',
35815     /**
35816      * @cfg {Boolean} square
35817      */   
35818     square : true,
35819     
35820     getAutoCreate : function()
35821     {
35822         var cls = 'roo-brick';
35823         
35824         if(this.href.length){
35825             cls += ' roo-brick-link';
35826         }
35827         
35828         if(this.bgimage.length){
35829             cls += ' roo-brick-image';
35830         }
35831         
35832         if(!this.html.length && !this.bgimage.length){
35833             cls += ' roo-brick-center-title';
35834         }
35835         
35836         if(!this.html.length && this.bgimage.length){
35837             cls += ' roo-brick-bottom-title';
35838         }
35839         
35840         if(this.cls){
35841             cls += ' ' + this.cls;
35842         }
35843         
35844         var cfg = {
35845             tag: (this.href.length) ? 'a' : 'div',
35846             cls: cls,
35847             cn: [
35848                 {
35849                     tag: 'div',
35850                     cls: 'roo-brick-paragraph',
35851                     cn: []
35852                 }
35853             ]
35854         };
35855         
35856         if(this.href.length){
35857             cfg.href = this.href;
35858         }
35859         
35860         var cn = cfg.cn[0].cn;
35861         
35862         if(this.title.length){
35863             cn.push({
35864                 tag: 'h4',
35865                 cls: 'roo-brick-title',
35866                 html: this.title
35867             });
35868         }
35869         
35870         if(this.html.length){
35871             cn.push({
35872                 tag: 'p',
35873                 cls: 'roo-brick-text',
35874                 html: this.html
35875             });
35876         } else {
35877             cn.cls += ' hide';
35878         }
35879         
35880         if(this.bgimage.length){
35881             cfg.cn.push({
35882                 tag: 'img',
35883                 cls: 'roo-brick-image-view',
35884                 src: this.bgimage
35885             });
35886         }
35887         
35888         return cfg;
35889     },
35890     
35891     initEvents: function() 
35892     {
35893         if(this.title.length || this.html.length){
35894             this.el.on('mouseenter'  ,this.enter, this);
35895             this.el.on('mouseleave', this.leave, this);
35896         }
35897         
35898         Roo.EventManager.onWindowResize(this.resize, this); 
35899         
35900         if(this.bgimage.length){
35901             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35902             this.imageEl.on('load', this.onImageLoad, this);
35903             return;
35904         }
35905         
35906         this.resize();
35907     },
35908     
35909     onImageLoad : function()
35910     {
35911         this.resize();
35912     },
35913     
35914     resize : function()
35915     {
35916         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35917         
35918         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35919         
35920         if(this.bgimage.length){
35921             var image = this.el.select('.roo-brick-image-view', true).first();
35922             
35923             image.setWidth(paragraph.getWidth());
35924             
35925             if(this.square){
35926                 image.setHeight(paragraph.getWidth());
35927             }
35928             
35929             this.el.setHeight(image.getHeight());
35930             paragraph.setHeight(image.getHeight());
35931             
35932         }
35933         
35934     },
35935     
35936     enter: function(e, el)
35937     {
35938         e.preventDefault();
35939         
35940         if(this.bgimage.length){
35941             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35942             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35943         }
35944     },
35945     
35946     leave: function(e, el)
35947     {
35948         e.preventDefault();
35949         
35950         if(this.bgimage.length){
35951             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35952             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35953         }
35954     }
35955     
35956 });
35957
35958  
35959
35960  /*
35961  * - LGPL
35962  *
35963  * Number field 
35964  */
35965
35966 /**
35967  * @class Roo.bootstrap.NumberField
35968  * @extends Roo.bootstrap.Input
35969  * Bootstrap NumberField class
35970  * 
35971  * 
35972  * 
35973  * 
35974  * @constructor
35975  * Create a new NumberField
35976  * @param {Object} config The config object
35977  */
35978
35979 Roo.bootstrap.NumberField = function(config){
35980     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35981 };
35982
35983 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35984     
35985     /**
35986      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35987      */
35988     allowDecimals : true,
35989     /**
35990      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35991      */
35992     decimalSeparator : ".",
35993     /**
35994      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35995      */
35996     decimalPrecision : 2,
35997     /**
35998      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35999      */
36000     allowNegative : true,
36001     
36002     /**
36003      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36004      */
36005     allowZero: true,
36006     /**
36007      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36008      */
36009     minValue : Number.NEGATIVE_INFINITY,
36010     /**
36011      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36012      */
36013     maxValue : Number.MAX_VALUE,
36014     /**
36015      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36016      */
36017     minText : "The minimum value for this field is {0}",
36018     /**
36019      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36020      */
36021     maxText : "The maximum value for this field is {0}",
36022     /**
36023      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36024      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36025      */
36026     nanText : "{0} is not a valid number",
36027     /**
36028      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36029      */
36030     thousandsDelimiter : false,
36031     /**
36032      * @cfg {String} valueAlign alignment of value
36033      */
36034     valueAlign : "left",
36035
36036     getAutoCreate : function()
36037     {
36038         var hiddenInput = {
36039             tag: 'input',
36040             type: 'hidden',
36041             id: Roo.id(),
36042             cls: 'hidden-number-input'
36043         };
36044         
36045         if (this.name) {
36046             hiddenInput.name = this.name;
36047         }
36048         
36049         this.name = '';
36050         
36051         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36052         
36053         this.name = hiddenInput.name;
36054         
36055         if(cfg.cn.length > 0) {
36056             cfg.cn.push(hiddenInput);
36057         }
36058         
36059         return cfg;
36060     },
36061
36062     // private
36063     initEvents : function()
36064     {   
36065         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36066         
36067         var allowed = "0123456789";
36068         
36069         if(this.allowDecimals){
36070             allowed += this.decimalSeparator;
36071         }
36072         
36073         if(this.allowNegative){
36074             allowed += "-";
36075         }
36076         
36077         if(this.thousandsDelimiter) {
36078             allowed += ",";
36079         }
36080         
36081         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36082         
36083         var keyPress = function(e){
36084             
36085             var k = e.getKey();
36086             
36087             var c = e.getCharCode();
36088             
36089             if(
36090                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36091                     allowed.indexOf(String.fromCharCode(c)) === -1
36092             ){
36093                 e.stopEvent();
36094                 return;
36095             }
36096             
36097             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36098                 return;
36099             }
36100             
36101             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36102                 e.stopEvent();
36103             }
36104         };
36105         
36106         this.el.on("keypress", keyPress, this);
36107     },
36108     
36109     validateValue : function(value)
36110     {
36111         
36112         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36113             return false;
36114         }
36115         
36116         var num = this.parseValue(value);
36117         
36118         if(isNaN(num)){
36119             this.markInvalid(String.format(this.nanText, value));
36120             return false;
36121         }
36122         
36123         if(num < this.minValue){
36124             this.markInvalid(String.format(this.minText, this.minValue));
36125             return false;
36126         }
36127         
36128         if(num > this.maxValue){
36129             this.markInvalid(String.format(this.maxText, this.maxValue));
36130             return false;
36131         }
36132         
36133         return true;
36134     },
36135
36136     getValue : function()
36137     {
36138         var v = this.hiddenEl().getValue();
36139         
36140         return this.fixPrecision(this.parseValue(v));
36141     },
36142
36143     parseValue : function(value)
36144     {
36145         if(this.thousandsDelimiter) {
36146             value += "";
36147             r = new RegExp(",", "g");
36148             value = value.replace(r, "");
36149         }
36150         
36151         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36152         return isNaN(value) ? '' : value;
36153     },
36154
36155     fixPrecision : function(value)
36156     {
36157         if(this.thousandsDelimiter) {
36158             value += "";
36159             r = new RegExp(",", "g");
36160             value = value.replace(r, "");
36161         }
36162         
36163         var nan = isNaN(value);
36164         
36165         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36166             return nan ? '' : value;
36167         }
36168         return parseFloat(value).toFixed(this.decimalPrecision);
36169     },
36170
36171     setValue : function(v)
36172     {
36173         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36174         
36175         this.value = v;
36176         
36177         if(this.rendered){
36178             
36179             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36180             
36181             this.inputEl().dom.value = (v == '') ? '' :
36182                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36183             
36184             if(!this.allowZero && v === '0') {
36185                 this.hiddenEl().dom.value = '';
36186                 this.inputEl().dom.value = '';
36187             }
36188             
36189             this.validate();
36190         }
36191     },
36192
36193     decimalPrecisionFcn : function(v)
36194     {
36195         return Math.floor(v);
36196     },
36197
36198     beforeBlur : function()
36199     {
36200         var v = this.parseValue(this.getRawValue());
36201         
36202         if(v || v === 0 || v === ''){
36203             this.setValue(v);
36204         }
36205     },
36206     
36207     hiddenEl : function()
36208     {
36209         return this.el.select('input.hidden-number-input',true).first();
36210     }
36211     
36212 });
36213
36214  
36215
36216 /*
36217 * Licence: LGPL
36218 */
36219
36220 /**
36221  * @class Roo.bootstrap.DocumentSlider
36222  * @extends Roo.bootstrap.Component
36223  * Bootstrap DocumentSlider class
36224  * 
36225  * @constructor
36226  * Create a new DocumentViewer
36227  * @param {Object} config The config object
36228  */
36229
36230 Roo.bootstrap.DocumentSlider = function(config){
36231     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36232     
36233     this.files = [];
36234     
36235     this.addEvents({
36236         /**
36237          * @event initial
36238          * Fire after initEvent
36239          * @param {Roo.bootstrap.DocumentSlider} this
36240          */
36241         "initial" : true,
36242         /**
36243          * @event update
36244          * Fire after update
36245          * @param {Roo.bootstrap.DocumentSlider} this
36246          */
36247         "update" : true,
36248         /**
36249          * @event click
36250          * Fire after click
36251          * @param {Roo.bootstrap.DocumentSlider} this
36252          */
36253         "click" : true
36254     });
36255 };
36256
36257 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36258     
36259     files : false,
36260     
36261     indicator : 0,
36262     
36263     getAutoCreate : function()
36264     {
36265         var cfg = {
36266             tag : 'div',
36267             cls : 'roo-document-slider',
36268             cn : [
36269                 {
36270                     tag : 'div',
36271                     cls : 'roo-document-slider-header',
36272                     cn : [
36273                         {
36274                             tag : 'div',
36275                             cls : 'roo-document-slider-header-title'
36276                         }
36277                     ]
36278                 },
36279                 {
36280                     tag : 'div',
36281                     cls : 'roo-document-slider-body',
36282                     cn : [
36283                         {
36284                             tag : 'div',
36285                             cls : 'roo-document-slider-prev',
36286                             cn : [
36287                                 {
36288                                     tag : 'i',
36289                                     cls : 'fa fa-chevron-left'
36290                                 }
36291                             ]
36292                         },
36293                         {
36294                             tag : 'div',
36295                             cls : 'roo-document-slider-thumb',
36296                             cn : [
36297                                 {
36298                                     tag : 'img',
36299                                     cls : 'roo-document-slider-image'
36300                                 }
36301                             ]
36302                         },
36303                         {
36304                             tag : 'div',
36305                             cls : 'roo-document-slider-next',
36306                             cn : [
36307                                 {
36308                                     tag : 'i',
36309                                     cls : 'fa fa-chevron-right'
36310                                 }
36311                             ]
36312                         }
36313                     ]
36314                 }
36315             ]
36316         };
36317         
36318         return cfg;
36319     },
36320     
36321     initEvents : function()
36322     {
36323         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36324         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36325         
36326         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36327         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36328         
36329         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36330         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36331         
36332         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36333         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36334         
36335         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36336         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36337         
36338         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36339         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36340         
36341         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36342         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36343         
36344         this.thumbEl.on('click', this.onClick, this);
36345         
36346         this.prevIndicator.on('click', this.prev, this);
36347         
36348         this.nextIndicator.on('click', this.next, this);
36349         
36350     },
36351     
36352     initial : function()
36353     {
36354         if(this.files.length){
36355             this.indicator = 1;
36356             this.update()
36357         }
36358         
36359         this.fireEvent('initial', this);
36360     },
36361     
36362     update : function()
36363     {
36364         this.imageEl.attr('src', this.files[this.indicator - 1]);
36365         
36366         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36367         
36368         this.prevIndicator.show();
36369         
36370         if(this.indicator == 1){
36371             this.prevIndicator.hide();
36372         }
36373         
36374         this.nextIndicator.show();
36375         
36376         if(this.indicator == this.files.length){
36377             this.nextIndicator.hide();
36378         }
36379         
36380         this.thumbEl.scrollTo('top');
36381         
36382         this.fireEvent('update', this);
36383     },
36384     
36385     onClick : function(e)
36386     {
36387         e.preventDefault();
36388         
36389         this.fireEvent('click', this);
36390     },
36391     
36392     prev : function(e)
36393     {
36394         e.preventDefault();
36395         
36396         this.indicator = Math.max(1, this.indicator - 1);
36397         
36398         this.update();
36399     },
36400     
36401     next : function(e)
36402     {
36403         e.preventDefault();
36404         
36405         this.indicator = Math.min(this.files.length, this.indicator + 1);
36406         
36407         this.update();
36408     }
36409 });
36410 /*
36411  * - LGPL
36412  *
36413  * RadioSet
36414  *
36415  *
36416  */
36417
36418 /**
36419  * @class Roo.bootstrap.RadioSet
36420  * @extends Roo.bootstrap.Input
36421  * Bootstrap RadioSet class
36422  * @cfg {String} indicatorpos (left|right) default left
36423  * @cfg {Boolean} inline (true|false) inline the element (default true)
36424  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36425  * @constructor
36426  * Create a new RadioSet
36427  * @param {Object} config The config object
36428  */
36429
36430 Roo.bootstrap.RadioSet = function(config){
36431     
36432     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36433     
36434     this.radioes = [];
36435     
36436     Roo.bootstrap.RadioSet.register(this);
36437     
36438     this.addEvents({
36439         /**
36440         * @event check
36441         * Fires when the element is checked or unchecked.
36442         * @param {Roo.bootstrap.RadioSet} this This radio
36443         * @param {Roo.bootstrap.Radio} item The checked item
36444         */
36445        check : true,
36446        /**
36447         * @event click
36448         * Fires when the element is click.
36449         * @param {Roo.bootstrap.RadioSet} this This radio set
36450         * @param {Roo.bootstrap.Radio} item The checked item
36451         * @param {Roo.EventObject} e The event object
36452         */
36453        click : true
36454     });
36455     
36456 };
36457
36458 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36459
36460     radioes : false,
36461     
36462     inline : true,
36463     
36464     weight : '',
36465     
36466     indicatorpos : 'left',
36467     
36468     getAutoCreate : function()
36469     {
36470         var label = {
36471             tag : 'label',
36472             cls : 'roo-radio-set-label',
36473             cn : [
36474                 {
36475                     tag : 'span',
36476                     html : this.fieldLabel
36477                 }
36478             ]
36479         };
36480         if (Roo.bootstrap.version == 3) {
36481             
36482             
36483             if(this.indicatorpos == 'left'){
36484                 label.cn.unshift({
36485                     tag : 'i',
36486                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36487                     tooltip : 'This field is required'
36488                 });
36489             } else {
36490                 label.cn.push({
36491                     tag : 'i',
36492                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36493                     tooltip : 'This field is required'
36494                 });
36495             }
36496         }
36497         var items = {
36498             tag : 'div',
36499             cls : 'roo-radio-set-items'
36500         };
36501         
36502         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36503         
36504         if (align === 'left' && this.fieldLabel.length) {
36505             
36506             items = {
36507                 cls : "roo-radio-set-right", 
36508                 cn: [
36509                     items
36510                 ]
36511             };
36512             
36513             if(this.labelWidth > 12){
36514                 label.style = "width: " + this.labelWidth + 'px';
36515             }
36516             
36517             if(this.labelWidth < 13 && this.labelmd == 0){
36518                 this.labelmd = this.labelWidth;
36519             }
36520             
36521             if(this.labellg > 0){
36522                 label.cls += ' col-lg-' + this.labellg;
36523                 items.cls += ' col-lg-' + (12 - this.labellg);
36524             }
36525             
36526             if(this.labelmd > 0){
36527                 label.cls += ' col-md-' + this.labelmd;
36528                 items.cls += ' col-md-' + (12 - this.labelmd);
36529             }
36530             
36531             if(this.labelsm > 0){
36532                 label.cls += ' col-sm-' + this.labelsm;
36533                 items.cls += ' col-sm-' + (12 - this.labelsm);
36534             }
36535             
36536             if(this.labelxs > 0){
36537                 label.cls += ' col-xs-' + this.labelxs;
36538                 items.cls += ' col-xs-' + (12 - this.labelxs);
36539             }
36540         }
36541         
36542         var cfg = {
36543             tag : 'div',
36544             cls : 'roo-radio-set',
36545             cn : [
36546                 {
36547                     tag : 'input',
36548                     cls : 'roo-radio-set-input',
36549                     type : 'hidden',
36550                     name : this.name,
36551                     value : this.value ? this.value :  ''
36552                 },
36553                 label,
36554                 items
36555             ]
36556         };
36557         
36558         if(this.weight.length){
36559             cfg.cls += ' roo-radio-' + this.weight;
36560         }
36561         
36562         if(this.inline) {
36563             cfg.cls += ' roo-radio-set-inline';
36564         }
36565         
36566         var settings=this;
36567         ['xs','sm','md','lg'].map(function(size){
36568             if (settings[size]) {
36569                 cfg.cls += ' col-' + size + '-' + settings[size];
36570             }
36571         });
36572         
36573         return cfg;
36574         
36575     },
36576
36577     initEvents : function()
36578     {
36579         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36580         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36581         
36582         if(!this.fieldLabel.length){
36583             this.labelEl.hide();
36584         }
36585         
36586         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36587         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36588         
36589         this.indicator = this.indicatorEl();
36590         
36591         if(this.indicator){
36592             this.indicator.addClass('invisible');
36593         }
36594         
36595         this.originalValue = this.getValue();
36596         
36597     },
36598     
36599     inputEl: function ()
36600     {
36601         return this.el.select('.roo-radio-set-input', true).first();
36602     },
36603     
36604     getChildContainer : function()
36605     {
36606         return this.itemsEl;
36607     },
36608     
36609     register : function(item)
36610     {
36611         this.radioes.push(item);
36612         
36613     },
36614     
36615     validate : function()
36616     {   
36617         if(this.getVisibilityEl().hasClass('hidden')){
36618             return true;
36619         }
36620         
36621         var valid = false;
36622         
36623         Roo.each(this.radioes, function(i){
36624             if(!i.checked){
36625                 return;
36626             }
36627             
36628             valid = true;
36629             return false;
36630         });
36631         
36632         if(this.allowBlank) {
36633             return true;
36634         }
36635         
36636         if(this.disabled || valid){
36637             this.markValid();
36638             return true;
36639         }
36640         
36641         this.markInvalid();
36642         return false;
36643         
36644     },
36645     
36646     markValid : function()
36647     {
36648         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36649             this.indicatorEl().removeClass('visible');
36650             this.indicatorEl().addClass('invisible');
36651         }
36652         
36653         
36654         if (Roo.bootstrap.version == 3) {
36655             this.el.removeClass([this.invalidClass, this.validClass]);
36656             this.el.addClass(this.validClass);
36657         } else {
36658             this.el.removeClass(['is-invalid','is-valid']);
36659             this.el.addClass(['is-valid']);
36660         }
36661         this.fireEvent('valid', this);
36662     },
36663     
36664     markInvalid : function(msg)
36665     {
36666         if(this.allowBlank || this.disabled){
36667             return;
36668         }
36669         
36670         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36671             this.indicatorEl().removeClass('invisible');
36672             this.indicatorEl().addClass('visible');
36673         }
36674         if (Roo.bootstrap.version == 3) {
36675             this.el.removeClass([this.invalidClass, this.validClass]);
36676             this.el.addClass(this.invalidClass);
36677         } else {
36678             this.el.removeClass(['is-invalid','is-valid']);
36679             this.el.addClass(['is-invalid']);
36680         }
36681         
36682         this.fireEvent('invalid', this, msg);
36683         
36684     },
36685     
36686     setValue : function(v, suppressEvent)
36687     {   
36688         if(this.value === v){
36689             return;
36690         }
36691         
36692         this.value = v;
36693         
36694         if(this.rendered){
36695             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36696         }
36697         
36698         Roo.each(this.radioes, function(i){
36699             i.checked = false;
36700             i.el.removeClass('checked');
36701         });
36702         
36703         Roo.each(this.radioes, function(i){
36704             
36705             if(i.value === v || i.value.toString() === v.toString()){
36706                 i.checked = true;
36707                 i.el.addClass('checked');
36708                 
36709                 if(suppressEvent !== true){
36710                     this.fireEvent('check', this, i);
36711                 }
36712                 
36713                 return false;
36714             }
36715             
36716         }, this);
36717         
36718         this.validate();
36719     },
36720     
36721     clearInvalid : function(){
36722         
36723         if(!this.el || this.preventMark){
36724             return;
36725         }
36726         
36727         this.el.removeClass([this.invalidClass]);
36728         
36729         this.fireEvent('valid', this);
36730     }
36731     
36732 });
36733
36734 Roo.apply(Roo.bootstrap.RadioSet, {
36735     
36736     groups: {},
36737     
36738     register : function(set)
36739     {
36740         this.groups[set.name] = set;
36741     },
36742     
36743     get: function(name) 
36744     {
36745         if (typeof(this.groups[name]) == 'undefined') {
36746             return false;
36747         }
36748         
36749         return this.groups[name] ;
36750     }
36751     
36752 });
36753 /*
36754  * Based on:
36755  * Ext JS Library 1.1.1
36756  * Copyright(c) 2006-2007, Ext JS, LLC.
36757  *
36758  * Originally Released Under LGPL - original licence link has changed is not relivant.
36759  *
36760  * Fork - LGPL
36761  * <script type="text/javascript">
36762  */
36763
36764
36765 /**
36766  * @class Roo.bootstrap.SplitBar
36767  * @extends Roo.util.Observable
36768  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36769  * <br><br>
36770  * Usage:
36771  * <pre><code>
36772 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36773                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36774 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36775 split.minSize = 100;
36776 split.maxSize = 600;
36777 split.animate = true;
36778 split.on('moved', splitterMoved);
36779 </code></pre>
36780  * @constructor
36781  * Create a new SplitBar
36782  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36783  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36784  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36785  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36786                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36787                         position of the SplitBar).
36788  */
36789 Roo.bootstrap.SplitBar = function(cfg){
36790     
36791     /** @private */
36792     
36793     //{
36794     //  dragElement : elm
36795     //  resizingElement: el,
36796         // optional..
36797     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36798     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36799         // existingProxy ???
36800     //}
36801     
36802     this.el = Roo.get(cfg.dragElement, true);
36803     this.el.dom.unselectable = "on";
36804     /** @private */
36805     this.resizingEl = Roo.get(cfg.resizingElement, true);
36806
36807     /**
36808      * @private
36809      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36810      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36811      * @type Number
36812      */
36813     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36814     
36815     /**
36816      * The minimum size of the resizing element. (Defaults to 0)
36817      * @type Number
36818      */
36819     this.minSize = 0;
36820     
36821     /**
36822      * The maximum size of the resizing element. (Defaults to 2000)
36823      * @type Number
36824      */
36825     this.maxSize = 2000;
36826     
36827     /**
36828      * Whether to animate the transition to the new size
36829      * @type Boolean
36830      */
36831     this.animate = false;
36832     
36833     /**
36834      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36835      * @type Boolean
36836      */
36837     this.useShim = false;
36838     
36839     /** @private */
36840     this.shim = null;
36841     
36842     if(!cfg.existingProxy){
36843         /** @private */
36844         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36845     }else{
36846         this.proxy = Roo.get(cfg.existingProxy).dom;
36847     }
36848     /** @private */
36849     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36850     
36851     /** @private */
36852     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36853     
36854     /** @private */
36855     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36856     
36857     /** @private */
36858     this.dragSpecs = {};
36859     
36860     /**
36861      * @private The adapter to use to positon and resize elements
36862      */
36863     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36864     this.adapter.init(this);
36865     
36866     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36867         /** @private */
36868         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36869         this.el.addClass("roo-splitbar-h");
36870     }else{
36871         /** @private */
36872         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36873         this.el.addClass("roo-splitbar-v");
36874     }
36875     
36876     this.addEvents({
36877         /**
36878          * @event resize
36879          * Fires when the splitter is moved (alias for {@link #event-moved})
36880          * @param {Roo.bootstrap.SplitBar} this
36881          * @param {Number} newSize the new width or height
36882          */
36883         "resize" : true,
36884         /**
36885          * @event moved
36886          * Fires when the splitter is moved
36887          * @param {Roo.bootstrap.SplitBar} this
36888          * @param {Number} newSize the new width or height
36889          */
36890         "moved" : true,
36891         /**
36892          * @event beforeresize
36893          * Fires before the splitter is dragged
36894          * @param {Roo.bootstrap.SplitBar} this
36895          */
36896         "beforeresize" : true,
36897
36898         "beforeapply" : true
36899     });
36900
36901     Roo.util.Observable.call(this);
36902 };
36903
36904 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36905     onStartProxyDrag : function(x, y){
36906         this.fireEvent("beforeresize", this);
36907         if(!this.overlay){
36908             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36909             o.unselectable();
36910             o.enableDisplayMode("block");
36911             // all splitbars share the same overlay
36912             Roo.bootstrap.SplitBar.prototype.overlay = o;
36913         }
36914         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36915         this.overlay.show();
36916         Roo.get(this.proxy).setDisplayed("block");
36917         var size = this.adapter.getElementSize(this);
36918         this.activeMinSize = this.getMinimumSize();;
36919         this.activeMaxSize = this.getMaximumSize();;
36920         var c1 = size - this.activeMinSize;
36921         var c2 = Math.max(this.activeMaxSize - size, 0);
36922         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36923             this.dd.resetConstraints();
36924             this.dd.setXConstraint(
36925                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36926                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36927             );
36928             this.dd.setYConstraint(0, 0);
36929         }else{
36930             this.dd.resetConstraints();
36931             this.dd.setXConstraint(0, 0);
36932             this.dd.setYConstraint(
36933                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36934                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36935             );
36936          }
36937         this.dragSpecs.startSize = size;
36938         this.dragSpecs.startPoint = [x, y];
36939         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36940     },
36941     
36942     /** 
36943      * @private Called after the drag operation by the DDProxy
36944      */
36945     onEndProxyDrag : function(e){
36946         Roo.get(this.proxy).setDisplayed(false);
36947         var endPoint = Roo.lib.Event.getXY(e);
36948         if(this.overlay){
36949             this.overlay.hide();
36950         }
36951         var newSize;
36952         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36953             newSize = this.dragSpecs.startSize + 
36954                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36955                     endPoint[0] - this.dragSpecs.startPoint[0] :
36956                     this.dragSpecs.startPoint[0] - endPoint[0]
36957                 );
36958         }else{
36959             newSize = this.dragSpecs.startSize + 
36960                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36961                     endPoint[1] - this.dragSpecs.startPoint[1] :
36962                     this.dragSpecs.startPoint[1] - endPoint[1]
36963                 );
36964         }
36965         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36966         if(newSize != this.dragSpecs.startSize){
36967             if(this.fireEvent('beforeapply', this, newSize) !== false){
36968                 this.adapter.setElementSize(this, newSize);
36969                 this.fireEvent("moved", this, newSize);
36970                 this.fireEvent("resize", this, newSize);
36971             }
36972         }
36973     },
36974     
36975     /**
36976      * Get the adapter this SplitBar uses
36977      * @return The adapter object
36978      */
36979     getAdapter : function(){
36980         return this.adapter;
36981     },
36982     
36983     /**
36984      * Set the adapter this SplitBar uses
36985      * @param {Object} adapter A SplitBar adapter object
36986      */
36987     setAdapter : function(adapter){
36988         this.adapter = adapter;
36989         this.adapter.init(this);
36990     },
36991     
36992     /**
36993      * Gets the minimum size for the resizing element
36994      * @return {Number} The minimum size
36995      */
36996     getMinimumSize : function(){
36997         return this.minSize;
36998     },
36999     
37000     /**
37001      * Sets the minimum size for the resizing element
37002      * @param {Number} minSize The minimum size
37003      */
37004     setMinimumSize : function(minSize){
37005         this.minSize = minSize;
37006     },
37007     
37008     /**
37009      * Gets the maximum size for the resizing element
37010      * @return {Number} The maximum size
37011      */
37012     getMaximumSize : function(){
37013         return this.maxSize;
37014     },
37015     
37016     /**
37017      * Sets the maximum size for the resizing element
37018      * @param {Number} maxSize The maximum size
37019      */
37020     setMaximumSize : function(maxSize){
37021         this.maxSize = maxSize;
37022     },
37023     
37024     /**
37025      * Sets the initialize size for the resizing element
37026      * @param {Number} size The initial size
37027      */
37028     setCurrentSize : function(size){
37029         var oldAnimate = this.animate;
37030         this.animate = false;
37031         this.adapter.setElementSize(this, size);
37032         this.animate = oldAnimate;
37033     },
37034     
37035     /**
37036      * Destroy this splitbar. 
37037      * @param {Boolean} removeEl True to remove the element
37038      */
37039     destroy : function(removeEl){
37040         if(this.shim){
37041             this.shim.remove();
37042         }
37043         this.dd.unreg();
37044         this.proxy.parentNode.removeChild(this.proxy);
37045         if(removeEl){
37046             this.el.remove();
37047         }
37048     }
37049 });
37050
37051 /**
37052  * @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.
37053  */
37054 Roo.bootstrap.SplitBar.createProxy = function(dir){
37055     var proxy = new Roo.Element(document.createElement("div"));
37056     proxy.unselectable();
37057     var cls = 'roo-splitbar-proxy';
37058     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37059     document.body.appendChild(proxy.dom);
37060     return proxy.dom;
37061 };
37062
37063 /** 
37064  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37065  * Default Adapter. It assumes the splitter and resizing element are not positioned
37066  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37067  */
37068 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37069 };
37070
37071 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37072     // do nothing for now
37073     init : function(s){
37074     
37075     },
37076     /**
37077      * Called before drag operations to get the current size of the resizing element. 
37078      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37079      */
37080      getElementSize : function(s){
37081         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37082             return s.resizingEl.getWidth();
37083         }else{
37084             return s.resizingEl.getHeight();
37085         }
37086     },
37087     
37088     /**
37089      * Called after drag operations to set the size of the resizing element.
37090      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37091      * @param {Number} newSize The new size to set
37092      * @param {Function} onComplete A function to be invoked when resizing is complete
37093      */
37094     setElementSize : function(s, newSize, onComplete){
37095         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37096             if(!s.animate){
37097                 s.resizingEl.setWidth(newSize);
37098                 if(onComplete){
37099                     onComplete(s, newSize);
37100                 }
37101             }else{
37102                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37103             }
37104         }else{
37105             
37106             if(!s.animate){
37107                 s.resizingEl.setHeight(newSize);
37108                 if(onComplete){
37109                     onComplete(s, newSize);
37110                 }
37111             }else{
37112                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37113             }
37114         }
37115     }
37116 };
37117
37118 /** 
37119  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37120  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37121  * Adapter that  moves the splitter element to align with the resized sizing element. 
37122  * Used with an absolute positioned SplitBar.
37123  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37124  * document.body, make sure you assign an id to the body element.
37125  */
37126 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37127     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37128     this.container = Roo.get(container);
37129 };
37130
37131 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37132     init : function(s){
37133         this.basic.init(s);
37134     },
37135     
37136     getElementSize : function(s){
37137         return this.basic.getElementSize(s);
37138     },
37139     
37140     setElementSize : function(s, newSize, onComplete){
37141         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37142     },
37143     
37144     moveSplitter : function(s){
37145         var yes = Roo.bootstrap.SplitBar;
37146         switch(s.placement){
37147             case yes.LEFT:
37148                 s.el.setX(s.resizingEl.getRight());
37149                 break;
37150             case yes.RIGHT:
37151                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37152                 break;
37153             case yes.TOP:
37154                 s.el.setY(s.resizingEl.getBottom());
37155                 break;
37156             case yes.BOTTOM:
37157                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37158                 break;
37159         }
37160     }
37161 };
37162
37163 /**
37164  * Orientation constant - Create a vertical SplitBar
37165  * @static
37166  * @type Number
37167  */
37168 Roo.bootstrap.SplitBar.VERTICAL = 1;
37169
37170 /**
37171  * Orientation constant - Create a horizontal SplitBar
37172  * @static
37173  * @type Number
37174  */
37175 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37176
37177 /**
37178  * Placement constant - The resizing element is to the left of the splitter element
37179  * @static
37180  * @type Number
37181  */
37182 Roo.bootstrap.SplitBar.LEFT = 1;
37183
37184 /**
37185  * Placement constant - The resizing element is to the right of the splitter element
37186  * @static
37187  * @type Number
37188  */
37189 Roo.bootstrap.SplitBar.RIGHT = 2;
37190
37191 /**
37192  * Placement constant - The resizing element is positioned above the splitter element
37193  * @static
37194  * @type Number
37195  */
37196 Roo.bootstrap.SplitBar.TOP = 3;
37197
37198 /**
37199  * Placement constant - The resizing element is positioned under splitter element
37200  * @static
37201  * @type Number
37202  */
37203 Roo.bootstrap.SplitBar.BOTTOM = 4;
37204 Roo.namespace("Roo.bootstrap.layout");/*
37205  * Based on:
37206  * Ext JS Library 1.1.1
37207  * Copyright(c) 2006-2007, Ext JS, LLC.
37208  *
37209  * Originally Released Under LGPL - original licence link has changed is not relivant.
37210  *
37211  * Fork - LGPL
37212  * <script type="text/javascript">
37213  */
37214
37215 /**
37216  * @class Roo.bootstrap.layout.Manager
37217  * @extends Roo.bootstrap.Component
37218  * Base class for layout managers.
37219  */
37220 Roo.bootstrap.layout.Manager = function(config)
37221 {
37222     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37223
37224
37225
37226
37227
37228     /** false to disable window resize monitoring @type Boolean */
37229     this.monitorWindowResize = true;
37230     this.regions = {};
37231     this.addEvents({
37232         /**
37233          * @event layout
37234          * Fires when a layout is performed.
37235          * @param {Roo.LayoutManager} this
37236          */
37237         "layout" : true,
37238         /**
37239          * @event regionresized
37240          * Fires when the user resizes a region.
37241          * @param {Roo.LayoutRegion} region The resized region
37242          * @param {Number} newSize The new size (width for east/west, height for north/south)
37243          */
37244         "regionresized" : true,
37245         /**
37246          * @event regioncollapsed
37247          * Fires when a region is collapsed.
37248          * @param {Roo.LayoutRegion} region The collapsed region
37249          */
37250         "regioncollapsed" : true,
37251         /**
37252          * @event regionexpanded
37253          * Fires when a region is expanded.
37254          * @param {Roo.LayoutRegion} region The expanded region
37255          */
37256         "regionexpanded" : true
37257     });
37258     this.updating = false;
37259
37260     if (config.el) {
37261         this.el = Roo.get(config.el);
37262         this.initEvents();
37263     }
37264
37265 };
37266
37267 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37268
37269
37270     regions : null,
37271
37272     monitorWindowResize : true,
37273
37274
37275     updating : false,
37276
37277
37278     onRender : function(ct, position)
37279     {
37280         if(!this.el){
37281             this.el = Roo.get(ct);
37282             this.initEvents();
37283         }
37284         //this.fireEvent('render',this);
37285     },
37286
37287
37288     initEvents: function()
37289     {
37290
37291
37292         // ie scrollbar fix
37293         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37294             document.body.scroll = "no";
37295         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37296             this.el.position('relative');
37297         }
37298         this.id = this.el.id;
37299         this.el.addClass("roo-layout-container");
37300         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37301         if(this.el.dom != document.body ) {
37302             this.el.on('resize', this.layout,this);
37303             this.el.on('show', this.layout,this);
37304         }
37305
37306     },
37307
37308     /**
37309      * Returns true if this layout is currently being updated
37310      * @return {Boolean}
37311      */
37312     isUpdating : function(){
37313         return this.updating;
37314     },
37315
37316     /**
37317      * Suspend the LayoutManager from doing auto-layouts while
37318      * making multiple add or remove calls
37319      */
37320     beginUpdate : function(){
37321         this.updating = true;
37322     },
37323
37324     /**
37325      * Restore auto-layouts and optionally disable the manager from performing a layout
37326      * @param {Boolean} noLayout true to disable a layout update
37327      */
37328     endUpdate : function(noLayout){
37329         this.updating = false;
37330         if(!noLayout){
37331             this.layout();
37332         }
37333     },
37334
37335     layout: function(){
37336         // abstract...
37337     },
37338
37339     onRegionResized : function(region, newSize){
37340         this.fireEvent("regionresized", region, newSize);
37341         this.layout();
37342     },
37343
37344     onRegionCollapsed : function(region){
37345         this.fireEvent("regioncollapsed", region);
37346     },
37347
37348     onRegionExpanded : function(region){
37349         this.fireEvent("regionexpanded", region);
37350     },
37351
37352     /**
37353      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37354      * performs box-model adjustments.
37355      * @return {Object} The size as an object {width: (the width), height: (the height)}
37356      */
37357     getViewSize : function()
37358     {
37359         var size;
37360         if(this.el.dom != document.body){
37361             size = this.el.getSize();
37362         }else{
37363             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37364         }
37365         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37366         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37367         return size;
37368     },
37369
37370     /**
37371      * Returns the Element this layout is bound to.
37372      * @return {Roo.Element}
37373      */
37374     getEl : function(){
37375         return this.el;
37376     },
37377
37378     /**
37379      * Returns the specified region.
37380      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37381      * @return {Roo.LayoutRegion}
37382      */
37383     getRegion : function(target){
37384         return this.regions[target.toLowerCase()];
37385     },
37386
37387     onWindowResize : function(){
37388         if(this.monitorWindowResize){
37389             this.layout();
37390         }
37391     }
37392 });
37393 /*
37394  * Based on:
37395  * Ext JS Library 1.1.1
37396  * Copyright(c) 2006-2007, Ext JS, LLC.
37397  *
37398  * Originally Released Under LGPL - original licence link has changed is not relivant.
37399  *
37400  * Fork - LGPL
37401  * <script type="text/javascript">
37402  */
37403 /**
37404  * @class Roo.bootstrap.layout.Border
37405  * @extends Roo.bootstrap.layout.Manager
37406  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37407  * please see: examples/bootstrap/nested.html<br><br>
37408  
37409 <b>The container the layout is rendered into can be either the body element or any other element.
37410 If it is not the body element, the container needs to either be an absolute positioned element,
37411 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37412 the container size if it is not the body element.</b>
37413
37414 * @constructor
37415 * Create a new Border
37416 * @param {Object} config Configuration options
37417  */
37418 Roo.bootstrap.layout.Border = function(config){
37419     config = config || {};
37420     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37421     
37422     
37423     
37424     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37425         if(config[region]){
37426             config[region].region = region;
37427             this.addRegion(config[region]);
37428         }
37429     },this);
37430     
37431 };
37432
37433 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37434
37435 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37436     
37437     parent : false, // this might point to a 'nest' or a ???
37438     
37439     /**
37440      * Creates and adds a new region if it doesn't already exist.
37441      * @param {String} target The target region key (north, south, east, west or center).
37442      * @param {Object} config The regions config object
37443      * @return {BorderLayoutRegion} The new region
37444      */
37445     addRegion : function(config)
37446     {
37447         if(!this.regions[config.region]){
37448             var r = this.factory(config);
37449             this.bindRegion(r);
37450         }
37451         return this.regions[config.region];
37452     },
37453
37454     // private (kinda)
37455     bindRegion : function(r){
37456         this.regions[r.config.region] = r;
37457         
37458         r.on("visibilitychange",    this.layout, this);
37459         r.on("paneladded",          this.layout, this);
37460         r.on("panelremoved",        this.layout, this);
37461         r.on("invalidated",         this.layout, this);
37462         r.on("resized",             this.onRegionResized, this);
37463         r.on("collapsed",           this.onRegionCollapsed, this);
37464         r.on("expanded",            this.onRegionExpanded, this);
37465     },
37466
37467     /**
37468      * Performs a layout update.
37469      */
37470     layout : function()
37471     {
37472         if(this.updating) {
37473             return;
37474         }
37475         
37476         // render all the rebions if they have not been done alreayd?
37477         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37478             if(this.regions[region] && !this.regions[region].bodyEl){
37479                 this.regions[region].onRender(this.el)
37480             }
37481         },this);
37482         
37483         var size = this.getViewSize();
37484         var w = size.width;
37485         var h = size.height;
37486         var centerW = w;
37487         var centerH = h;
37488         var centerY = 0;
37489         var centerX = 0;
37490         //var x = 0, y = 0;
37491
37492         var rs = this.regions;
37493         var north = rs["north"];
37494         var south = rs["south"]; 
37495         var west = rs["west"];
37496         var east = rs["east"];
37497         var center = rs["center"];
37498         //if(this.hideOnLayout){ // not supported anymore
37499             //c.el.setStyle("display", "none");
37500         //}
37501         if(north && north.isVisible()){
37502             var b = north.getBox();
37503             var m = north.getMargins();
37504             b.width = w - (m.left+m.right);
37505             b.x = m.left;
37506             b.y = m.top;
37507             centerY = b.height + b.y + m.bottom;
37508             centerH -= centerY;
37509             north.updateBox(this.safeBox(b));
37510         }
37511         if(south && south.isVisible()){
37512             var b = south.getBox();
37513             var m = south.getMargins();
37514             b.width = w - (m.left+m.right);
37515             b.x = m.left;
37516             var totalHeight = (b.height + m.top + m.bottom);
37517             b.y = h - totalHeight + m.top;
37518             centerH -= totalHeight;
37519             south.updateBox(this.safeBox(b));
37520         }
37521         if(west && west.isVisible()){
37522             var b = west.getBox();
37523             var m = west.getMargins();
37524             b.height = centerH - (m.top+m.bottom);
37525             b.x = m.left;
37526             b.y = centerY + m.top;
37527             var totalWidth = (b.width + m.left + m.right);
37528             centerX += totalWidth;
37529             centerW -= totalWidth;
37530             west.updateBox(this.safeBox(b));
37531         }
37532         if(east && east.isVisible()){
37533             var b = east.getBox();
37534             var m = east.getMargins();
37535             b.height = centerH - (m.top+m.bottom);
37536             var totalWidth = (b.width + m.left + m.right);
37537             b.x = w - totalWidth + m.left;
37538             b.y = centerY + m.top;
37539             centerW -= totalWidth;
37540             east.updateBox(this.safeBox(b));
37541         }
37542         if(center){
37543             var m = center.getMargins();
37544             var centerBox = {
37545                 x: centerX + m.left,
37546                 y: centerY + m.top,
37547                 width: centerW - (m.left+m.right),
37548                 height: centerH - (m.top+m.bottom)
37549             };
37550             //if(this.hideOnLayout){
37551                 //center.el.setStyle("display", "block");
37552             //}
37553             center.updateBox(this.safeBox(centerBox));
37554         }
37555         this.el.repaint();
37556         this.fireEvent("layout", this);
37557     },
37558
37559     // private
37560     safeBox : function(box){
37561         box.width = Math.max(0, box.width);
37562         box.height = Math.max(0, box.height);
37563         return box;
37564     },
37565
37566     /**
37567      * Adds a ContentPanel (or subclass) to this layout.
37568      * @param {String} target The target region key (north, south, east, west or center).
37569      * @param {Roo.ContentPanel} panel The panel to add
37570      * @return {Roo.ContentPanel} The added panel
37571      */
37572     add : function(target, panel){
37573          
37574         target = target.toLowerCase();
37575         return this.regions[target].add(panel);
37576     },
37577
37578     /**
37579      * Remove a ContentPanel (or subclass) to this layout.
37580      * @param {String} target The target region key (north, south, east, west or center).
37581      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37582      * @return {Roo.ContentPanel} The removed panel
37583      */
37584     remove : function(target, panel){
37585         target = target.toLowerCase();
37586         return this.regions[target].remove(panel);
37587     },
37588
37589     /**
37590      * Searches all regions for a panel with the specified id
37591      * @param {String} panelId
37592      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37593      */
37594     findPanel : function(panelId){
37595         var rs = this.regions;
37596         for(var target in rs){
37597             if(typeof rs[target] != "function"){
37598                 var p = rs[target].getPanel(panelId);
37599                 if(p){
37600                     return p;
37601                 }
37602             }
37603         }
37604         return null;
37605     },
37606
37607     /**
37608      * Searches all regions for a panel with the specified id and activates (shows) it.
37609      * @param {String/ContentPanel} panelId The panels id or the panel itself
37610      * @return {Roo.ContentPanel} The shown panel or null
37611      */
37612     showPanel : function(panelId) {
37613       var rs = this.regions;
37614       for(var target in rs){
37615          var r = rs[target];
37616          if(typeof r != "function"){
37617             if(r.hasPanel(panelId)){
37618                return r.showPanel(panelId);
37619             }
37620          }
37621       }
37622       return null;
37623    },
37624
37625    /**
37626      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37627      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37628      */
37629    /*
37630     restoreState : function(provider){
37631         if(!provider){
37632             provider = Roo.state.Manager;
37633         }
37634         var sm = new Roo.LayoutStateManager();
37635         sm.init(this, provider);
37636     },
37637 */
37638  
37639  
37640     /**
37641      * Adds a xtype elements to the layout.
37642      * <pre><code>
37643
37644 layout.addxtype({
37645        xtype : 'ContentPanel',
37646        region: 'west',
37647        items: [ .... ]
37648    }
37649 );
37650
37651 layout.addxtype({
37652         xtype : 'NestedLayoutPanel',
37653         region: 'west',
37654         layout: {
37655            center: { },
37656            west: { }   
37657         },
37658         items : [ ... list of content panels or nested layout panels.. ]
37659    }
37660 );
37661 </code></pre>
37662      * @param {Object} cfg Xtype definition of item to add.
37663      */
37664     addxtype : function(cfg)
37665     {
37666         // basically accepts a pannel...
37667         // can accept a layout region..!?!?
37668         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37669         
37670         
37671         // theory?  children can only be panels??
37672         
37673         //if (!cfg.xtype.match(/Panel$/)) {
37674         //    return false;
37675         //}
37676         var ret = false;
37677         
37678         if (typeof(cfg.region) == 'undefined') {
37679             Roo.log("Failed to add Panel, region was not set");
37680             Roo.log(cfg);
37681             return false;
37682         }
37683         var region = cfg.region;
37684         delete cfg.region;
37685         
37686           
37687         var xitems = [];
37688         if (cfg.items) {
37689             xitems = cfg.items;
37690             delete cfg.items;
37691         }
37692         var nb = false;
37693         
37694         if ( region == 'center') {
37695             Roo.log("Center: " + cfg.title);
37696         }
37697         
37698         
37699         switch(cfg.xtype) 
37700         {
37701             case 'Content':  // ContentPanel (el, cfg)
37702             case 'Scroll':  // ContentPanel (el, cfg)
37703             case 'View': 
37704                 cfg.autoCreate = cfg.autoCreate || true;
37705                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37706                 //} else {
37707                 //    var el = this.el.createChild();
37708                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37709                 //}
37710                 
37711                 this.add(region, ret);
37712                 break;
37713             
37714             /*
37715             case 'TreePanel': // our new panel!
37716                 cfg.el = this.el.createChild();
37717                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37718                 this.add(region, ret);
37719                 break;
37720             */
37721             
37722             case 'Nest': 
37723                 // create a new Layout (which is  a Border Layout...
37724                 
37725                 var clayout = cfg.layout;
37726                 clayout.el  = this.el.createChild();
37727                 clayout.items   = clayout.items  || [];
37728                 
37729                 delete cfg.layout;
37730                 
37731                 // replace this exitems with the clayout ones..
37732                 xitems = clayout.items;
37733                  
37734                 // force background off if it's in center...
37735                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37736                     cfg.background = false;
37737                 }
37738                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37739                 
37740                 
37741                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37742                 //console.log('adding nested layout panel '  + cfg.toSource());
37743                 this.add(region, ret);
37744                 nb = {}; /// find first...
37745                 break;
37746             
37747             case 'Grid':
37748                 
37749                 // needs grid and region
37750                 
37751                 //var el = this.getRegion(region).el.createChild();
37752                 /*
37753                  *var el = this.el.createChild();
37754                 // create the grid first...
37755                 cfg.grid.container = el;
37756                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37757                 */
37758                 
37759                 if (region == 'center' && this.active ) {
37760                     cfg.background = false;
37761                 }
37762                 
37763                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37764                 
37765                 this.add(region, ret);
37766                 /*
37767                 if (cfg.background) {
37768                     // render grid on panel activation (if panel background)
37769                     ret.on('activate', function(gp) {
37770                         if (!gp.grid.rendered) {
37771                     //        gp.grid.render(el);
37772                         }
37773                     });
37774                 } else {
37775                   //  cfg.grid.render(el);
37776                 }
37777                 */
37778                 break;
37779            
37780            
37781             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37782                 // it was the old xcomponent building that caused this before.
37783                 // espeically if border is the top element in the tree.
37784                 ret = this;
37785                 break; 
37786                 
37787                     
37788                 
37789                 
37790                 
37791             default:
37792                 /*
37793                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37794                     
37795                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37796                     this.add(region, ret);
37797                 } else {
37798                 */
37799                     Roo.log(cfg);
37800                     throw "Can not add '" + cfg.xtype + "' to Border";
37801                     return null;
37802              
37803                                 
37804              
37805         }
37806         this.beginUpdate();
37807         // add children..
37808         var region = '';
37809         var abn = {};
37810         Roo.each(xitems, function(i)  {
37811             region = nb && i.region ? i.region : false;
37812             
37813             var add = ret.addxtype(i);
37814            
37815             if (region) {
37816                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37817                 if (!i.background) {
37818                     abn[region] = nb[region] ;
37819                 }
37820             }
37821             
37822         });
37823         this.endUpdate();
37824
37825         // make the last non-background panel active..
37826         //if (nb) { Roo.log(abn); }
37827         if (nb) {
37828             
37829             for(var r in abn) {
37830                 region = this.getRegion(r);
37831                 if (region) {
37832                     // tried using nb[r], but it does not work..
37833                      
37834                     region.showPanel(abn[r]);
37835                    
37836                 }
37837             }
37838         }
37839         return ret;
37840         
37841     },
37842     
37843     
37844 // private
37845     factory : function(cfg)
37846     {
37847         
37848         var validRegions = Roo.bootstrap.layout.Border.regions;
37849
37850         var target = cfg.region;
37851         cfg.mgr = this;
37852         
37853         var r = Roo.bootstrap.layout;
37854         Roo.log(target);
37855         switch(target){
37856             case "north":
37857                 return new r.North(cfg);
37858             case "south":
37859                 return new r.South(cfg);
37860             case "east":
37861                 return new r.East(cfg);
37862             case "west":
37863                 return new r.West(cfg);
37864             case "center":
37865                 return new r.Center(cfg);
37866         }
37867         throw 'Layout region "'+target+'" not supported.';
37868     }
37869     
37870     
37871 });
37872  /*
37873  * Based on:
37874  * Ext JS Library 1.1.1
37875  * Copyright(c) 2006-2007, Ext JS, LLC.
37876  *
37877  * Originally Released Under LGPL - original licence link has changed is not relivant.
37878  *
37879  * Fork - LGPL
37880  * <script type="text/javascript">
37881  */
37882  
37883 /**
37884  * @class Roo.bootstrap.layout.Basic
37885  * @extends Roo.util.Observable
37886  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37887  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37888  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37889  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37890  * @cfg {string}   region  the region that it inhabits..
37891  * @cfg {bool}   skipConfig skip config?
37892  * 
37893
37894  */
37895 Roo.bootstrap.layout.Basic = function(config){
37896     
37897     this.mgr = config.mgr;
37898     
37899     this.position = config.region;
37900     
37901     var skipConfig = config.skipConfig;
37902     
37903     this.events = {
37904         /**
37905          * @scope Roo.BasicLayoutRegion
37906          */
37907         
37908         /**
37909          * @event beforeremove
37910          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37911          * @param {Roo.LayoutRegion} this
37912          * @param {Roo.ContentPanel} panel The panel
37913          * @param {Object} e The cancel event object
37914          */
37915         "beforeremove" : true,
37916         /**
37917          * @event invalidated
37918          * Fires when the layout for this region is changed.
37919          * @param {Roo.LayoutRegion} this
37920          */
37921         "invalidated" : true,
37922         /**
37923          * @event visibilitychange
37924          * Fires when this region is shown or hidden 
37925          * @param {Roo.LayoutRegion} this
37926          * @param {Boolean} visibility true or false
37927          */
37928         "visibilitychange" : true,
37929         /**
37930          * @event paneladded
37931          * Fires when a panel is added. 
37932          * @param {Roo.LayoutRegion} this
37933          * @param {Roo.ContentPanel} panel The panel
37934          */
37935         "paneladded" : true,
37936         /**
37937          * @event panelremoved
37938          * Fires when a panel is removed. 
37939          * @param {Roo.LayoutRegion} this
37940          * @param {Roo.ContentPanel} panel The panel
37941          */
37942         "panelremoved" : true,
37943         /**
37944          * @event beforecollapse
37945          * Fires when this region before collapse.
37946          * @param {Roo.LayoutRegion} this
37947          */
37948         "beforecollapse" : true,
37949         /**
37950          * @event collapsed
37951          * Fires when this region is collapsed.
37952          * @param {Roo.LayoutRegion} this
37953          */
37954         "collapsed" : true,
37955         /**
37956          * @event expanded
37957          * Fires when this region is expanded.
37958          * @param {Roo.LayoutRegion} this
37959          */
37960         "expanded" : true,
37961         /**
37962          * @event slideshow
37963          * Fires when this region is slid into view.
37964          * @param {Roo.LayoutRegion} this
37965          */
37966         "slideshow" : true,
37967         /**
37968          * @event slidehide
37969          * Fires when this region slides out of view. 
37970          * @param {Roo.LayoutRegion} this
37971          */
37972         "slidehide" : true,
37973         /**
37974          * @event panelactivated
37975          * Fires when a panel is activated. 
37976          * @param {Roo.LayoutRegion} this
37977          * @param {Roo.ContentPanel} panel The activated panel
37978          */
37979         "panelactivated" : true,
37980         /**
37981          * @event resized
37982          * Fires when the user resizes this region. 
37983          * @param {Roo.LayoutRegion} this
37984          * @param {Number} newSize The new size (width for east/west, height for north/south)
37985          */
37986         "resized" : true
37987     };
37988     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37989     this.panels = new Roo.util.MixedCollection();
37990     this.panels.getKey = this.getPanelId.createDelegate(this);
37991     this.box = null;
37992     this.activePanel = null;
37993     // ensure listeners are added...
37994     
37995     if (config.listeners || config.events) {
37996         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37997             listeners : config.listeners || {},
37998             events : config.events || {}
37999         });
38000     }
38001     
38002     if(skipConfig !== true){
38003         this.applyConfig(config);
38004     }
38005 };
38006
38007 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38008 {
38009     getPanelId : function(p){
38010         return p.getId();
38011     },
38012     
38013     applyConfig : function(config){
38014         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38015         this.config = config;
38016         
38017     },
38018     
38019     /**
38020      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38021      * the width, for horizontal (north, south) the height.
38022      * @param {Number} newSize The new width or height
38023      */
38024     resizeTo : function(newSize){
38025         var el = this.el ? this.el :
38026                  (this.activePanel ? this.activePanel.getEl() : null);
38027         if(el){
38028             switch(this.position){
38029                 case "east":
38030                 case "west":
38031                     el.setWidth(newSize);
38032                     this.fireEvent("resized", this, newSize);
38033                 break;
38034                 case "north":
38035                 case "south":
38036                     el.setHeight(newSize);
38037                     this.fireEvent("resized", this, newSize);
38038                 break;                
38039             }
38040         }
38041     },
38042     
38043     getBox : function(){
38044         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38045     },
38046     
38047     getMargins : function(){
38048         return this.margins;
38049     },
38050     
38051     updateBox : function(box){
38052         this.box = box;
38053         var el = this.activePanel.getEl();
38054         el.dom.style.left = box.x + "px";
38055         el.dom.style.top = box.y + "px";
38056         this.activePanel.setSize(box.width, box.height);
38057     },
38058     
38059     /**
38060      * Returns the container element for this region.
38061      * @return {Roo.Element}
38062      */
38063     getEl : function(){
38064         return this.activePanel;
38065     },
38066     
38067     /**
38068      * Returns true if this region is currently visible.
38069      * @return {Boolean}
38070      */
38071     isVisible : function(){
38072         return this.activePanel ? true : false;
38073     },
38074     
38075     setActivePanel : function(panel){
38076         panel = this.getPanel(panel);
38077         if(this.activePanel && this.activePanel != panel){
38078             this.activePanel.setActiveState(false);
38079             this.activePanel.getEl().setLeftTop(-10000,-10000);
38080         }
38081         this.activePanel = panel;
38082         panel.setActiveState(true);
38083         if(this.box){
38084             panel.setSize(this.box.width, this.box.height);
38085         }
38086         this.fireEvent("panelactivated", this, panel);
38087         this.fireEvent("invalidated");
38088     },
38089     
38090     /**
38091      * Show the specified panel.
38092      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38093      * @return {Roo.ContentPanel} The shown panel or null
38094      */
38095     showPanel : function(panel){
38096         panel = this.getPanel(panel);
38097         if(panel){
38098             this.setActivePanel(panel);
38099         }
38100         return panel;
38101     },
38102     
38103     /**
38104      * Get the active panel for this region.
38105      * @return {Roo.ContentPanel} The active panel or null
38106      */
38107     getActivePanel : function(){
38108         return this.activePanel;
38109     },
38110     
38111     /**
38112      * Add the passed ContentPanel(s)
38113      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38114      * @return {Roo.ContentPanel} The panel added (if only one was added)
38115      */
38116     add : function(panel){
38117         if(arguments.length > 1){
38118             for(var i = 0, len = arguments.length; i < len; i++) {
38119                 this.add(arguments[i]);
38120             }
38121             return null;
38122         }
38123         if(this.hasPanel(panel)){
38124             this.showPanel(panel);
38125             return panel;
38126         }
38127         var el = panel.getEl();
38128         if(el.dom.parentNode != this.mgr.el.dom){
38129             this.mgr.el.dom.appendChild(el.dom);
38130         }
38131         if(panel.setRegion){
38132             panel.setRegion(this);
38133         }
38134         this.panels.add(panel);
38135         el.setStyle("position", "absolute");
38136         if(!panel.background){
38137             this.setActivePanel(panel);
38138             if(this.config.initialSize && this.panels.getCount()==1){
38139                 this.resizeTo(this.config.initialSize);
38140             }
38141         }
38142         this.fireEvent("paneladded", this, panel);
38143         return panel;
38144     },
38145     
38146     /**
38147      * Returns true if the panel is in this region.
38148      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38149      * @return {Boolean}
38150      */
38151     hasPanel : function(panel){
38152         if(typeof panel == "object"){ // must be panel obj
38153             panel = panel.getId();
38154         }
38155         return this.getPanel(panel) ? true : false;
38156     },
38157     
38158     /**
38159      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38160      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38161      * @param {Boolean} preservePanel Overrides the config preservePanel option
38162      * @return {Roo.ContentPanel} The panel that was removed
38163      */
38164     remove : function(panel, preservePanel){
38165         panel = this.getPanel(panel);
38166         if(!panel){
38167             return null;
38168         }
38169         var e = {};
38170         this.fireEvent("beforeremove", this, panel, e);
38171         if(e.cancel === true){
38172             return null;
38173         }
38174         var panelId = panel.getId();
38175         this.panels.removeKey(panelId);
38176         return panel;
38177     },
38178     
38179     /**
38180      * Returns the panel specified or null if it's not in this region.
38181      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38182      * @return {Roo.ContentPanel}
38183      */
38184     getPanel : function(id){
38185         if(typeof id == "object"){ // must be panel obj
38186             return id;
38187         }
38188         return this.panels.get(id);
38189     },
38190     
38191     /**
38192      * Returns this regions position (north/south/east/west/center).
38193      * @return {String} 
38194      */
38195     getPosition: function(){
38196         return this.position;    
38197     }
38198 });/*
38199  * Based on:
38200  * Ext JS Library 1.1.1
38201  * Copyright(c) 2006-2007, Ext JS, LLC.
38202  *
38203  * Originally Released Under LGPL - original licence link has changed is not relivant.
38204  *
38205  * Fork - LGPL
38206  * <script type="text/javascript">
38207  */
38208  
38209 /**
38210  * @class Roo.bootstrap.layout.Region
38211  * @extends Roo.bootstrap.layout.Basic
38212  * This class represents a region in a layout manager.
38213  
38214  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38215  * @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})
38216  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38217  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38218  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38219  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38220  * @cfg {String}    title           The title for the region (overrides panel titles)
38221  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38222  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38223  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38224  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38225  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38226  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38227  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38228  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38229  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38230  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38231
38232  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38233  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38234  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38235  * @cfg {Number}    width           For East/West panels
38236  * @cfg {Number}    height          For North/South panels
38237  * @cfg {Boolean}   split           To show the splitter
38238  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38239  * 
38240  * @cfg {string}   cls             Extra CSS classes to add to region
38241  * 
38242  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38243  * @cfg {string}   region  the region that it inhabits..
38244  *
38245
38246  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38247  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38248
38249  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38250  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38251  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38252  */
38253 Roo.bootstrap.layout.Region = function(config)
38254 {
38255     this.applyConfig(config);
38256
38257     var mgr = config.mgr;
38258     var pos = config.region;
38259     config.skipConfig = true;
38260     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38261     
38262     if (mgr.el) {
38263         this.onRender(mgr.el);   
38264     }
38265      
38266     this.visible = true;
38267     this.collapsed = false;
38268     this.unrendered_panels = [];
38269 };
38270
38271 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38272
38273     position: '', // set by wrapper (eg. north/south etc..)
38274     unrendered_panels : null,  // unrendered panels.
38275     
38276     tabPosition : false,
38277     
38278     mgr: false, // points to 'Border'
38279     
38280     
38281     createBody : function(){
38282         /** This region's body element 
38283         * @type Roo.Element */
38284         this.bodyEl = this.el.createChild({
38285                 tag: "div",
38286                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38287         });
38288     },
38289
38290     onRender: function(ctr, pos)
38291     {
38292         var dh = Roo.DomHelper;
38293         /** This region's container element 
38294         * @type Roo.Element */
38295         this.el = dh.append(ctr.dom, {
38296                 tag: "div",
38297                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38298             }, true);
38299         /** This region's title element 
38300         * @type Roo.Element */
38301     
38302         this.titleEl = dh.append(this.el.dom,  {
38303                 tag: "div",
38304                 unselectable: "on",
38305                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38306                 children:[
38307                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38308                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38309                 ]
38310             }, true);
38311         
38312         this.titleEl.enableDisplayMode();
38313         /** This region's title text element 
38314         * @type HTMLElement */
38315         this.titleTextEl = this.titleEl.dom.firstChild;
38316         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38317         /*
38318         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38319         this.closeBtn.enableDisplayMode();
38320         this.closeBtn.on("click", this.closeClicked, this);
38321         this.closeBtn.hide();
38322     */
38323         this.createBody(this.config);
38324         if(this.config.hideWhenEmpty){
38325             this.hide();
38326             this.on("paneladded", this.validateVisibility, this);
38327             this.on("panelremoved", this.validateVisibility, this);
38328         }
38329         if(this.autoScroll){
38330             this.bodyEl.setStyle("overflow", "auto");
38331         }else{
38332             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38333         }
38334         //if(c.titlebar !== false){
38335             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38336                 this.titleEl.hide();
38337             }else{
38338                 this.titleEl.show();
38339                 if(this.config.title){
38340                     this.titleTextEl.innerHTML = this.config.title;
38341                 }
38342             }
38343         //}
38344         if(this.config.collapsed){
38345             this.collapse(true);
38346         }
38347         if(this.config.hidden){
38348             this.hide();
38349         }
38350         
38351         if (this.unrendered_panels && this.unrendered_panels.length) {
38352             for (var i =0;i< this.unrendered_panels.length; i++) {
38353                 this.add(this.unrendered_panels[i]);
38354             }
38355             this.unrendered_panels = null;
38356             
38357         }
38358         
38359     },
38360     
38361     applyConfig : function(c)
38362     {
38363         /*
38364          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38365             var dh = Roo.DomHelper;
38366             if(c.titlebar !== false){
38367                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38368                 this.collapseBtn.on("click", this.collapse, this);
38369                 this.collapseBtn.enableDisplayMode();
38370                 /*
38371                 if(c.showPin === true || this.showPin){
38372                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38373                     this.stickBtn.enableDisplayMode();
38374                     this.stickBtn.on("click", this.expand, this);
38375                     this.stickBtn.hide();
38376                 }
38377                 
38378             }
38379             */
38380             /** This region's collapsed element
38381             * @type Roo.Element */
38382             /*
38383              *
38384             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38385                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38386             ]}, true);
38387             
38388             if(c.floatable !== false){
38389                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38390                this.collapsedEl.on("click", this.collapseClick, this);
38391             }
38392
38393             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38394                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38395                    id: "message", unselectable: "on", style:{"float":"left"}});
38396                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38397              }
38398             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38399             this.expandBtn.on("click", this.expand, this);
38400             
38401         }
38402         
38403         if(this.collapseBtn){
38404             this.collapseBtn.setVisible(c.collapsible == true);
38405         }
38406         
38407         this.cmargins = c.cmargins || this.cmargins ||
38408                          (this.position == "west" || this.position == "east" ?
38409                              {top: 0, left: 2, right:2, bottom: 0} :
38410                              {top: 2, left: 0, right:0, bottom: 2});
38411         */
38412         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38413         
38414         
38415         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38416         
38417         this.autoScroll = c.autoScroll || false;
38418         
38419         
38420        
38421         
38422         this.duration = c.duration || .30;
38423         this.slideDuration = c.slideDuration || .45;
38424         this.config = c;
38425        
38426     },
38427     /**
38428      * Returns true if this region is currently visible.
38429      * @return {Boolean}
38430      */
38431     isVisible : function(){
38432         return this.visible;
38433     },
38434
38435     /**
38436      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38437      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38438      */
38439     //setCollapsedTitle : function(title){
38440     //    title = title || "&#160;";
38441      //   if(this.collapsedTitleTextEl){
38442       //      this.collapsedTitleTextEl.innerHTML = title;
38443        // }
38444     //},
38445
38446     getBox : function(){
38447         var b;
38448       //  if(!this.collapsed){
38449             b = this.el.getBox(false, true);
38450        // }else{
38451           //  b = this.collapsedEl.getBox(false, true);
38452         //}
38453         return b;
38454     },
38455
38456     getMargins : function(){
38457         return this.margins;
38458         //return this.collapsed ? this.cmargins : this.margins;
38459     },
38460 /*
38461     highlight : function(){
38462         this.el.addClass("x-layout-panel-dragover");
38463     },
38464
38465     unhighlight : function(){
38466         this.el.removeClass("x-layout-panel-dragover");
38467     },
38468 */
38469     updateBox : function(box)
38470     {
38471         if (!this.bodyEl) {
38472             return; // not rendered yet..
38473         }
38474         
38475         this.box = box;
38476         if(!this.collapsed){
38477             this.el.dom.style.left = box.x + "px";
38478             this.el.dom.style.top = box.y + "px";
38479             this.updateBody(box.width, box.height);
38480         }else{
38481             this.collapsedEl.dom.style.left = box.x + "px";
38482             this.collapsedEl.dom.style.top = box.y + "px";
38483             this.collapsedEl.setSize(box.width, box.height);
38484         }
38485         if(this.tabs){
38486             this.tabs.autoSizeTabs();
38487         }
38488     },
38489
38490     updateBody : function(w, h)
38491     {
38492         if(w !== null){
38493             this.el.setWidth(w);
38494             w -= this.el.getBorderWidth("rl");
38495             if(this.config.adjustments){
38496                 w += this.config.adjustments[0];
38497             }
38498         }
38499         if(h !== null && h > 0){
38500             this.el.setHeight(h);
38501             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38502             h -= this.el.getBorderWidth("tb");
38503             if(this.config.adjustments){
38504                 h += this.config.adjustments[1];
38505             }
38506             this.bodyEl.setHeight(h);
38507             if(this.tabs){
38508                 h = this.tabs.syncHeight(h);
38509             }
38510         }
38511         if(this.panelSize){
38512             w = w !== null ? w : this.panelSize.width;
38513             h = h !== null ? h : this.panelSize.height;
38514         }
38515         if(this.activePanel){
38516             var el = this.activePanel.getEl();
38517             w = w !== null ? w : el.getWidth();
38518             h = h !== null ? h : el.getHeight();
38519             this.panelSize = {width: w, height: h};
38520             this.activePanel.setSize(w, h);
38521         }
38522         if(Roo.isIE && this.tabs){
38523             this.tabs.el.repaint();
38524         }
38525     },
38526
38527     /**
38528      * Returns the container element for this region.
38529      * @return {Roo.Element}
38530      */
38531     getEl : function(){
38532         return this.el;
38533     },
38534
38535     /**
38536      * Hides this region.
38537      */
38538     hide : function(){
38539         //if(!this.collapsed){
38540             this.el.dom.style.left = "-2000px";
38541             this.el.hide();
38542         //}else{
38543          //   this.collapsedEl.dom.style.left = "-2000px";
38544          //   this.collapsedEl.hide();
38545        // }
38546         this.visible = false;
38547         this.fireEvent("visibilitychange", this, false);
38548     },
38549
38550     /**
38551      * Shows this region if it was previously hidden.
38552      */
38553     show : function(){
38554         //if(!this.collapsed){
38555             this.el.show();
38556         //}else{
38557         //    this.collapsedEl.show();
38558        // }
38559         this.visible = true;
38560         this.fireEvent("visibilitychange", this, true);
38561     },
38562 /*
38563     closeClicked : function(){
38564         if(this.activePanel){
38565             this.remove(this.activePanel);
38566         }
38567     },
38568
38569     collapseClick : function(e){
38570         if(this.isSlid){
38571            e.stopPropagation();
38572            this.slideIn();
38573         }else{
38574            e.stopPropagation();
38575            this.slideOut();
38576         }
38577     },
38578 */
38579     /**
38580      * Collapses this region.
38581      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38582      */
38583     /*
38584     collapse : function(skipAnim, skipCheck = false){
38585         if(this.collapsed) {
38586             return;
38587         }
38588         
38589         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38590             
38591             this.collapsed = true;
38592             if(this.split){
38593                 this.split.el.hide();
38594             }
38595             if(this.config.animate && skipAnim !== true){
38596                 this.fireEvent("invalidated", this);
38597                 this.animateCollapse();
38598             }else{
38599                 this.el.setLocation(-20000,-20000);
38600                 this.el.hide();
38601                 this.collapsedEl.show();
38602                 this.fireEvent("collapsed", this);
38603                 this.fireEvent("invalidated", this);
38604             }
38605         }
38606         
38607     },
38608 */
38609     animateCollapse : function(){
38610         // overridden
38611     },
38612
38613     /**
38614      * Expands this region if it was previously collapsed.
38615      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38616      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38617      */
38618     /*
38619     expand : function(e, skipAnim){
38620         if(e) {
38621             e.stopPropagation();
38622         }
38623         if(!this.collapsed || this.el.hasActiveFx()) {
38624             return;
38625         }
38626         if(this.isSlid){
38627             this.afterSlideIn();
38628             skipAnim = true;
38629         }
38630         this.collapsed = false;
38631         if(this.config.animate && skipAnim !== true){
38632             this.animateExpand();
38633         }else{
38634             this.el.show();
38635             if(this.split){
38636                 this.split.el.show();
38637             }
38638             this.collapsedEl.setLocation(-2000,-2000);
38639             this.collapsedEl.hide();
38640             this.fireEvent("invalidated", this);
38641             this.fireEvent("expanded", this);
38642         }
38643     },
38644 */
38645     animateExpand : function(){
38646         // overridden
38647     },
38648
38649     initTabs : function()
38650     {
38651         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38652         
38653         var ts = new Roo.bootstrap.panel.Tabs({
38654             el: this.bodyEl.dom,
38655             region : this,
38656             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38657             disableTooltips: this.config.disableTabTips,
38658             toolbar : this.config.toolbar
38659         });
38660         
38661         if(this.config.hideTabs){
38662             ts.stripWrap.setDisplayed(false);
38663         }
38664         this.tabs = ts;
38665         ts.resizeTabs = this.config.resizeTabs === true;
38666         ts.minTabWidth = this.config.minTabWidth || 40;
38667         ts.maxTabWidth = this.config.maxTabWidth || 250;
38668         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38669         ts.monitorResize = false;
38670         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38671         ts.bodyEl.addClass('roo-layout-tabs-body');
38672         this.panels.each(this.initPanelAsTab, this);
38673     },
38674
38675     initPanelAsTab : function(panel){
38676         var ti = this.tabs.addTab(
38677             panel.getEl().id,
38678             panel.getTitle(),
38679             null,
38680             this.config.closeOnTab && panel.isClosable(),
38681             panel.tpl
38682         );
38683         if(panel.tabTip !== undefined){
38684             ti.setTooltip(panel.tabTip);
38685         }
38686         ti.on("activate", function(){
38687               this.setActivePanel(panel);
38688         }, this);
38689         
38690         if(this.config.closeOnTab){
38691             ti.on("beforeclose", function(t, e){
38692                 e.cancel = true;
38693                 this.remove(panel);
38694             }, this);
38695         }
38696         
38697         panel.tabItem = ti;
38698         
38699         return ti;
38700     },
38701
38702     updatePanelTitle : function(panel, title)
38703     {
38704         if(this.activePanel == panel){
38705             this.updateTitle(title);
38706         }
38707         if(this.tabs){
38708             var ti = this.tabs.getTab(panel.getEl().id);
38709             ti.setText(title);
38710             if(panel.tabTip !== undefined){
38711                 ti.setTooltip(panel.tabTip);
38712             }
38713         }
38714     },
38715
38716     updateTitle : function(title){
38717         if(this.titleTextEl && !this.config.title){
38718             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38719         }
38720     },
38721
38722     setActivePanel : function(panel)
38723     {
38724         panel = this.getPanel(panel);
38725         if(this.activePanel && this.activePanel != panel){
38726             if(this.activePanel.setActiveState(false) === false){
38727                 return;
38728             }
38729         }
38730         this.activePanel = panel;
38731         panel.setActiveState(true);
38732         if(this.panelSize){
38733             panel.setSize(this.panelSize.width, this.panelSize.height);
38734         }
38735         if(this.closeBtn){
38736             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38737         }
38738         this.updateTitle(panel.getTitle());
38739         if(this.tabs){
38740             this.fireEvent("invalidated", this);
38741         }
38742         this.fireEvent("panelactivated", this, panel);
38743     },
38744
38745     /**
38746      * Shows the specified panel.
38747      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38748      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38749      */
38750     showPanel : function(panel)
38751     {
38752         panel = this.getPanel(panel);
38753         if(panel){
38754             if(this.tabs){
38755                 var tab = this.tabs.getTab(panel.getEl().id);
38756                 if(tab.isHidden()){
38757                     this.tabs.unhideTab(tab.id);
38758                 }
38759                 tab.activate();
38760             }else{
38761                 this.setActivePanel(panel);
38762             }
38763         }
38764         return panel;
38765     },
38766
38767     /**
38768      * Get the active panel for this region.
38769      * @return {Roo.ContentPanel} The active panel or null
38770      */
38771     getActivePanel : function(){
38772         return this.activePanel;
38773     },
38774
38775     validateVisibility : function(){
38776         if(this.panels.getCount() < 1){
38777             this.updateTitle("&#160;");
38778             this.closeBtn.hide();
38779             this.hide();
38780         }else{
38781             if(!this.isVisible()){
38782                 this.show();
38783             }
38784         }
38785     },
38786
38787     /**
38788      * Adds the passed ContentPanel(s) to this region.
38789      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38790      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38791      */
38792     add : function(panel)
38793     {
38794         if(arguments.length > 1){
38795             for(var i = 0, len = arguments.length; i < len; i++) {
38796                 this.add(arguments[i]);
38797             }
38798             return null;
38799         }
38800         
38801         // if we have not been rendered yet, then we can not really do much of this..
38802         if (!this.bodyEl) {
38803             this.unrendered_panels.push(panel);
38804             return panel;
38805         }
38806         
38807         
38808         
38809         
38810         if(this.hasPanel(panel)){
38811             this.showPanel(panel);
38812             return panel;
38813         }
38814         panel.setRegion(this);
38815         this.panels.add(panel);
38816        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38817             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38818             // and hide them... ???
38819             this.bodyEl.dom.appendChild(panel.getEl().dom);
38820             if(panel.background !== true){
38821                 this.setActivePanel(panel);
38822             }
38823             this.fireEvent("paneladded", this, panel);
38824             return panel;
38825         }
38826         */
38827         if(!this.tabs){
38828             this.initTabs();
38829         }else{
38830             this.initPanelAsTab(panel);
38831         }
38832         
38833         
38834         if(panel.background !== true){
38835             this.tabs.activate(panel.getEl().id);
38836         }
38837         this.fireEvent("paneladded", this, panel);
38838         return panel;
38839     },
38840
38841     /**
38842      * Hides the tab for the specified panel.
38843      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38844      */
38845     hidePanel : function(panel){
38846         if(this.tabs && (panel = this.getPanel(panel))){
38847             this.tabs.hideTab(panel.getEl().id);
38848         }
38849     },
38850
38851     /**
38852      * Unhides the tab for a previously hidden panel.
38853      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38854      */
38855     unhidePanel : function(panel){
38856         if(this.tabs && (panel = this.getPanel(panel))){
38857             this.tabs.unhideTab(panel.getEl().id);
38858         }
38859     },
38860
38861     clearPanels : function(){
38862         while(this.panels.getCount() > 0){
38863              this.remove(this.panels.first());
38864         }
38865     },
38866
38867     /**
38868      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38869      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38870      * @param {Boolean} preservePanel Overrides the config preservePanel option
38871      * @return {Roo.ContentPanel} The panel that was removed
38872      */
38873     remove : function(panel, preservePanel)
38874     {
38875         panel = this.getPanel(panel);
38876         if(!panel){
38877             return null;
38878         }
38879         var e = {};
38880         this.fireEvent("beforeremove", this, panel, e);
38881         if(e.cancel === true){
38882             return null;
38883         }
38884         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38885         var panelId = panel.getId();
38886         this.panels.removeKey(panelId);
38887         if(preservePanel){
38888             document.body.appendChild(panel.getEl().dom);
38889         }
38890         if(this.tabs){
38891             this.tabs.removeTab(panel.getEl().id);
38892         }else if (!preservePanel){
38893             this.bodyEl.dom.removeChild(panel.getEl().dom);
38894         }
38895         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38896             var p = this.panels.first();
38897             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38898             tempEl.appendChild(p.getEl().dom);
38899             this.bodyEl.update("");
38900             this.bodyEl.dom.appendChild(p.getEl().dom);
38901             tempEl = null;
38902             this.updateTitle(p.getTitle());
38903             this.tabs = null;
38904             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38905             this.setActivePanel(p);
38906         }
38907         panel.setRegion(null);
38908         if(this.activePanel == panel){
38909             this.activePanel = null;
38910         }
38911         if(this.config.autoDestroy !== false && preservePanel !== true){
38912             try{panel.destroy();}catch(e){}
38913         }
38914         this.fireEvent("panelremoved", this, panel);
38915         return panel;
38916     },
38917
38918     /**
38919      * Returns the TabPanel component used by this region
38920      * @return {Roo.TabPanel}
38921      */
38922     getTabs : function(){
38923         return this.tabs;
38924     },
38925
38926     createTool : function(parentEl, className){
38927         var btn = Roo.DomHelper.append(parentEl, {
38928             tag: "div",
38929             cls: "x-layout-tools-button",
38930             children: [ {
38931                 tag: "div",
38932                 cls: "roo-layout-tools-button-inner " + className,
38933                 html: "&#160;"
38934             }]
38935         }, true);
38936         btn.addClassOnOver("roo-layout-tools-button-over");
38937         return btn;
38938     }
38939 });/*
38940  * Based on:
38941  * Ext JS Library 1.1.1
38942  * Copyright(c) 2006-2007, Ext JS, LLC.
38943  *
38944  * Originally Released Under LGPL - original licence link has changed is not relivant.
38945  *
38946  * Fork - LGPL
38947  * <script type="text/javascript">
38948  */
38949  
38950
38951
38952 /**
38953  * @class Roo.SplitLayoutRegion
38954  * @extends Roo.LayoutRegion
38955  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38956  */
38957 Roo.bootstrap.layout.Split = function(config){
38958     this.cursor = config.cursor;
38959     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38960 };
38961
38962 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38963 {
38964     splitTip : "Drag to resize.",
38965     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38966     useSplitTips : false,
38967
38968     applyConfig : function(config){
38969         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38970     },
38971     
38972     onRender : function(ctr,pos) {
38973         
38974         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38975         if(!this.config.split){
38976             return;
38977         }
38978         if(!this.split){
38979             
38980             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38981                             tag: "div",
38982                             id: this.el.id + "-split",
38983                             cls: "roo-layout-split roo-layout-split-"+this.position,
38984                             html: "&#160;"
38985             });
38986             /** The SplitBar for this region 
38987             * @type Roo.SplitBar */
38988             // does not exist yet...
38989             Roo.log([this.position, this.orientation]);
38990             
38991             this.split = new Roo.bootstrap.SplitBar({
38992                 dragElement : splitEl,
38993                 resizingElement: this.el,
38994                 orientation : this.orientation
38995             });
38996             
38997             this.split.on("moved", this.onSplitMove, this);
38998             this.split.useShim = this.config.useShim === true;
38999             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39000             if(this.useSplitTips){
39001                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39002             }
39003             //if(config.collapsible){
39004             //    this.split.el.on("dblclick", this.collapse,  this);
39005             //}
39006         }
39007         if(typeof this.config.minSize != "undefined"){
39008             this.split.minSize = this.config.minSize;
39009         }
39010         if(typeof this.config.maxSize != "undefined"){
39011             this.split.maxSize = this.config.maxSize;
39012         }
39013         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39014             this.hideSplitter();
39015         }
39016         
39017     },
39018
39019     getHMaxSize : function(){
39020          var cmax = this.config.maxSize || 10000;
39021          var center = this.mgr.getRegion("center");
39022          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39023     },
39024
39025     getVMaxSize : function(){
39026          var cmax = this.config.maxSize || 10000;
39027          var center = this.mgr.getRegion("center");
39028          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39029     },
39030
39031     onSplitMove : function(split, newSize){
39032         this.fireEvent("resized", this, newSize);
39033     },
39034     
39035     /** 
39036      * Returns the {@link Roo.SplitBar} for this region.
39037      * @return {Roo.SplitBar}
39038      */
39039     getSplitBar : function(){
39040         return this.split;
39041     },
39042     
39043     hide : function(){
39044         this.hideSplitter();
39045         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39046     },
39047
39048     hideSplitter : function(){
39049         if(this.split){
39050             this.split.el.setLocation(-2000,-2000);
39051             this.split.el.hide();
39052         }
39053     },
39054
39055     show : function(){
39056         if(this.split){
39057             this.split.el.show();
39058         }
39059         Roo.bootstrap.layout.Split.superclass.show.call(this);
39060     },
39061     
39062     beforeSlide: function(){
39063         if(Roo.isGecko){// firefox overflow auto bug workaround
39064             this.bodyEl.clip();
39065             if(this.tabs) {
39066                 this.tabs.bodyEl.clip();
39067             }
39068             if(this.activePanel){
39069                 this.activePanel.getEl().clip();
39070                 
39071                 if(this.activePanel.beforeSlide){
39072                     this.activePanel.beforeSlide();
39073                 }
39074             }
39075         }
39076     },
39077     
39078     afterSlide : function(){
39079         if(Roo.isGecko){// firefox overflow auto bug workaround
39080             this.bodyEl.unclip();
39081             if(this.tabs) {
39082                 this.tabs.bodyEl.unclip();
39083             }
39084             if(this.activePanel){
39085                 this.activePanel.getEl().unclip();
39086                 if(this.activePanel.afterSlide){
39087                     this.activePanel.afterSlide();
39088                 }
39089             }
39090         }
39091     },
39092
39093     initAutoHide : function(){
39094         if(this.autoHide !== false){
39095             if(!this.autoHideHd){
39096                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39097                 this.autoHideHd = {
39098                     "mouseout": function(e){
39099                         if(!e.within(this.el, true)){
39100                             st.delay(500);
39101                         }
39102                     },
39103                     "mouseover" : function(e){
39104                         st.cancel();
39105                     },
39106                     scope : this
39107                 };
39108             }
39109             this.el.on(this.autoHideHd);
39110         }
39111     },
39112
39113     clearAutoHide : function(){
39114         if(this.autoHide !== false){
39115             this.el.un("mouseout", this.autoHideHd.mouseout);
39116             this.el.un("mouseover", this.autoHideHd.mouseover);
39117         }
39118     },
39119
39120     clearMonitor : function(){
39121         Roo.get(document).un("click", this.slideInIf, this);
39122     },
39123
39124     // these names are backwards but not changed for compat
39125     slideOut : function(){
39126         if(this.isSlid || this.el.hasActiveFx()){
39127             return;
39128         }
39129         this.isSlid = true;
39130         if(this.collapseBtn){
39131             this.collapseBtn.hide();
39132         }
39133         this.closeBtnState = this.closeBtn.getStyle('display');
39134         this.closeBtn.hide();
39135         if(this.stickBtn){
39136             this.stickBtn.show();
39137         }
39138         this.el.show();
39139         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39140         this.beforeSlide();
39141         this.el.setStyle("z-index", 10001);
39142         this.el.slideIn(this.getSlideAnchor(), {
39143             callback: function(){
39144                 this.afterSlide();
39145                 this.initAutoHide();
39146                 Roo.get(document).on("click", this.slideInIf, this);
39147                 this.fireEvent("slideshow", this);
39148             },
39149             scope: this,
39150             block: true
39151         });
39152     },
39153
39154     afterSlideIn : function(){
39155         this.clearAutoHide();
39156         this.isSlid = false;
39157         this.clearMonitor();
39158         this.el.setStyle("z-index", "");
39159         if(this.collapseBtn){
39160             this.collapseBtn.show();
39161         }
39162         this.closeBtn.setStyle('display', this.closeBtnState);
39163         if(this.stickBtn){
39164             this.stickBtn.hide();
39165         }
39166         this.fireEvent("slidehide", this);
39167     },
39168
39169     slideIn : function(cb){
39170         if(!this.isSlid || this.el.hasActiveFx()){
39171             Roo.callback(cb);
39172             return;
39173         }
39174         this.isSlid = false;
39175         this.beforeSlide();
39176         this.el.slideOut(this.getSlideAnchor(), {
39177             callback: function(){
39178                 this.el.setLeftTop(-10000, -10000);
39179                 this.afterSlide();
39180                 this.afterSlideIn();
39181                 Roo.callback(cb);
39182             },
39183             scope: this,
39184             block: true
39185         });
39186     },
39187     
39188     slideInIf : function(e){
39189         if(!e.within(this.el)){
39190             this.slideIn();
39191         }
39192     },
39193
39194     animateCollapse : function(){
39195         this.beforeSlide();
39196         this.el.setStyle("z-index", 20000);
39197         var anchor = this.getSlideAnchor();
39198         this.el.slideOut(anchor, {
39199             callback : function(){
39200                 this.el.setStyle("z-index", "");
39201                 this.collapsedEl.slideIn(anchor, {duration:.3});
39202                 this.afterSlide();
39203                 this.el.setLocation(-10000,-10000);
39204                 this.el.hide();
39205                 this.fireEvent("collapsed", this);
39206             },
39207             scope: this,
39208             block: true
39209         });
39210     },
39211
39212     animateExpand : function(){
39213         this.beforeSlide();
39214         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39215         this.el.setStyle("z-index", 20000);
39216         this.collapsedEl.hide({
39217             duration:.1
39218         });
39219         this.el.slideIn(this.getSlideAnchor(), {
39220             callback : function(){
39221                 this.el.setStyle("z-index", "");
39222                 this.afterSlide();
39223                 if(this.split){
39224                     this.split.el.show();
39225                 }
39226                 this.fireEvent("invalidated", this);
39227                 this.fireEvent("expanded", this);
39228             },
39229             scope: this,
39230             block: true
39231         });
39232     },
39233
39234     anchors : {
39235         "west" : "left",
39236         "east" : "right",
39237         "north" : "top",
39238         "south" : "bottom"
39239     },
39240
39241     sanchors : {
39242         "west" : "l",
39243         "east" : "r",
39244         "north" : "t",
39245         "south" : "b"
39246     },
39247
39248     canchors : {
39249         "west" : "tl-tr",
39250         "east" : "tr-tl",
39251         "north" : "tl-bl",
39252         "south" : "bl-tl"
39253     },
39254
39255     getAnchor : function(){
39256         return this.anchors[this.position];
39257     },
39258
39259     getCollapseAnchor : function(){
39260         return this.canchors[this.position];
39261     },
39262
39263     getSlideAnchor : function(){
39264         return this.sanchors[this.position];
39265     },
39266
39267     getAlignAdj : function(){
39268         var cm = this.cmargins;
39269         switch(this.position){
39270             case "west":
39271                 return [0, 0];
39272             break;
39273             case "east":
39274                 return [0, 0];
39275             break;
39276             case "north":
39277                 return [0, 0];
39278             break;
39279             case "south":
39280                 return [0, 0];
39281             break;
39282         }
39283     },
39284
39285     getExpandAdj : function(){
39286         var c = this.collapsedEl, cm = this.cmargins;
39287         switch(this.position){
39288             case "west":
39289                 return [-(cm.right+c.getWidth()+cm.left), 0];
39290             break;
39291             case "east":
39292                 return [cm.right+c.getWidth()+cm.left, 0];
39293             break;
39294             case "north":
39295                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39296             break;
39297             case "south":
39298                 return [0, cm.top+cm.bottom+c.getHeight()];
39299             break;
39300         }
39301     }
39302 });/*
39303  * Based on:
39304  * Ext JS Library 1.1.1
39305  * Copyright(c) 2006-2007, Ext JS, LLC.
39306  *
39307  * Originally Released Under LGPL - original licence link has changed is not relivant.
39308  *
39309  * Fork - LGPL
39310  * <script type="text/javascript">
39311  */
39312 /*
39313  * These classes are private internal classes
39314  */
39315 Roo.bootstrap.layout.Center = function(config){
39316     config.region = "center";
39317     Roo.bootstrap.layout.Region.call(this, config);
39318     this.visible = true;
39319     this.minWidth = config.minWidth || 20;
39320     this.minHeight = config.minHeight || 20;
39321 };
39322
39323 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39324     hide : function(){
39325         // center panel can't be hidden
39326     },
39327     
39328     show : function(){
39329         // center panel can't be hidden
39330     },
39331     
39332     getMinWidth: function(){
39333         return this.minWidth;
39334     },
39335     
39336     getMinHeight: function(){
39337         return this.minHeight;
39338     }
39339 });
39340
39341
39342
39343
39344  
39345
39346
39347
39348
39349
39350
39351 Roo.bootstrap.layout.North = function(config)
39352 {
39353     config.region = 'north';
39354     config.cursor = 'n-resize';
39355     
39356     Roo.bootstrap.layout.Split.call(this, config);
39357     
39358     
39359     if(this.split){
39360         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39361         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39362         this.split.el.addClass("roo-layout-split-v");
39363     }
39364     //var size = config.initialSize || config.height;
39365     //if(this.el && typeof size != "undefined"){
39366     //    this.el.setHeight(size);
39367     //}
39368 };
39369 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39370 {
39371     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39372      
39373      
39374     onRender : function(ctr, pos)
39375     {
39376         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39377         var size = this.config.initialSize || this.config.height;
39378         if(this.el && typeof size != "undefined"){
39379             this.el.setHeight(size);
39380         }
39381     
39382     },
39383     
39384     getBox : function(){
39385         if(this.collapsed){
39386             return this.collapsedEl.getBox();
39387         }
39388         var box = this.el.getBox();
39389         if(this.split){
39390             box.height += this.split.el.getHeight();
39391         }
39392         return box;
39393     },
39394     
39395     updateBox : function(box){
39396         if(this.split && !this.collapsed){
39397             box.height -= this.split.el.getHeight();
39398             this.split.el.setLeft(box.x);
39399             this.split.el.setTop(box.y+box.height);
39400             this.split.el.setWidth(box.width);
39401         }
39402         if(this.collapsed){
39403             this.updateBody(box.width, null);
39404         }
39405         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39406     }
39407 });
39408
39409
39410
39411
39412
39413 Roo.bootstrap.layout.South = function(config){
39414     config.region = 'south';
39415     config.cursor = 's-resize';
39416     Roo.bootstrap.layout.Split.call(this, config);
39417     if(this.split){
39418         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39419         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39420         this.split.el.addClass("roo-layout-split-v");
39421     }
39422     
39423 };
39424
39425 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39426     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39427     
39428     onRender : function(ctr, pos)
39429     {
39430         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39431         var size = this.config.initialSize || this.config.height;
39432         if(this.el && typeof size != "undefined"){
39433             this.el.setHeight(size);
39434         }
39435     
39436     },
39437     
39438     getBox : function(){
39439         if(this.collapsed){
39440             return this.collapsedEl.getBox();
39441         }
39442         var box = this.el.getBox();
39443         if(this.split){
39444             var sh = this.split.el.getHeight();
39445             box.height += sh;
39446             box.y -= sh;
39447         }
39448         return box;
39449     },
39450     
39451     updateBox : function(box){
39452         if(this.split && !this.collapsed){
39453             var sh = this.split.el.getHeight();
39454             box.height -= sh;
39455             box.y += sh;
39456             this.split.el.setLeft(box.x);
39457             this.split.el.setTop(box.y-sh);
39458             this.split.el.setWidth(box.width);
39459         }
39460         if(this.collapsed){
39461             this.updateBody(box.width, null);
39462         }
39463         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39464     }
39465 });
39466
39467 Roo.bootstrap.layout.East = function(config){
39468     config.region = "east";
39469     config.cursor = "e-resize";
39470     Roo.bootstrap.layout.Split.call(this, config);
39471     if(this.split){
39472         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39473         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39474         this.split.el.addClass("roo-layout-split-h");
39475     }
39476     
39477 };
39478 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39479     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39480     
39481     onRender : function(ctr, pos)
39482     {
39483         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39484         var size = this.config.initialSize || this.config.width;
39485         if(this.el && typeof size != "undefined"){
39486             this.el.setWidth(size);
39487         }
39488     
39489     },
39490     
39491     getBox : function(){
39492         if(this.collapsed){
39493             return this.collapsedEl.getBox();
39494         }
39495         var box = this.el.getBox();
39496         if(this.split){
39497             var sw = this.split.el.getWidth();
39498             box.width += sw;
39499             box.x -= sw;
39500         }
39501         return box;
39502     },
39503
39504     updateBox : function(box){
39505         if(this.split && !this.collapsed){
39506             var sw = this.split.el.getWidth();
39507             box.width -= sw;
39508             this.split.el.setLeft(box.x);
39509             this.split.el.setTop(box.y);
39510             this.split.el.setHeight(box.height);
39511             box.x += sw;
39512         }
39513         if(this.collapsed){
39514             this.updateBody(null, box.height);
39515         }
39516         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39517     }
39518 });
39519
39520 Roo.bootstrap.layout.West = function(config){
39521     config.region = "west";
39522     config.cursor = "w-resize";
39523     
39524     Roo.bootstrap.layout.Split.call(this, config);
39525     if(this.split){
39526         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39527         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39528         this.split.el.addClass("roo-layout-split-h");
39529     }
39530     
39531 };
39532 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39533     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39534     
39535     onRender: function(ctr, pos)
39536     {
39537         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39538         var size = this.config.initialSize || this.config.width;
39539         if(typeof size != "undefined"){
39540             this.el.setWidth(size);
39541         }
39542     },
39543     
39544     getBox : function(){
39545         if(this.collapsed){
39546             return this.collapsedEl.getBox();
39547         }
39548         var box = this.el.getBox();
39549         if (box.width == 0) {
39550             box.width = this.config.width; // kludge?
39551         }
39552         if(this.split){
39553             box.width += this.split.el.getWidth();
39554         }
39555         return box;
39556     },
39557     
39558     updateBox : function(box){
39559         if(this.split && !this.collapsed){
39560             var sw = this.split.el.getWidth();
39561             box.width -= sw;
39562             this.split.el.setLeft(box.x+box.width);
39563             this.split.el.setTop(box.y);
39564             this.split.el.setHeight(box.height);
39565         }
39566         if(this.collapsed){
39567             this.updateBody(null, box.height);
39568         }
39569         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39570     }
39571 });Roo.namespace("Roo.bootstrap.panel");/*
39572  * Based on:
39573  * Ext JS Library 1.1.1
39574  * Copyright(c) 2006-2007, Ext JS, LLC.
39575  *
39576  * Originally Released Under LGPL - original licence link has changed is not relivant.
39577  *
39578  * Fork - LGPL
39579  * <script type="text/javascript">
39580  */
39581 /**
39582  * @class Roo.ContentPanel
39583  * @extends Roo.util.Observable
39584  * A basic ContentPanel element.
39585  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39586  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39587  * @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
39588  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39589  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39590  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39591  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39592  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39593  * @cfg {String} title          The title for this panel
39594  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39595  * @cfg {String} url            Calls {@link #setUrl} with this value
39596  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39597  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39598  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39599  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39600  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39601  * @cfg {Boolean} badges render the badges
39602  * @cfg {String} cls  extra classes to use  
39603  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39604
39605  * @constructor
39606  * Create a new ContentPanel.
39607  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39608  * @param {String/Object} config A string to set only the title or a config object
39609  * @param {String} content (optional) Set the HTML content for this panel
39610  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39611  */
39612 Roo.bootstrap.panel.Content = function( config){
39613     
39614     this.tpl = config.tpl || false;
39615     
39616     var el = config.el;
39617     var content = config.content;
39618
39619     if(config.autoCreate){ // xtype is available if this is called from factory
39620         el = Roo.id();
39621     }
39622     this.el = Roo.get(el);
39623     if(!this.el && config && config.autoCreate){
39624         if(typeof config.autoCreate == "object"){
39625             if(!config.autoCreate.id){
39626                 config.autoCreate.id = config.id||el;
39627             }
39628             this.el = Roo.DomHelper.append(document.body,
39629                         config.autoCreate, true);
39630         }else{
39631             var elcfg =  {
39632                 tag: "div",
39633                 cls: (config.cls || '') +
39634                     (config.background ? ' bg-' + config.background : '') +
39635                     " roo-layout-inactive-content",
39636                 id: config.id||el
39637             };
39638             if (config.iframe) {
39639                 elcfg.cn = [
39640                     {
39641                         tag : 'iframe',
39642                         style : 'border: 0px',
39643                         src : 'about:blank'
39644                     }
39645                 ];
39646             }
39647               
39648             if (config.html) {
39649                 elcfg.html = config.html;
39650                 
39651             }
39652                         
39653             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39654             if (config.iframe) {
39655                 this.iframeEl = this.el.select('iframe',true).first();
39656             }
39657             
39658         }
39659     } 
39660     this.closable = false;
39661     this.loaded = false;
39662     this.active = false;
39663    
39664       
39665     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39666         
39667         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39668         
39669         this.wrapEl = this.el; //this.el.wrap();
39670         var ti = [];
39671         if (config.toolbar.items) {
39672             ti = config.toolbar.items ;
39673             delete config.toolbar.items ;
39674         }
39675         
39676         var nitems = [];
39677         this.toolbar.render(this.wrapEl, 'before');
39678         for(var i =0;i < ti.length;i++) {
39679           //  Roo.log(['add child', items[i]]);
39680             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39681         }
39682         this.toolbar.items = nitems;
39683         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39684         delete config.toolbar;
39685         
39686     }
39687     /*
39688     // xtype created footer. - not sure if will work as we normally have to render first..
39689     if (this.footer && !this.footer.el && this.footer.xtype) {
39690         if (!this.wrapEl) {
39691             this.wrapEl = this.el.wrap();
39692         }
39693     
39694         this.footer.container = this.wrapEl.createChild();
39695          
39696         this.footer = Roo.factory(this.footer, Roo);
39697         
39698     }
39699     */
39700     
39701      if(typeof config == "string"){
39702         this.title = config;
39703     }else{
39704         Roo.apply(this, config);
39705     }
39706     
39707     if(this.resizeEl){
39708         this.resizeEl = Roo.get(this.resizeEl, true);
39709     }else{
39710         this.resizeEl = this.el;
39711     }
39712     // handle view.xtype
39713     
39714  
39715     
39716     
39717     this.addEvents({
39718         /**
39719          * @event activate
39720          * Fires when this panel is activated. 
39721          * @param {Roo.ContentPanel} this
39722          */
39723         "activate" : true,
39724         /**
39725          * @event deactivate
39726          * Fires when this panel is activated. 
39727          * @param {Roo.ContentPanel} this
39728          */
39729         "deactivate" : true,
39730
39731         /**
39732          * @event resize
39733          * Fires when this panel is resized if fitToFrame is true.
39734          * @param {Roo.ContentPanel} this
39735          * @param {Number} width The width after any component adjustments
39736          * @param {Number} height The height after any component adjustments
39737          */
39738         "resize" : true,
39739         
39740          /**
39741          * @event render
39742          * Fires when this tab is created
39743          * @param {Roo.ContentPanel} this
39744          */
39745         "render" : true
39746         
39747         
39748         
39749     });
39750     
39751
39752     
39753     
39754     if(this.autoScroll && !this.iframe){
39755         this.resizeEl.setStyle("overflow", "auto");
39756     } else {
39757         // fix randome scrolling
39758         //this.el.on('scroll', function() {
39759         //    Roo.log('fix random scolling');
39760         //    this.scrollTo('top',0); 
39761         //});
39762     }
39763     content = content || this.content;
39764     if(content){
39765         this.setContent(content);
39766     }
39767     if(config && config.url){
39768         this.setUrl(this.url, this.params, this.loadOnce);
39769     }
39770     
39771     
39772     
39773     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39774     
39775     if (this.view && typeof(this.view.xtype) != 'undefined') {
39776         this.view.el = this.el.appendChild(document.createElement("div"));
39777         this.view = Roo.factory(this.view); 
39778         this.view.render  &&  this.view.render(false, '');  
39779     }
39780     
39781     
39782     this.fireEvent('render', this);
39783 };
39784
39785 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39786     
39787     cls : '',
39788     background : '',
39789     
39790     tabTip : '',
39791     
39792     iframe : false,
39793     iframeEl : false,
39794     
39795     setRegion : function(region){
39796         this.region = region;
39797         this.setActiveClass(region && !this.background);
39798     },
39799     
39800     
39801     setActiveClass: function(state)
39802     {
39803         if(state){
39804            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39805            this.el.setStyle('position','relative');
39806         }else{
39807            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39808            this.el.setStyle('position', 'absolute');
39809         } 
39810     },
39811     
39812     /**
39813      * Returns the toolbar for this Panel if one was configured. 
39814      * @return {Roo.Toolbar} 
39815      */
39816     getToolbar : function(){
39817         return this.toolbar;
39818     },
39819     
39820     setActiveState : function(active)
39821     {
39822         this.active = active;
39823         this.setActiveClass(active);
39824         if(!active){
39825             if(this.fireEvent("deactivate", this) === false){
39826                 return false;
39827             }
39828             return true;
39829         }
39830         this.fireEvent("activate", this);
39831         return true;
39832     },
39833     /**
39834      * Updates this panel's element (not for iframe)
39835      * @param {String} content The new content
39836      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39837     */
39838     setContent : function(content, loadScripts){
39839         if (this.iframe) {
39840             return;
39841         }
39842         
39843         this.el.update(content, loadScripts);
39844     },
39845
39846     ignoreResize : function(w, h){
39847         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39848             return true;
39849         }else{
39850             this.lastSize = {width: w, height: h};
39851             return false;
39852         }
39853     },
39854     /**
39855      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39856      * @return {Roo.UpdateManager} The UpdateManager
39857      */
39858     getUpdateManager : function(){
39859         if (this.iframe) {
39860             return false;
39861         }
39862         return this.el.getUpdateManager();
39863     },
39864      /**
39865      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39866      * Does not work with IFRAME contents
39867      * @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:
39868 <pre><code>
39869 panel.load({
39870     url: "your-url.php",
39871     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39872     callback: yourFunction,
39873     scope: yourObject, //(optional scope)
39874     discardUrl: false,
39875     nocache: false,
39876     text: "Loading...",
39877     timeout: 30,
39878     scripts: false
39879 });
39880 </code></pre>
39881      
39882      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39883      * 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.
39884      * @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}
39885      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39886      * @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.
39887      * @return {Roo.ContentPanel} this
39888      */
39889     load : function(){
39890         
39891         if (this.iframe) {
39892             return this;
39893         }
39894         
39895         var um = this.el.getUpdateManager();
39896         um.update.apply(um, arguments);
39897         return this;
39898     },
39899
39900
39901     /**
39902      * 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.
39903      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39904      * @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)
39905      * @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)
39906      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39907      */
39908     setUrl : function(url, params, loadOnce){
39909         if (this.iframe) {
39910             this.iframeEl.dom.src = url;
39911             return false;
39912         }
39913         
39914         if(this.refreshDelegate){
39915             this.removeListener("activate", this.refreshDelegate);
39916         }
39917         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39918         this.on("activate", this.refreshDelegate);
39919         return this.el.getUpdateManager();
39920     },
39921     
39922     _handleRefresh : function(url, params, loadOnce){
39923         if(!loadOnce || !this.loaded){
39924             var updater = this.el.getUpdateManager();
39925             updater.update(url, params, this._setLoaded.createDelegate(this));
39926         }
39927     },
39928     
39929     _setLoaded : function(){
39930         this.loaded = true;
39931     }, 
39932     
39933     /**
39934      * Returns this panel's id
39935      * @return {String} 
39936      */
39937     getId : function(){
39938         return this.el.id;
39939     },
39940     
39941     /** 
39942      * Returns this panel's element - used by regiosn to add.
39943      * @return {Roo.Element} 
39944      */
39945     getEl : function(){
39946         return this.wrapEl || this.el;
39947     },
39948     
39949    
39950     
39951     adjustForComponents : function(width, height)
39952     {
39953         //Roo.log('adjustForComponents ');
39954         if(this.resizeEl != this.el){
39955             width -= this.el.getFrameWidth('lr');
39956             height -= this.el.getFrameWidth('tb');
39957         }
39958         if(this.toolbar){
39959             var te = this.toolbar.getEl();
39960             te.setWidth(width);
39961             height -= te.getHeight();
39962         }
39963         if(this.footer){
39964             var te = this.footer.getEl();
39965             te.setWidth(width);
39966             height -= te.getHeight();
39967         }
39968         
39969         
39970         if(this.adjustments){
39971             width += this.adjustments[0];
39972             height += this.adjustments[1];
39973         }
39974         return {"width": width, "height": height};
39975     },
39976     
39977     setSize : function(width, height){
39978         if(this.fitToFrame && !this.ignoreResize(width, height)){
39979             if(this.fitContainer && this.resizeEl != this.el){
39980                 this.el.setSize(width, height);
39981             }
39982             var size = this.adjustForComponents(width, height);
39983             if (this.iframe) {
39984                 this.iframeEl.setSize(width,height);
39985             }
39986             
39987             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39988             this.fireEvent('resize', this, size.width, size.height);
39989             
39990             
39991         }
39992     },
39993     
39994     /**
39995      * Returns this panel's title
39996      * @return {String} 
39997      */
39998     getTitle : function(){
39999         
40000         if (typeof(this.title) != 'object') {
40001             return this.title;
40002         }
40003         
40004         var t = '';
40005         for (var k in this.title) {
40006             if (!this.title.hasOwnProperty(k)) {
40007                 continue;
40008             }
40009             
40010             if (k.indexOf('-') >= 0) {
40011                 var s = k.split('-');
40012                 for (var i = 0; i<s.length; i++) {
40013                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40014                 }
40015             } else {
40016                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40017             }
40018         }
40019         return t;
40020     },
40021     
40022     /**
40023      * Set this panel's title
40024      * @param {String} title
40025      */
40026     setTitle : function(title){
40027         this.title = title;
40028         if(this.region){
40029             this.region.updatePanelTitle(this, title);
40030         }
40031     },
40032     
40033     /**
40034      * Returns true is this panel was configured to be closable
40035      * @return {Boolean} 
40036      */
40037     isClosable : function(){
40038         return this.closable;
40039     },
40040     
40041     beforeSlide : function(){
40042         this.el.clip();
40043         this.resizeEl.clip();
40044     },
40045     
40046     afterSlide : function(){
40047         this.el.unclip();
40048         this.resizeEl.unclip();
40049     },
40050     
40051     /**
40052      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40053      *   Will fail silently if the {@link #setUrl} method has not been called.
40054      *   This does not activate the panel, just updates its content.
40055      */
40056     refresh : function(){
40057         if(this.refreshDelegate){
40058            this.loaded = false;
40059            this.refreshDelegate();
40060         }
40061     },
40062     
40063     /**
40064      * Destroys this panel
40065      */
40066     destroy : function(){
40067         this.el.removeAllListeners();
40068         var tempEl = document.createElement("span");
40069         tempEl.appendChild(this.el.dom);
40070         tempEl.innerHTML = "";
40071         this.el.remove();
40072         this.el = null;
40073     },
40074     
40075     /**
40076      * form - if the content panel contains a form - this is a reference to it.
40077      * @type {Roo.form.Form}
40078      */
40079     form : false,
40080     /**
40081      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40082      *    This contains a reference to it.
40083      * @type {Roo.View}
40084      */
40085     view : false,
40086     
40087       /**
40088      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40089      * <pre><code>
40090
40091 layout.addxtype({
40092        xtype : 'Form',
40093        items: [ .... ]
40094    }
40095 );
40096
40097 </code></pre>
40098      * @param {Object} cfg Xtype definition of item to add.
40099      */
40100     
40101     
40102     getChildContainer: function () {
40103         return this.getEl();
40104     }
40105     
40106     
40107     /*
40108         var  ret = new Roo.factory(cfg);
40109         return ret;
40110         
40111         
40112         // add form..
40113         if (cfg.xtype.match(/^Form$/)) {
40114             
40115             var el;
40116             //if (this.footer) {
40117             //    el = this.footer.container.insertSibling(false, 'before');
40118             //} else {
40119                 el = this.el.createChild();
40120             //}
40121
40122             this.form = new  Roo.form.Form(cfg);
40123             
40124             
40125             if ( this.form.allItems.length) {
40126                 this.form.render(el.dom);
40127             }
40128             return this.form;
40129         }
40130         // should only have one of theses..
40131         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40132             // views.. should not be just added - used named prop 'view''
40133             
40134             cfg.el = this.el.appendChild(document.createElement("div"));
40135             // factory?
40136             
40137             var ret = new Roo.factory(cfg);
40138              
40139              ret.render && ret.render(false, ''); // render blank..
40140             this.view = ret;
40141             return ret;
40142         }
40143         return false;
40144     }
40145     \*/
40146 });
40147  
40148 /**
40149  * @class Roo.bootstrap.panel.Grid
40150  * @extends Roo.bootstrap.panel.Content
40151  * @constructor
40152  * Create a new GridPanel.
40153  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40154  * @param {Object} config A the config object
40155   
40156  */
40157
40158
40159
40160 Roo.bootstrap.panel.Grid = function(config)
40161 {
40162     
40163       
40164     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40165         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40166
40167     config.el = this.wrapper;
40168     //this.el = this.wrapper;
40169     
40170       if (config.container) {
40171         // ctor'ed from a Border/panel.grid
40172         
40173         
40174         this.wrapper.setStyle("overflow", "hidden");
40175         this.wrapper.addClass('roo-grid-container');
40176
40177     }
40178     
40179     
40180     if(config.toolbar){
40181         var tool_el = this.wrapper.createChild();    
40182         this.toolbar = Roo.factory(config.toolbar);
40183         var ti = [];
40184         if (config.toolbar.items) {
40185             ti = config.toolbar.items ;
40186             delete config.toolbar.items ;
40187         }
40188         
40189         var nitems = [];
40190         this.toolbar.render(tool_el);
40191         for(var i =0;i < ti.length;i++) {
40192           //  Roo.log(['add child', items[i]]);
40193             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40194         }
40195         this.toolbar.items = nitems;
40196         
40197         delete config.toolbar;
40198     }
40199     
40200     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40201     config.grid.scrollBody = true;;
40202     config.grid.monitorWindowResize = false; // turn off autosizing
40203     config.grid.autoHeight = false;
40204     config.grid.autoWidth = false;
40205     
40206     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40207     
40208     if (config.background) {
40209         // render grid on panel activation (if panel background)
40210         this.on('activate', function(gp) {
40211             if (!gp.grid.rendered) {
40212                 gp.grid.render(this.wrapper);
40213                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40214             }
40215         });
40216             
40217     } else {
40218         this.grid.render(this.wrapper);
40219         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40220
40221     }
40222     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40223     // ??? needed ??? config.el = this.wrapper;
40224     
40225     
40226     
40227   
40228     // xtype created footer. - not sure if will work as we normally have to render first..
40229     if (this.footer && !this.footer.el && this.footer.xtype) {
40230         
40231         var ctr = this.grid.getView().getFooterPanel(true);
40232         this.footer.dataSource = this.grid.dataSource;
40233         this.footer = Roo.factory(this.footer, Roo);
40234         this.footer.render(ctr);
40235         
40236     }
40237     
40238     
40239     
40240     
40241      
40242 };
40243
40244 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40245     getId : function(){
40246         return this.grid.id;
40247     },
40248     
40249     /**
40250      * Returns the grid for this panel
40251      * @return {Roo.bootstrap.Table} 
40252      */
40253     getGrid : function(){
40254         return this.grid;    
40255     },
40256     
40257     setSize : function(width, height){
40258         if(!this.ignoreResize(width, height)){
40259             var grid = this.grid;
40260             var size = this.adjustForComponents(width, height);
40261             // tfoot is not a footer?
40262           
40263             
40264             var gridel = grid.getGridEl();
40265             gridel.setSize(size.width, size.height);
40266             
40267             var tbd = grid.getGridEl().select('tbody', true).first();
40268             var thd = grid.getGridEl().select('thead',true).first();
40269             var tbf= grid.getGridEl().select('tfoot', true).first();
40270
40271             if (tbf) {
40272                 size.height -= tbf.getHeight();
40273             }
40274             if (thd) {
40275                 size.height -= thd.getHeight();
40276             }
40277             
40278             tbd.setSize(size.width, size.height );
40279             // this is for the account management tab -seems to work there.
40280             var thd = grid.getGridEl().select('thead',true).first();
40281             //if (tbd) {
40282             //    tbd.setSize(size.width, size.height - thd.getHeight());
40283             //}
40284              
40285             grid.autoSize();
40286         }
40287     },
40288      
40289     
40290     
40291     beforeSlide : function(){
40292         this.grid.getView().scroller.clip();
40293     },
40294     
40295     afterSlide : function(){
40296         this.grid.getView().scroller.unclip();
40297     },
40298     
40299     destroy : function(){
40300         this.grid.destroy();
40301         delete this.grid;
40302         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40303     }
40304 });
40305
40306 /**
40307  * @class Roo.bootstrap.panel.Nest
40308  * @extends Roo.bootstrap.panel.Content
40309  * @constructor
40310  * Create a new Panel, that can contain a layout.Border.
40311  * 
40312  * 
40313  * @param {Roo.BorderLayout} layout The layout for this panel
40314  * @param {String/Object} config A string to set only the title or a config object
40315  */
40316 Roo.bootstrap.panel.Nest = function(config)
40317 {
40318     // construct with only one argument..
40319     /* FIXME - implement nicer consturctors
40320     if (layout.layout) {
40321         config = layout;
40322         layout = config.layout;
40323         delete config.layout;
40324     }
40325     if (layout.xtype && !layout.getEl) {
40326         // then layout needs constructing..
40327         layout = Roo.factory(layout, Roo);
40328     }
40329     */
40330     
40331     config.el =  config.layout.getEl();
40332     
40333     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40334     
40335     config.layout.monitorWindowResize = false; // turn off autosizing
40336     this.layout = config.layout;
40337     this.layout.getEl().addClass("roo-layout-nested-layout");
40338     this.layout.parent = this;
40339     
40340     
40341     
40342     
40343 };
40344
40345 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40346
40347     setSize : function(width, height){
40348         if(!this.ignoreResize(width, height)){
40349             var size = this.adjustForComponents(width, height);
40350             var el = this.layout.getEl();
40351             if (size.height < 1) {
40352                 el.setWidth(size.width);   
40353             } else {
40354                 el.setSize(size.width, size.height);
40355             }
40356             var touch = el.dom.offsetWidth;
40357             this.layout.layout();
40358             // ie requires a double layout on the first pass
40359             if(Roo.isIE && !this.initialized){
40360                 this.initialized = true;
40361                 this.layout.layout();
40362             }
40363         }
40364     },
40365     
40366     // activate all subpanels if not currently active..
40367     
40368     setActiveState : function(active){
40369         this.active = active;
40370         this.setActiveClass(active);
40371         
40372         if(!active){
40373             this.fireEvent("deactivate", this);
40374             return;
40375         }
40376         
40377         this.fireEvent("activate", this);
40378         // not sure if this should happen before or after..
40379         if (!this.layout) {
40380             return; // should not happen..
40381         }
40382         var reg = false;
40383         for (var r in this.layout.regions) {
40384             reg = this.layout.getRegion(r);
40385             if (reg.getActivePanel()) {
40386                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40387                 reg.setActivePanel(reg.getActivePanel());
40388                 continue;
40389             }
40390             if (!reg.panels.length) {
40391                 continue;
40392             }
40393             reg.showPanel(reg.getPanel(0));
40394         }
40395         
40396         
40397         
40398         
40399     },
40400     
40401     /**
40402      * Returns the nested BorderLayout for this panel
40403      * @return {Roo.BorderLayout} 
40404      */
40405     getLayout : function(){
40406         return this.layout;
40407     },
40408     
40409      /**
40410      * Adds a xtype elements to the layout of the nested panel
40411      * <pre><code>
40412
40413 panel.addxtype({
40414        xtype : 'ContentPanel',
40415        region: 'west',
40416        items: [ .... ]
40417    }
40418 );
40419
40420 panel.addxtype({
40421         xtype : 'NestedLayoutPanel',
40422         region: 'west',
40423         layout: {
40424            center: { },
40425            west: { }   
40426         },
40427         items : [ ... list of content panels or nested layout panels.. ]
40428    }
40429 );
40430 </code></pre>
40431      * @param {Object} cfg Xtype definition of item to add.
40432      */
40433     addxtype : function(cfg) {
40434         return this.layout.addxtype(cfg);
40435     
40436     }
40437 });/*
40438  * Based on:
40439  * Ext JS Library 1.1.1
40440  * Copyright(c) 2006-2007, Ext JS, LLC.
40441  *
40442  * Originally Released Under LGPL - original licence link has changed is not relivant.
40443  *
40444  * Fork - LGPL
40445  * <script type="text/javascript">
40446  */
40447 /**
40448  * @class Roo.TabPanel
40449  * @extends Roo.util.Observable
40450  * A lightweight tab container.
40451  * <br><br>
40452  * Usage:
40453  * <pre><code>
40454 // basic tabs 1, built from existing content
40455 var tabs = new Roo.TabPanel("tabs1");
40456 tabs.addTab("script", "View Script");
40457 tabs.addTab("markup", "View Markup");
40458 tabs.activate("script");
40459
40460 // more advanced tabs, built from javascript
40461 var jtabs = new Roo.TabPanel("jtabs");
40462 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40463
40464 // set up the UpdateManager
40465 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40466 var updater = tab2.getUpdateManager();
40467 updater.setDefaultUrl("ajax1.htm");
40468 tab2.on('activate', updater.refresh, updater, true);
40469
40470 // Use setUrl for Ajax loading
40471 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40472 tab3.setUrl("ajax2.htm", null, true);
40473
40474 // Disabled tab
40475 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40476 tab4.disable();
40477
40478 jtabs.activate("jtabs-1");
40479  * </code></pre>
40480  * @constructor
40481  * Create a new TabPanel.
40482  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40483  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40484  */
40485 Roo.bootstrap.panel.Tabs = function(config){
40486     /**
40487     * The container element for this TabPanel.
40488     * @type Roo.Element
40489     */
40490     this.el = Roo.get(config.el);
40491     delete config.el;
40492     if(config){
40493         if(typeof config == "boolean"){
40494             this.tabPosition = config ? "bottom" : "top";
40495         }else{
40496             Roo.apply(this, config);
40497         }
40498     }
40499     
40500     if(this.tabPosition == "bottom"){
40501         // if tabs are at the bottom = create the body first.
40502         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40503         this.el.addClass("roo-tabs-bottom");
40504     }
40505     // next create the tabs holders
40506     
40507     if (this.tabPosition == "west"){
40508         
40509         var reg = this.region; // fake it..
40510         while (reg) {
40511             if (!reg.mgr.parent) {
40512                 break;
40513             }
40514             reg = reg.mgr.parent.region;
40515         }
40516         Roo.log("got nest?");
40517         Roo.log(reg);
40518         if (reg.mgr.getRegion('west')) {
40519             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40520             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40521             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40522             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40523             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40524         
40525             
40526         }
40527         
40528         
40529     } else {
40530      
40531         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40532         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40533         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40534         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40535     }
40536     
40537     
40538     if(Roo.isIE){
40539         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40540     }
40541     
40542     // finally - if tabs are at the top, then create the body last..
40543     if(this.tabPosition != "bottom"){
40544         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40545          * @type Roo.Element
40546          */
40547         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40548         this.el.addClass("roo-tabs-top");
40549     }
40550     this.items = [];
40551
40552     this.bodyEl.setStyle("position", "relative");
40553
40554     this.active = null;
40555     this.activateDelegate = this.activate.createDelegate(this);
40556
40557     this.addEvents({
40558         /**
40559          * @event tabchange
40560          * Fires when the active tab changes
40561          * @param {Roo.TabPanel} this
40562          * @param {Roo.TabPanelItem} activePanel The new active tab
40563          */
40564         "tabchange": true,
40565         /**
40566          * @event beforetabchange
40567          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40568          * @param {Roo.TabPanel} this
40569          * @param {Object} e Set cancel to true on this object to cancel the tab change
40570          * @param {Roo.TabPanelItem} tab The tab being changed to
40571          */
40572         "beforetabchange" : true
40573     });
40574
40575     Roo.EventManager.onWindowResize(this.onResize, this);
40576     this.cpad = this.el.getPadding("lr");
40577     this.hiddenCount = 0;
40578
40579
40580     // toolbar on the tabbar support...
40581     if (this.toolbar) {
40582         alert("no toolbar support yet");
40583         this.toolbar  = false;
40584         /*
40585         var tcfg = this.toolbar;
40586         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40587         this.toolbar = new Roo.Toolbar(tcfg);
40588         if (Roo.isSafari) {
40589             var tbl = tcfg.container.child('table', true);
40590             tbl.setAttribute('width', '100%');
40591         }
40592         */
40593         
40594     }
40595    
40596
40597
40598     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40599 };
40600
40601 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40602     /*
40603      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40604      */
40605     tabPosition : "top",
40606     /*
40607      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40608      */
40609     currentTabWidth : 0,
40610     /*
40611      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40612      */
40613     minTabWidth : 40,
40614     /*
40615      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40616      */
40617     maxTabWidth : 250,
40618     /*
40619      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40620      */
40621     preferredTabWidth : 175,
40622     /*
40623      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40624      */
40625     resizeTabs : false,
40626     /*
40627      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40628      */
40629     monitorResize : true,
40630     /*
40631      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40632      */
40633     toolbar : false,  // set by caller..
40634     
40635     region : false, /// set by caller
40636     
40637     disableTooltips : true, // not used yet...
40638
40639     /**
40640      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40641      * @param {String} id The id of the div to use <b>or create</b>
40642      * @param {String} text The text for the tab
40643      * @param {String} content (optional) Content to put in the TabPanelItem body
40644      * @param {Boolean} closable (optional) True to create a close icon on the tab
40645      * @return {Roo.TabPanelItem} The created TabPanelItem
40646      */
40647     addTab : function(id, text, content, closable, tpl)
40648     {
40649         var item = new Roo.bootstrap.panel.TabItem({
40650             panel: this,
40651             id : id,
40652             text : text,
40653             closable : closable,
40654             tpl : tpl
40655         });
40656         this.addTabItem(item);
40657         if(content){
40658             item.setContent(content);
40659         }
40660         return item;
40661     },
40662
40663     /**
40664      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40665      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40666      * @return {Roo.TabPanelItem}
40667      */
40668     getTab : function(id){
40669         return this.items[id];
40670     },
40671
40672     /**
40673      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40674      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40675      */
40676     hideTab : function(id){
40677         var t = this.items[id];
40678         if(!t.isHidden()){
40679            t.setHidden(true);
40680            this.hiddenCount++;
40681            this.autoSizeTabs();
40682         }
40683     },
40684
40685     /**
40686      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40687      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40688      */
40689     unhideTab : function(id){
40690         var t = this.items[id];
40691         if(t.isHidden()){
40692            t.setHidden(false);
40693            this.hiddenCount--;
40694            this.autoSizeTabs();
40695         }
40696     },
40697
40698     /**
40699      * Adds an existing {@link Roo.TabPanelItem}.
40700      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40701      */
40702     addTabItem : function(item)
40703     {
40704         this.items[item.id] = item;
40705         this.items.push(item);
40706         this.autoSizeTabs();
40707       //  if(this.resizeTabs){
40708     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40709   //         this.autoSizeTabs();
40710 //        }else{
40711 //            item.autoSize();
40712        // }
40713     },
40714
40715     /**
40716      * Removes a {@link Roo.TabPanelItem}.
40717      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40718      */
40719     removeTab : function(id){
40720         var items = this.items;
40721         var tab = items[id];
40722         if(!tab) { return; }
40723         var index = items.indexOf(tab);
40724         if(this.active == tab && items.length > 1){
40725             var newTab = this.getNextAvailable(index);
40726             if(newTab) {
40727                 newTab.activate();
40728             }
40729         }
40730         this.stripEl.dom.removeChild(tab.pnode.dom);
40731         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40732             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40733         }
40734         items.splice(index, 1);
40735         delete this.items[tab.id];
40736         tab.fireEvent("close", tab);
40737         tab.purgeListeners();
40738         this.autoSizeTabs();
40739     },
40740
40741     getNextAvailable : function(start){
40742         var items = this.items;
40743         var index = start;
40744         // look for a next tab that will slide over to
40745         // replace the one being removed
40746         while(index < items.length){
40747             var item = items[++index];
40748             if(item && !item.isHidden()){
40749                 return item;
40750             }
40751         }
40752         // if one isn't found select the previous tab (on the left)
40753         index = start;
40754         while(index >= 0){
40755             var item = items[--index];
40756             if(item && !item.isHidden()){
40757                 return item;
40758             }
40759         }
40760         return null;
40761     },
40762
40763     /**
40764      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40765      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40766      */
40767     disableTab : function(id){
40768         var tab = this.items[id];
40769         if(tab && this.active != tab){
40770             tab.disable();
40771         }
40772     },
40773
40774     /**
40775      * Enables a {@link Roo.TabPanelItem} that is disabled.
40776      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40777      */
40778     enableTab : function(id){
40779         var tab = this.items[id];
40780         tab.enable();
40781     },
40782
40783     /**
40784      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40785      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40786      * @return {Roo.TabPanelItem} The TabPanelItem.
40787      */
40788     activate : function(id)
40789     {
40790         //Roo.log('activite:'  + id);
40791         
40792         var tab = this.items[id];
40793         if(!tab){
40794             return null;
40795         }
40796         if(tab == this.active || tab.disabled){
40797             return tab;
40798         }
40799         var e = {};
40800         this.fireEvent("beforetabchange", this, e, tab);
40801         if(e.cancel !== true && !tab.disabled){
40802             if(this.active){
40803                 this.active.hide();
40804             }
40805             this.active = this.items[id];
40806             this.active.show();
40807             this.fireEvent("tabchange", this, this.active);
40808         }
40809         return tab;
40810     },
40811
40812     /**
40813      * Gets the active {@link Roo.TabPanelItem}.
40814      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40815      */
40816     getActiveTab : function(){
40817         return this.active;
40818     },
40819
40820     /**
40821      * Updates the tab body element to fit the height of the container element
40822      * for overflow scrolling
40823      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40824      */
40825     syncHeight : function(targetHeight){
40826         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40827         var bm = this.bodyEl.getMargins();
40828         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40829         this.bodyEl.setHeight(newHeight);
40830         return newHeight;
40831     },
40832
40833     onResize : function(){
40834         if(this.monitorResize){
40835             this.autoSizeTabs();
40836         }
40837     },
40838
40839     /**
40840      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40841      */
40842     beginUpdate : function(){
40843         this.updating = true;
40844     },
40845
40846     /**
40847      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40848      */
40849     endUpdate : function(){
40850         this.updating = false;
40851         this.autoSizeTabs();
40852     },
40853
40854     /**
40855      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40856      */
40857     autoSizeTabs : function()
40858     {
40859         var count = this.items.length;
40860         var vcount = count - this.hiddenCount;
40861         
40862         if (vcount < 2) {
40863             this.stripEl.hide();
40864         } else {
40865             this.stripEl.show();
40866         }
40867         
40868         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40869             return;
40870         }
40871         
40872         
40873         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40874         var availWidth = Math.floor(w / vcount);
40875         var b = this.stripBody;
40876         if(b.getWidth() > w){
40877             var tabs = this.items;
40878             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40879             if(availWidth < this.minTabWidth){
40880                 /*if(!this.sleft){    // incomplete scrolling code
40881                     this.createScrollButtons();
40882                 }
40883                 this.showScroll();
40884                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40885             }
40886         }else{
40887             if(this.currentTabWidth < this.preferredTabWidth){
40888                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40889             }
40890         }
40891     },
40892
40893     /**
40894      * Returns the number of tabs in this TabPanel.
40895      * @return {Number}
40896      */
40897      getCount : function(){
40898          return this.items.length;
40899      },
40900
40901     /**
40902      * Resizes all the tabs to the passed width
40903      * @param {Number} The new width
40904      */
40905     setTabWidth : function(width){
40906         this.currentTabWidth = width;
40907         for(var i = 0, len = this.items.length; i < len; i++) {
40908                 if(!this.items[i].isHidden()) {
40909                 this.items[i].setWidth(width);
40910             }
40911         }
40912     },
40913
40914     /**
40915      * Destroys this TabPanel
40916      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40917      */
40918     destroy : function(removeEl){
40919         Roo.EventManager.removeResizeListener(this.onResize, this);
40920         for(var i = 0, len = this.items.length; i < len; i++){
40921             this.items[i].purgeListeners();
40922         }
40923         if(removeEl === true){
40924             this.el.update("");
40925             this.el.remove();
40926         }
40927     },
40928     
40929     createStrip : function(container)
40930     {
40931         var strip = document.createElement("nav");
40932         strip.className = Roo.bootstrap.version == 4 ?
40933             "navbar-light bg-light" : 
40934             "navbar navbar-default"; //"x-tabs-wrap";
40935         container.appendChild(strip);
40936         return strip;
40937     },
40938     
40939     createStripList : function(strip)
40940     {
40941         // div wrapper for retard IE
40942         // returns the "tr" element.
40943         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40944         //'<div class="x-tabs-strip-wrap">'+
40945           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40946           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40947         return strip.firstChild; //.firstChild.firstChild.firstChild;
40948     },
40949     createBody : function(container)
40950     {
40951         var body = document.createElement("div");
40952         Roo.id(body, "tab-body");
40953         //Roo.fly(body).addClass("x-tabs-body");
40954         Roo.fly(body).addClass("tab-content");
40955         container.appendChild(body);
40956         return body;
40957     },
40958     createItemBody :function(bodyEl, id){
40959         var body = Roo.getDom(id);
40960         if(!body){
40961             body = document.createElement("div");
40962             body.id = id;
40963         }
40964         //Roo.fly(body).addClass("x-tabs-item-body");
40965         Roo.fly(body).addClass("tab-pane");
40966          bodyEl.insertBefore(body, bodyEl.firstChild);
40967         return body;
40968     },
40969     /** @private */
40970     createStripElements :  function(stripEl, text, closable, tpl)
40971     {
40972         var td = document.createElement("li"); // was td..
40973         td.className = 'nav-item';
40974         
40975         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40976         
40977         
40978         stripEl.appendChild(td);
40979         /*if(closable){
40980             td.className = "x-tabs-closable";
40981             if(!this.closeTpl){
40982                 this.closeTpl = new Roo.Template(
40983                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40984                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40985                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40986                 );
40987             }
40988             var el = this.closeTpl.overwrite(td, {"text": text});
40989             var close = el.getElementsByTagName("div")[0];
40990             var inner = el.getElementsByTagName("em")[0];
40991             return {"el": el, "close": close, "inner": inner};
40992         } else {
40993         */
40994         // not sure what this is..
40995 //            if(!this.tabTpl){
40996                 //this.tabTpl = new Roo.Template(
40997                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40998                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40999                 //);
41000 //                this.tabTpl = new Roo.Template(
41001 //                   '<a href="#">' +
41002 //                   '<span unselectable="on"' +
41003 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41004 //                            ' >{text}</span></a>'
41005 //                );
41006 //                
41007 //            }
41008
41009
41010             var template = tpl || this.tabTpl || false;
41011             
41012             if(!template){
41013                 template =  new Roo.Template(
41014                         Roo.bootstrap.version == 4 ? 
41015                             (
41016                                 '<a class="nav-link" href="#" unselectable="on"' +
41017                                      (this.disableTooltips ? '' : ' title="{text}"') +
41018                                      ' >{text}</a>'
41019                             ) : (
41020                                 '<a class="nav-link" href="#">' +
41021                                 '<span unselectable="on"' +
41022                                          (this.disableTooltips ? '' : ' title="{text}"') +
41023                                     ' >{text}</span></a>'
41024                             )
41025                 );
41026             }
41027             
41028             switch (typeof(template)) {
41029                 case 'object' :
41030                     break;
41031                 case 'string' :
41032                     template = new Roo.Template(template);
41033                     break;
41034                 default :
41035                     break;
41036             }
41037             
41038             var el = template.overwrite(td, {"text": text});
41039             
41040             var inner = el.getElementsByTagName("span")[0];
41041             
41042             return {"el": el, "inner": inner};
41043             
41044     }
41045         
41046     
41047 });
41048
41049 /**
41050  * @class Roo.TabPanelItem
41051  * @extends Roo.util.Observable
41052  * Represents an individual item (tab plus body) in a TabPanel.
41053  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41054  * @param {String} id The id of this TabPanelItem
41055  * @param {String} text The text for the tab of this TabPanelItem
41056  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41057  */
41058 Roo.bootstrap.panel.TabItem = function(config){
41059     /**
41060      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41061      * @type Roo.TabPanel
41062      */
41063     this.tabPanel = config.panel;
41064     /**
41065      * The id for this TabPanelItem
41066      * @type String
41067      */
41068     this.id = config.id;
41069     /** @private */
41070     this.disabled = false;
41071     /** @private */
41072     this.text = config.text;
41073     /** @private */
41074     this.loaded = false;
41075     this.closable = config.closable;
41076
41077     /**
41078      * The body element for this TabPanelItem.
41079      * @type Roo.Element
41080      */
41081     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41082     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41083     this.bodyEl.setStyle("display", "block");
41084     this.bodyEl.setStyle("zoom", "1");
41085     //this.hideAction();
41086
41087     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41088     /** @private */
41089     this.el = Roo.get(els.el);
41090     this.inner = Roo.get(els.inner, true);
41091      this.textEl = Roo.bootstrap.version == 4 ?
41092         this.el : Roo.get(this.el.dom.firstChild, true);
41093
41094     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41095     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41096
41097     
41098 //    this.el.on("mousedown", this.onTabMouseDown, this);
41099     this.el.on("click", this.onTabClick, this);
41100     /** @private */
41101     if(config.closable){
41102         var c = Roo.get(els.close, true);
41103         c.dom.title = this.closeText;
41104         c.addClassOnOver("close-over");
41105         c.on("click", this.closeClick, this);
41106      }
41107
41108     this.addEvents({
41109          /**
41110          * @event activate
41111          * Fires when this tab becomes the active tab.
41112          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41113          * @param {Roo.TabPanelItem} this
41114          */
41115         "activate": true,
41116         /**
41117          * @event beforeclose
41118          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41119          * @param {Roo.TabPanelItem} this
41120          * @param {Object} e Set cancel to true on this object to cancel the close.
41121          */
41122         "beforeclose": true,
41123         /**
41124          * @event close
41125          * Fires when this tab is closed.
41126          * @param {Roo.TabPanelItem} this
41127          */
41128          "close": true,
41129         /**
41130          * @event deactivate
41131          * Fires when this tab is no longer the active tab.
41132          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41133          * @param {Roo.TabPanelItem} this
41134          */
41135          "deactivate" : true
41136     });
41137     this.hidden = false;
41138
41139     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41140 };
41141
41142 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41143            {
41144     purgeListeners : function(){
41145        Roo.util.Observable.prototype.purgeListeners.call(this);
41146        this.el.removeAllListeners();
41147     },
41148     /**
41149      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41150      */
41151     show : function(){
41152         this.status_node.addClass("active");
41153         this.showAction();
41154         if(Roo.isOpera){
41155             this.tabPanel.stripWrap.repaint();
41156         }
41157         this.fireEvent("activate", this.tabPanel, this);
41158     },
41159
41160     /**
41161      * Returns true if this tab is the active tab.
41162      * @return {Boolean}
41163      */
41164     isActive : function(){
41165         return this.tabPanel.getActiveTab() == this;
41166     },
41167
41168     /**
41169      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41170      */
41171     hide : function(){
41172         this.status_node.removeClass("active");
41173         this.hideAction();
41174         this.fireEvent("deactivate", this.tabPanel, this);
41175     },
41176
41177     hideAction : function(){
41178         this.bodyEl.hide();
41179         this.bodyEl.setStyle("position", "absolute");
41180         this.bodyEl.setLeft("-20000px");
41181         this.bodyEl.setTop("-20000px");
41182     },
41183
41184     showAction : function(){
41185         this.bodyEl.setStyle("position", "relative");
41186         this.bodyEl.setTop("");
41187         this.bodyEl.setLeft("");
41188         this.bodyEl.show();
41189     },
41190
41191     /**
41192      * Set the tooltip for the tab.
41193      * @param {String} tooltip The tab's tooltip
41194      */
41195     setTooltip : function(text){
41196         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41197             this.textEl.dom.qtip = text;
41198             this.textEl.dom.removeAttribute('title');
41199         }else{
41200             this.textEl.dom.title = text;
41201         }
41202     },
41203
41204     onTabClick : function(e){
41205         e.preventDefault();
41206         this.tabPanel.activate(this.id);
41207     },
41208
41209     onTabMouseDown : function(e){
41210         e.preventDefault();
41211         this.tabPanel.activate(this.id);
41212     },
41213 /*
41214     getWidth : function(){
41215         return this.inner.getWidth();
41216     },
41217
41218     setWidth : function(width){
41219         var iwidth = width - this.linode.getPadding("lr");
41220         this.inner.setWidth(iwidth);
41221         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41222         this.linode.setWidth(width);
41223     },
41224 */
41225     /**
41226      * Show or hide the tab
41227      * @param {Boolean} hidden True to hide or false to show.
41228      */
41229     setHidden : function(hidden){
41230         this.hidden = hidden;
41231         this.linode.setStyle("display", hidden ? "none" : "");
41232     },
41233
41234     /**
41235      * Returns true if this tab is "hidden"
41236      * @return {Boolean}
41237      */
41238     isHidden : function(){
41239         return this.hidden;
41240     },
41241
41242     /**
41243      * Returns the text for this tab
41244      * @return {String}
41245      */
41246     getText : function(){
41247         return this.text;
41248     },
41249     /*
41250     autoSize : function(){
41251         //this.el.beginMeasure();
41252         this.textEl.setWidth(1);
41253         /*
41254          *  #2804 [new] Tabs in Roojs
41255          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41256          */
41257         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41258         //this.el.endMeasure();
41259     //},
41260
41261     /**
41262      * Sets the text for the tab (Note: this also sets the tooltip text)
41263      * @param {String} text The tab's text and tooltip
41264      */
41265     setText : function(text){
41266         this.text = text;
41267         this.textEl.update(text);
41268         this.setTooltip(text);
41269         //if(!this.tabPanel.resizeTabs){
41270         //    this.autoSize();
41271         //}
41272     },
41273     /**
41274      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41275      */
41276     activate : function(){
41277         this.tabPanel.activate(this.id);
41278     },
41279
41280     /**
41281      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41282      */
41283     disable : function(){
41284         if(this.tabPanel.active != this){
41285             this.disabled = true;
41286             this.status_node.addClass("disabled");
41287         }
41288     },
41289
41290     /**
41291      * Enables this TabPanelItem if it was previously disabled.
41292      */
41293     enable : function(){
41294         this.disabled = false;
41295         this.status_node.removeClass("disabled");
41296     },
41297
41298     /**
41299      * Sets the content for this TabPanelItem.
41300      * @param {String} content The content
41301      * @param {Boolean} loadScripts true to look for and load scripts
41302      */
41303     setContent : function(content, loadScripts){
41304         this.bodyEl.update(content, loadScripts);
41305     },
41306
41307     /**
41308      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41309      * @return {Roo.UpdateManager} The UpdateManager
41310      */
41311     getUpdateManager : function(){
41312         return this.bodyEl.getUpdateManager();
41313     },
41314
41315     /**
41316      * Set a URL to be used to load the content for this TabPanelItem.
41317      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41318      * @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)
41319      * @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)
41320      * @return {Roo.UpdateManager} The UpdateManager
41321      */
41322     setUrl : function(url, params, loadOnce){
41323         if(this.refreshDelegate){
41324             this.un('activate', this.refreshDelegate);
41325         }
41326         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41327         this.on("activate", this.refreshDelegate);
41328         return this.bodyEl.getUpdateManager();
41329     },
41330
41331     /** @private */
41332     _handleRefresh : function(url, params, loadOnce){
41333         if(!loadOnce || !this.loaded){
41334             var updater = this.bodyEl.getUpdateManager();
41335             updater.update(url, params, this._setLoaded.createDelegate(this));
41336         }
41337     },
41338
41339     /**
41340      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41341      *   Will fail silently if the setUrl method has not been called.
41342      *   This does not activate the panel, just updates its content.
41343      */
41344     refresh : function(){
41345         if(this.refreshDelegate){
41346            this.loaded = false;
41347            this.refreshDelegate();
41348         }
41349     },
41350
41351     /** @private */
41352     _setLoaded : function(){
41353         this.loaded = true;
41354     },
41355
41356     /** @private */
41357     closeClick : function(e){
41358         var o = {};
41359         e.stopEvent();
41360         this.fireEvent("beforeclose", this, o);
41361         if(o.cancel !== true){
41362             this.tabPanel.removeTab(this.id);
41363         }
41364     },
41365     /**
41366      * The text displayed in the tooltip for the close icon.
41367      * @type String
41368      */
41369     closeText : "Close this tab"
41370 });
41371 /**
41372 *    This script refer to:
41373 *    Title: International Telephone Input
41374 *    Author: Jack O'Connor
41375 *    Code version:  v12.1.12
41376 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41377 **/
41378
41379 Roo.bootstrap.PhoneInputData = function() {
41380     var d = [
41381       [
41382         "Afghanistan (‫افغانستان‬‎)",
41383         "af",
41384         "93"
41385       ],
41386       [
41387         "Albania (Shqipëri)",
41388         "al",
41389         "355"
41390       ],
41391       [
41392         "Algeria (‫الجزائر‬‎)",
41393         "dz",
41394         "213"
41395       ],
41396       [
41397         "American Samoa",
41398         "as",
41399         "1684"
41400       ],
41401       [
41402         "Andorra",
41403         "ad",
41404         "376"
41405       ],
41406       [
41407         "Angola",
41408         "ao",
41409         "244"
41410       ],
41411       [
41412         "Anguilla",
41413         "ai",
41414         "1264"
41415       ],
41416       [
41417         "Antigua and Barbuda",
41418         "ag",
41419         "1268"
41420       ],
41421       [
41422         "Argentina",
41423         "ar",
41424         "54"
41425       ],
41426       [
41427         "Armenia (Հայաստան)",
41428         "am",
41429         "374"
41430       ],
41431       [
41432         "Aruba",
41433         "aw",
41434         "297"
41435       ],
41436       [
41437         "Australia",
41438         "au",
41439         "61",
41440         0
41441       ],
41442       [
41443         "Austria (Österreich)",
41444         "at",
41445         "43"
41446       ],
41447       [
41448         "Azerbaijan (Azərbaycan)",
41449         "az",
41450         "994"
41451       ],
41452       [
41453         "Bahamas",
41454         "bs",
41455         "1242"
41456       ],
41457       [
41458         "Bahrain (‫البحرين‬‎)",
41459         "bh",
41460         "973"
41461       ],
41462       [
41463         "Bangladesh (বাংলাদেশ)",
41464         "bd",
41465         "880"
41466       ],
41467       [
41468         "Barbados",
41469         "bb",
41470         "1246"
41471       ],
41472       [
41473         "Belarus (Беларусь)",
41474         "by",
41475         "375"
41476       ],
41477       [
41478         "Belgium (België)",
41479         "be",
41480         "32"
41481       ],
41482       [
41483         "Belize",
41484         "bz",
41485         "501"
41486       ],
41487       [
41488         "Benin (Bénin)",
41489         "bj",
41490         "229"
41491       ],
41492       [
41493         "Bermuda",
41494         "bm",
41495         "1441"
41496       ],
41497       [
41498         "Bhutan (འབྲུག)",
41499         "bt",
41500         "975"
41501       ],
41502       [
41503         "Bolivia",
41504         "bo",
41505         "591"
41506       ],
41507       [
41508         "Bosnia and Herzegovina (Босна и Херцеговина)",
41509         "ba",
41510         "387"
41511       ],
41512       [
41513         "Botswana",
41514         "bw",
41515         "267"
41516       ],
41517       [
41518         "Brazil (Brasil)",
41519         "br",
41520         "55"
41521       ],
41522       [
41523         "British Indian Ocean Territory",
41524         "io",
41525         "246"
41526       ],
41527       [
41528         "British Virgin Islands",
41529         "vg",
41530         "1284"
41531       ],
41532       [
41533         "Brunei",
41534         "bn",
41535         "673"
41536       ],
41537       [
41538         "Bulgaria (България)",
41539         "bg",
41540         "359"
41541       ],
41542       [
41543         "Burkina Faso",
41544         "bf",
41545         "226"
41546       ],
41547       [
41548         "Burundi (Uburundi)",
41549         "bi",
41550         "257"
41551       ],
41552       [
41553         "Cambodia (កម្ពុជា)",
41554         "kh",
41555         "855"
41556       ],
41557       [
41558         "Cameroon (Cameroun)",
41559         "cm",
41560         "237"
41561       ],
41562       [
41563         "Canada",
41564         "ca",
41565         "1",
41566         1,
41567         ["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"]
41568       ],
41569       [
41570         "Cape Verde (Kabu Verdi)",
41571         "cv",
41572         "238"
41573       ],
41574       [
41575         "Caribbean Netherlands",
41576         "bq",
41577         "599",
41578         1
41579       ],
41580       [
41581         "Cayman Islands",
41582         "ky",
41583         "1345"
41584       ],
41585       [
41586         "Central African Republic (République centrafricaine)",
41587         "cf",
41588         "236"
41589       ],
41590       [
41591         "Chad (Tchad)",
41592         "td",
41593         "235"
41594       ],
41595       [
41596         "Chile",
41597         "cl",
41598         "56"
41599       ],
41600       [
41601         "China (中国)",
41602         "cn",
41603         "86"
41604       ],
41605       [
41606         "Christmas Island",
41607         "cx",
41608         "61",
41609         2
41610       ],
41611       [
41612         "Cocos (Keeling) Islands",
41613         "cc",
41614         "61",
41615         1
41616       ],
41617       [
41618         "Colombia",
41619         "co",
41620         "57"
41621       ],
41622       [
41623         "Comoros (‫جزر القمر‬‎)",
41624         "km",
41625         "269"
41626       ],
41627       [
41628         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41629         "cd",
41630         "243"
41631       ],
41632       [
41633         "Congo (Republic) (Congo-Brazzaville)",
41634         "cg",
41635         "242"
41636       ],
41637       [
41638         "Cook Islands",
41639         "ck",
41640         "682"
41641       ],
41642       [
41643         "Costa Rica",
41644         "cr",
41645         "506"
41646       ],
41647       [
41648         "Côte d’Ivoire",
41649         "ci",
41650         "225"
41651       ],
41652       [
41653         "Croatia (Hrvatska)",
41654         "hr",
41655         "385"
41656       ],
41657       [
41658         "Cuba",
41659         "cu",
41660         "53"
41661       ],
41662       [
41663         "Curaçao",
41664         "cw",
41665         "599",
41666         0
41667       ],
41668       [
41669         "Cyprus (Κύπρος)",
41670         "cy",
41671         "357"
41672       ],
41673       [
41674         "Czech Republic (Česká republika)",
41675         "cz",
41676         "420"
41677       ],
41678       [
41679         "Denmark (Danmark)",
41680         "dk",
41681         "45"
41682       ],
41683       [
41684         "Djibouti",
41685         "dj",
41686         "253"
41687       ],
41688       [
41689         "Dominica",
41690         "dm",
41691         "1767"
41692       ],
41693       [
41694         "Dominican Republic (República Dominicana)",
41695         "do",
41696         "1",
41697         2,
41698         ["809", "829", "849"]
41699       ],
41700       [
41701         "Ecuador",
41702         "ec",
41703         "593"
41704       ],
41705       [
41706         "Egypt (‫مصر‬‎)",
41707         "eg",
41708         "20"
41709       ],
41710       [
41711         "El Salvador",
41712         "sv",
41713         "503"
41714       ],
41715       [
41716         "Equatorial Guinea (Guinea Ecuatorial)",
41717         "gq",
41718         "240"
41719       ],
41720       [
41721         "Eritrea",
41722         "er",
41723         "291"
41724       ],
41725       [
41726         "Estonia (Eesti)",
41727         "ee",
41728         "372"
41729       ],
41730       [
41731         "Ethiopia",
41732         "et",
41733         "251"
41734       ],
41735       [
41736         "Falkland Islands (Islas Malvinas)",
41737         "fk",
41738         "500"
41739       ],
41740       [
41741         "Faroe Islands (Føroyar)",
41742         "fo",
41743         "298"
41744       ],
41745       [
41746         "Fiji",
41747         "fj",
41748         "679"
41749       ],
41750       [
41751         "Finland (Suomi)",
41752         "fi",
41753         "358",
41754         0
41755       ],
41756       [
41757         "France",
41758         "fr",
41759         "33"
41760       ],
41761       [
41762         "French Guiana (Guyane française)",
41763         "gf",
41764         "594"
41765       ],
41766       [
41767         "French Polynesia (Polynésie française)",
41768         "pf",
41769         "689"
41770       ],
41771       [
41772         "Gabon",
41773         "ga",
41774         "241"
41775       ],
41776       [
41777         "Gambia",
41778         "gm",
41779         "220"
41780       ],
41781       [
41782         "Georgia (საქართველო)",
41783         "ge",
41784         "995"
41785       ],
41786       [
41787         "Germany (Deutschland)",
41788         "de",
41789         "49"
41790       ],
41791       [
41792         "Ghana (Gaana)",
41793         "gh",
41794         "233"
41795       ],
41796       [
41797         "Gibraltar",
41798         "gi",
41799         "350"
41800       ],
41801       [
41802         "Greece (Ελλάδα)",
41803         "gr",
41804         "30"
41805       ],
41806       [
41807         "Greenland (Kalaallit Nunaat)",
41808         "gl",
41809         "299"
41810       ],
41811       [
41812         "Grenada",
41813         "gd",
41814         "1473"
41815       ],
41816       [
41817         "Guadeloupe",
41818         "gp",
41819         "590",
41820         0
41821       ],
41822       [
41823         "Guam",
41824         "gu",
41825         "1671"
41826       ],
41827       [
41828         "Guatemala",
41829         "gt",
41830         "502"
41831       ],
41832       [
41833         "Guernsey",
41834         "gg",
41835         "44",
41836         1
41837       ],
41838       [
41839         "Guinea (Guinée)",
41840         "gn",
41841         "224"
41842       ],
41843       [
41844         "Guinea-Bissau (Guiné Bissau)",
41845         "gw",
41846         "245"
41847       ],
41848       [
41849         "Guyana",
41850         "gy",
41851         "592"
41852       ],
41853       [
41854         "Haiti",
41855         "ht",
41856         "509"
41857       ],
41858       [
41859         "Honduras",
41860         "hn",
41861         "504"
41862       ],
41863       [
41864         "Hong Kong (香港)",
41865         "hk",
41866         "852"
41867       ],
41868       [
41869         "Hungary (Magyarország)",
41870         "hu",
41871         "36"
41872       ],
41873       [
41874         "Iceland (Ísland)",
41875         "is",
41876         "354"
41877       ],
41878       [
41879         "India (भारत)",
41880         "in",
41881         "91"
41882       ],
41883       [
41884         "Indonesia",
41885         "id",
41886         "62"
41887       ],
41888       [
41889         "Iran (‫ایران‬‎)",
41890         "ir",
41891         "98"
41892       ],
41893       [
41894         "Iraq (‫العراق‬‎)",
41895         "iq",
41896         "964"
41897       ],
41898       [
41899         "Ireland",
41900         "ie",
41901         "353"
41902       ],
41903       [
41904         "Isle of Man",
41905         "im",
41906         "44",
41907         2
41908       ],
41909       [
41910         "Israel (‫ישראל‬‎)",
41911         "il",
41912         "972"
41913       ],
41914       [
41915         "Italy (Italia)",
41916         "it",
41917         "39",
41918         0
41919       ],
41920       [
41921         "Jamaica",
41922         "jm",
41923         "1876"
41924       ],
41925       [
41926         "Japan (日本)",
41927         "jp",
41928         "81"
41929       ],
41930       [
41931         "Jersey",
41932         "je",
41933         "44",
41934         3
41935       ],
41936       [
41937         "Jordan (‫الأردن‬‎)",
41938         "jo",
41939         "962"
41940       ],
41941       [
41942         "Kazakhstan (Казахстан)",
41943         "kz",
41944         "7",
41945         1
41946       ],
41947       [
41948         "Kenya",
41949         "ke",
41950         "254"
41951       ],
41952       [
41953         "Kiribati",
41954         "ki",
41955         "686"
41956       ],
41957       [
41958         "Kosovo",
41959         "xk",
41960         "383"
41961       ],
41962       [
41963         "Kuwait (‫الكويت‬‎)",
41964         "kw",
41965         "965"
41966       ],
41967       [
41968         "Kyrgyzstan (Кыргызстан)",
41969         "kg",
41970         "996"
41971       ],
41972       [
41973         "Laos (ລາວ)",
41974         "la",
41975         "856"
41976       ],
41977       [
41978         "Latvia (Latvija)",
41979         "lv",
41980         "371"
41981       ],
41982       [
41983         "Lebanon (‫لبنان‬‎)",
41984         "lb",
41985         "961"
41986       ],
41987       [
41988         "Lesotho",
41989         "ls",
41990         "266"
41991       ],
41992       [
41993         "Liberia",
41994         "lr",
41995         "231"
41996       ],
41997       [
41998         "Libya (‫ليبيا‬‎)",
41999         "ly",
42000         "218"
42001       ],
42002       [
42003         "Liechtenstein",
42004         "li",
42005         "423"
42006       ],
42007       [
42008         "Lithuania (Lietuva)",
42009         "lt",
42010         "370"
42011       ],
42012       [
42013         "Luxembourg",
42014         "lu",
42015         "352"
42016       ],
42017       [
42018         "Macau (澳門)",
42019         "mo",
42020         "853"
42021       ],
42022       [
42023         "Macedonia (FYROM) (Македонија)",
42024         "mk",
42025         "389"
42026       ],
42027       [
42028         "Madagascar (Madagasikara)",
42029         "mg",
42030         "261"
42031       ],
42032       [
42033         "Malawi",
42034         "mw",
42035         "265"
42036       ],
42037       [
42038         "Malaysia",
42039         "my",
42040         "60"
42041       ],
42042       [
42043         "Maldives",
42044         "mv",
42045         "960"
42046       ],
42047       [
42048         "Mali",
42049         "ml",
42050         "223"
42051       ],
42052       [
42053         "Malta",
42054         "mt",
42055         "356"
42056       ],
42057       [
42058         "Marshall Islands",
42059         "mh",
42060         "692"
42061       ],
42062       [
42063         "Martinique",
42064         "mq",
42065         "596"
42066       ],
42067       [
42068         "Mauritania (‫موريتانيا‬‎)",
42069         "mr",
42070         "222"
42071       ],
42072       [
42073         "Mauritius (Moris)",
42074         "mu",
42075         "230"
42076       ],
42077       [
42078         "Mayotte",
42079         "yt",
42080         "262",
42081         1
42082       ],
42083       [
42084         "Mexico (México)",
42085         "mx",
42086         "52"
42087       ],
42088       [
42089         "Micronesia",
42090         "fm",
42091         "691"
42092       ],
42093       [
42094         "Moldova (Republica Moldova)",
42095         "md",
42096         "373"
42097       ],
42098       [
42099         "Monaco",
42100         "mc",
42101         "377"
42102       ],
42103       [
42104         "Mongolia (Монгол)",
42105         "mn",
42106         "976"
42107       ],
42108       [
42109         "Montenegro (Crna Gora)",
42110         "me",
42111         "382"
42112       ],
42113       [
42114         "Montserrat",
42115         "ms",
42116         "1664"
42117       ],
42118       [
42119         "Morocco (‫المغرب‬‎)",
42120         "ma",
42121         "212",
42122         0
42123       ],
42124       [
42125         "Mozambique (Moçambique)",
42126         "mz",
42127         "258"
42128       ],
42129       [
42130         "Myanmar (Burma) (မြန်မာ)",
42131         "mm",
42132         "95"
42133       ],
42134       [
42135         "Namibia (Namibië)",
42136         "na",
42137         "264"
42138       ],
42139       [
42140         "Nauru",
42141         "nr",
42142         "674"
42143       ],
42144       [
42145         "Nepal (नेपाल)",
42146         "np",
42147         "977"
42148       ],
42149       [
42150         "Netherlands (Nederland)",
42151         "nl",
42152         "31"
42153       ],
42154       [
42155         "New Caledonia (Nouvelle-Calédonie)",
42156         "nc",
42157         "687"
42158       ],
42159       [
42160         "New Zealand",
42161         "nz",
42162         "64"
42163       ],
42164       [
42165         "Nicaragua",
42166         "ni",
42167         "505"
42168       ],
42169       [
42170         "Niger (Nijar)",
42171         "ne",
42172         "227"
42173       ],
42174       [
42175         "Nigeria",
42176         "ng",
42177         "234"
42178       ],
42179       [
42180         "Niue",
42181         "nu",
42182         "683"
42183       ],
42184       [
42185         "Norfolk Island",
42186         "nf",
42187         "672"
42188       ],
42189       [
42190         "North Korea (조선 민주주의 인민 공화국)",
42191         "kp",
42192         "850"
42193       ],
42194       [
42195         "Northern Mariana Islands",
42196         "mp",
42197         "1670"
42198       ],
42199       [
42200         "Norway (Norge)",
42201         "no",
42202         "47",
42203         0
42204       ],
42205       [
42206         "Oman (‫عُمان‬‎)",
42207         "om",
42208         "968"
42209       ],
42210       [
42211         "Pakistan (‫پاکستان‬‎)",
42212         "pk",
42213         "92"
42214       ],
42215       [
42216         "Palau",
42217         "pw",
42218         "680"
42219       ],
42220       [
42221         "Palestine (‫فلسطين‬‎)",
42222         "ps",
42223         "970"
42224       ],
42225       [
42226         "Panama (Panamá)",
42227         "pa",
42228         "507"
42229       ],
42230       [
42231         "Papua New Guinea",
42232         "pg",
42233         "675"
42234       ],
42235       [
42236         "Paraguay",
42237         "py",
42238         "595"
42239       ],
42240       [
42241         "Peru (Perú)",
42242         "pe",
42243         "51"
42244       ],
42245       [
42246         "Philippines",
42247         "ph",
42248         "63"
42249       ],
42250       [
42251         "Poland (Polska)",
42252         "pl",
42253         "48"
42254       ],
42255       [
42256         "Portugal",
42257         "pt",
42258         "351"
42259       ],
42260       [
42261         "Puerto Rico",
42262         "pr",
42263         "1",
42264         3,
42265         ["787", "939"]
42266       ],
42267       [
42268         "Qatar (‫قطر‬‎)",
42269         "qa",
42270         "974"
42271       ],
42272       [
42273         "Réunion (La Réunion)",
42274         "re",
42275         "262",
42276         0
42277       ],
42278       [
42279         "Romania (România)",
42280         "ro",
42281         "40"
42282       ],
42283       [
42284         "Russia (Россия)",
42285         "ru",
42286         "7",
42287         0
42288       ],
42289       [
42290         "Rwanda",
42291         "rw",
42292         "250"
42293       ],
42294       [
42295         "Saint Barthélemy",
42296         "bl",
42297         "590",
42298         1
42299       ],
42300       [
42301         "Saint Helena",
42302         "sh",
42303         "290"
42304       ],
42305       [
42306         "Saint Kitts and Nevis",
42307         "kn",
42308         "1869"
42309       ],
42310       [
42311         "Saint Lucia",
42312         "lc",
42313         "1758"
42314       ],
42315       [
42316         "Saint Martin (Saint-Martin (partie française))",
42317         "mf",
42318         "590",
42319         2
42320       ],
42321       [
42322         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42323         "pm",
42324         "508"
42325       ],
42326       [
42327         "Saint Vincent and the Grenadines",
42328         "vc",
42329         "1784"
42330       ],
42331       [
42332         "Samoa",
42333         "ws",
42334         "685"
42335       ],
42336       [
42337         "San Marino",
42338         "sm",
42339         "378"
42340       ],
42341       [
42342         "São Tomé and Príncipe (São Tomé e Príncipe)",
42343         "st",
42344         "239"
42345       ],
42346       [
42347         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42348         "sa",
42349         "966"
42350       ],
42351       [
42352         "Senegal (Sénégal)",
42353         "sn",
42354         "221"
42355       ],
42356       [
42357         "Serbia (Србија)",
42358         "rs",
42359         "381"
42360       ],
42361       [
42362         "Seychelles",
42363         "sc",
42364         "248"
42365       ],
42366       [
42367         "Sierra Leone",
42368         "sl",
42369         "232"
42370       ],
42371       [
42372         "Singapore",
42373         "sg",
42374         "65"
42375       ],
42376       [
42377         "Sint Maarten",
42378         "sx",
42379         "1721"
42380       ],
42381       [
42382         "Slovakia (Slovensko)",
42383         "sk",
42384         "421"
42385       ],
42386       [
42387         "Slovenia (Slovenija)",
42388         "si",
42389         "386"
42390       ],
42391       [
42392         "Solomon Islands",
42393         "sb",
42394         "677"
42395       ],
42396       [
42397         "Somalia (Soomaaliya)",
42398         "so",
42399         "252"
42400       ],
42401       [
42402         "South Africa",
42403         "za",
42404         "27"
42405       ],
42406       [
42407         "South Korea (대한민국)",
42408         "kr",
42409         "82"
42410       ],
42411       [
42412         "South Sudan (‫جنوب السودان‬‎)",
42413         "ss",
42414         "211"
42415       ],
42416       [
42417         "Spain (España)",
42418         "es",
42419         "34"
42420       ],
42421       [
42422         "Sri Lanka (ශ්‍රී ලංකාව)",
42423         "lk",
42424         "94"
42425       ],
42426       [
42427         "Sudan (‫السودان‬‎)",
42428         "sd",
42429         "249"
42430       ],
42431       [
42432         "Suriname",
42433         "sr",
42434         "597"
42435       ],
42436       [
42437         "Svalbard and Jan Mayen",
42438         "sj",
42439         "47",
42440         1
42441       ],
42442       [
42443         "Swaziland",
42444         "sz",
42445         "268"
42446       ],
42447       [
42448         "Sweden (Sverige)",
42449         "se",
42450         "46"
42451       ],
42452       [
42453         "Switzerland (Schweiz)",
42454         "ch",
42455         "41"
42456       ],
42457       [
42458         "Syria (‫سوريا‬‎)",
42459         "sy",
42460         "963"
42461       ],
42462       [
42463         "Taiwan (台灣)",
42464         "tw",
42465         "886"
42466       ],
42467       [
42468         "Tajikistan",
42469         "tj",
42470         "992"
42471       ],
42472       [
42473         "Tanzania",
42474         "tz",
42475         "255"
42476       ],
42477       [
42478         "Thailand (ไทย)",
42479         "th",
42480         "66"
42481       ],
42482       [
42483         "Timor-Leste",
42484         "tl",
42485         "670"
42486       ],
42487       [
42488         "Togo",
42489         "tg",
42490         "228"
42491       ],
42492       [
42493         "Tokelau",
42494         "tk",
42495         "690"
42496       ],
42497       [
42498         "Tonga",
42499         "to",
42500         "676"
42501       ],
42502       [
42503         "Trinidad and Tobago",
42504         "tt",
42505         "1868"
42506       ],
42507       [
42508         "Tunisia (‫تونس‬‎)",
42509         "tn",
42510         "216"
42511       ],
42512       [
42513         "Turkey (Türkiye)",
42514         "tr",
42515         "90"
42516       ],
42517       [
42518         "Turkmenistan",
42519         "tm",
42520         "993"
42521       ],
42522       [
42523         "Turks and Caicos Islands",
42524         "tc",
42525         "1649"
42526       ],
42527       [
42528         "Tuvalu",
42529         "tv",
42530         "688"
42531       ],
42532       [
42533         "U.S. Virgin Islands",
42534         "vi",
42535         "1340"
42536       ],
42537       [
42538         "Uganda",
42539         "ug",
42540         "256"
42541       ],
42542       [
42543         "Ukraine (Україна)",
42544         "ua",
42545         "380"
42546       ],
42547       [
42548         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42549         "ae",
42550         "971"
42551       ],
42552       [
42553         "United Kingdom",
42554         "gb",
42555         "44",
42556         0
42557       ],
42558       [
42559         "United States",
42560         "us",
42561         "1",
42562         0
42563       ],
42564       [
42565         "Uruguay",
42566         "uy",
42567         "598"
42568       ],
42569       [
42570         "Uzbekistan (Oʻzbekiston)",
42571         "uz",
42572         "998"
42573       ],
42574       [
42575         "Vanuatu",
42576         "vu",
42577         "678"
42578       ],
42579       [
42580         "Vatican City (Città del Vaticano)",
42581         "va",
42582         "39",
42583         1
42584       ],
42585       [
42586         "Venezuela",
42587         "ve",
42588         "58"
42589       ],
42590       [
42591         "Vietnam (Việt Nam)",
42592         "vn",
42593         "84"
42594       ],
42595       [
42596         "Wallis and Futuna (Wallis-et-Futuna)",
42597         "wf",
42598         "681"
42599       ],
42600       [
42601         "Western Sahara (‫الصحراء الغربية‬‎)",
42602         "eh",
42603         "212",
42604         1
42605       ],
42606       [
42607         "Yemen (‫اليمن‬‎)",
42608         "ye",
42609         "967"
42610       ],
42611       [
42612         "Zambia",
42613         "zm",
42614         "260"
42615       ],
42616       [
42617         "Zimbabwe",
42618         "zw",
42619         "263"
42620       ],
42621       [
42622         "Åland Islands",
42623         "ax",
42624         "358",
42625         1
42626       ]
42627   ];
42628   
42629   return d;
42630 }/**
42631 *    This script refer to:
42632 *    Title: International Telephone Input
42633 *    Author: Jack O'Connor
42634 *    Code version:  v12.1.12
42635 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42636 **/
42637
42638 /**
42639  * @class Roo.bootstrap.PhoneInput
42640  * @extends Roo.bootstrap.TriggerField
42641  * An input with International dial-code selection
42642  
42643  * @cfg {String} defaultDialCode default '+852'
42644  * @cfg {Array} preferedCountries default []
42645   
42646  * @constructor
42647  * Create a new PhoneInput.
42648  * @param {Object} config Configuration options
42649  */
42650
42651 Roo.bootstrap.PhoneInput = function(config) {
42652     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42653 };
42654
42655 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42656         
42657         listWidth: undefined,
42658         
42659         selectedClass: 'active',
42660         
42661         invalidClass : "has-warning",
42662         
42663         validClass: 'has-success',
42664         
42665         allowed: '0123456789',
42666         
42667         max_length: 15,
42668         
42669         /**
42670          * @cfg {String} defaultDialCode The default dial code when initializing the input
42671          */
42672         defaultDialCode: '+852',
42673         
42674         /**
42675          * @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
42676          */
42677         preferedCountries: false,
42678         
42679         getAutoCreate : function()
42680         {
42681             var data = Roo.bootstrap.PhoneInputData();
42682             var align = this.labelAlign || this.parentLabelAlign();
42683             var id = Roo.id();
42684             
42685             this.allCountries = [];
42686             this.dialCodeMapping = [];
42687             
42688             for (var i = 0; i < data.length; i++) {
42689               var c = data[i];
42690               this.allCountries[i] = {
42691                 name: c[0],
42692                 iso2: c[1],
42693                 dialCode: c[2],
42694                 priority: c[3] || 0,
42695                 areaCodes: c[4] || null
42696               };
42697               this.dialCodeMapping[c[2]] = {
42698                   name: c[0],
42699                   iso2: c[1],
42700                   priority: c[3] || 0,
42701                   areaCodes: c[4] || null
42702               };
42703             }
42704             
42705             var cfg = {
42706                 cls: 'form-group',
42707                 cn: []
42708             };
42709             
42710             var input =  {
42711                 tag: 'input',
42712                 id : id,
42713                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42714                 maxlength: this.max_length,
42715                 cls : 'form-control tel-input',
42716                 autocomplete: 'new-password'
42717             };
42718             
42719             var hiddenInput = {
42720                 tag: 'input',
42721                 type: 'hidden',
42722                 cls: 'hidden-tel-input'
42723             };
42724             
42725             if (this.name) {
42726                 hiddenInput.name = this.name;
42727             }
42728             
42729             if (this.disabled) {
42730                 input.disabled = true;
42731             }
42732             
42733             var flag_container = {
42734                 tag: 'div',
42735                 cls: 'flag-box',
42736                 cn: [
42737                     {
42738                         tag: 'div',
42739                         cls: 'flag'
42740                     },
42741                     {
42742                         tag: 'div',
42743                         cls: 'caret'
42744                     }
42745                 ]
42746             };
42747             
42748             var box = {
42749                 tag: 'div',
42750                 cls: this.hasFeedback ? 'has-feedback' : '',
42751                 cn: [
42752                     hiddenInput,
42753                     input,
42754                     {
42755                         tag: 'input',
42756                         cls: 'dial-code-holder',
42757                         disabled: true
42758                     }
42759                 ]
42760             };
42761             
42762             var container = {
42763                 cls: 'roo-select2-container input-group',
42764                 cn: [
42765                     flag_container,
42766                     box
42767                 ]
42768             };
42769             
42770             if (this.fieldLabel.length) {
42771                 var indicator = {
42772                     tag: 'i',
42773                     tooltip: 'This field is required'
42774                 };
42775                 
42776                 var label = {
42777                     tag: 'label',
42778                     'for':  id,
42779                     cls: 'control-label',
42780                     cn: []
42781                 };
42782                 
42783                 var label_text = {
42784                     tag: 'span',
42785                     html: this.fieldLabel
42786                 };
42787                 
42788                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42789                 label.cn = [
42790                     indicator,
42791                     label_text
42792                 ];
42793                 
42794                 if(this.indicatorpos == 'right') {
42795                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42796                     label.cn = [
42797                         label_text,
42798                         indicator
42799                     ];
42800                 }
42801                 
42802                 if(align == 'left') {
42803                     container = {
42804                         tag: 'div',
42805                         cn: [
42806                             container
42807                         ]
42808                     };
42809                     
42810                     if(this.labelWidth > 12){
42811                         label.style = "width: " + this.labelWidth + 'px';
42812                     }
42813                     if(this.labelWidth < 13 && this.labelmd == 0){
42814                         this.labelmd = this.labelWidth;
42815                     }
42816                     if(this.labellg > 0){
42817                         label.cls += ' col-lg-' + this.labellg;
42818                         input.cls += ' col-lg-' + (12 - this.labellg);
42819                     }
42820                     if(this.labelmd > 0){
42821                         label.cls += ' col-md-' + this.labelmd;
42822                         container.cls += ' col-md-' + (12 - this.labelmd);
42823                     }
42824                     if(this.labelsm > 0){
42825                         label.cls += ' col-sm-' + this.labelsm;
42826                         container.cls += ' col-sm-' + (12 - this.labelsm);
42827                     }
42828                     if(this.labelxs > 0){
42829                         label.cls += ' col-xs-' + this.labelxs;
42830                         container.cls += ' col-xs-' + (12 - this.labelxs);
42831                     }
42832                 }
42833             }
42834             
42835             cfg.cn = [
42836                 label,
42837                 container
42838             ];
42839             
42840             var settings = this;
42841             
42842             ['xs','sm','md','lg'].map(function(size){
42843                 if (settings[size]) {
42844                     cfg.cls += ' col-' + size + '-' + settings[size];
42845                 }
42846             });
42847             
42848             this.store = new Roo.data.Store({
42849                 proxy : new Roo.data.MemoryProxy({}),
42850                 reader : new Roo.data.JsonReader({
42851                     fields : [
42852                         {
42853                             'name' : 'name',
42854                             'type' : 'string'
42855                         },
42856                         {
42857                             'name' : 'iso2',
42858                             'type' : 'string'
42859                         },
42860                         {
42861                             'name' : 'dialCode',
42862                             'type' : 'string'
42863                         },
42864                         {
42865                             'name' : 'priority',
42866                             'type' : 'string'
42867                         },
42868                         {
42869                             'name' : 'areaCodes',
42870                             'type' : 'string'
42871                         }
42872                     ]
42873                 })
42874             });
42875             
42876             if(!this.preferedCountries) {
42877                 this.preferedCountries = [
42878                     'hk',
42879                     'gb',
42880                     'us'
42881                 ];
42882             }
42883             
42884             var p = this.preferedCountries.reverse();
42885             
42886             if(p) {
42887                 for (var i = 0; i < p.length; i++) {
42888                     for (var j = 0; j < this.allCountries.length; j++) {
42889                         if(this.allCountries[j].iso2 == p[i]) {
42890                             var t = this.allCountries[j];
42891                             this.allCountries.splice(j,1);
42892                             this.allCountries.unshift(t);
42893                         }
42894                     } 
42895                 }
42896             }
42897             
42898             this.store.proxy.data = {
42899                 success: true,
42900                 data: this.allCountries
42901             };
42902             
42903             return cfg;
42904         },
42905         
42906         initEvents : function()
42907         {
42908             this.createList();
42909             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42910             
42911             this.indicator = this.indicatorEl();
42912             this.flag = this.flagEl();
42913             this.dialCodeHolder = this.dialCodeHolderEl();
42914             
42915             this.trigger = this.el.select('div.flag-box',true).first();
42916             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42917             
42918             var _this = this;
42919             
42920             (function(){
42921                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42922                 _this.list.setWidth(lw);
42923             }).defer(100);
42924             
42925             this.list.on('mouseover', this.onViewOver, this);
42926             this.list.on('mousemove', this.onViewMove, this);
42927             this.inputEl().on("keyup", this.onKeyUp, this);
42928             this.inputEl().on("keypress", this.onKeyPress, this);
42929             
42930             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42931
42932             this.view = new Roo.View(this.list, this.tpl, {
42933                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42934             });
42935             
42936             this.view.on('click', this.onViewClick, this);
42937             this.setValue(this.defaultDialCode);
42938         },
42939         
42940         onTriggerClick : function(e)
42941         {
42942             Roo.log('trigger click');
42943             if(this.disabled){
42944                 return;
42945             }
42946             
42947             if(this.isExpanded()){
42948                 this.collapse();
42949                 this.hasFocus = false;
42950             }else {
42951                 this.store.load({});
42952                 this.hasFocus = true;
42953                 this.expand();
42954             }
42955         },
42956         
42957         isExpanded : function()
42958         {
42959             return this.list.isVisible();
42960         },
42961         
42962         collapse : function()
42963         {
42964             if(!this.isExpanded()){
42965                 return;
42966             }
42967             this.list.hide();
42968             Roo.get(document).un('mousedown', this.collapseIf, this);
42969             Roo.get(document).un('mousewheel', this.collapseIf, this);
42970             this.fireEvent('collapse', this);
42971             this.validate();
42972         },
42973         
42974         expand : function()
42975         {
42976             Roo.log('expand');
42977
42978             if(this.isExpanded() || !this.hasFocus){
42979                 return;
42980             }
42981             
42982             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42983             this.list.setWidth(lw);
42984             
42985             this.list.show();
42986             this.restrictHeight();
42987             
42988             Roo.get(document).on('mousedown', this.collapseIf, this);
42989             Roo.get(document).on('mousewheel', this.collapseIf, this);
42990             
42991             this.fireEvent('expand', this);
42992         },
42993         
42994         restrictHeight : function()
42995         {
42996             this.list.alignTo(this.inputEl(), this.listAlign);
42997             this.list.alignTo(this.inputEl(), this.listAlign);
42998         },
42999         
43000         onViewOver : function(e, t)
43001         {
43002             if(this.inKeyMode){
43003                 return;
43004             }
43005             var item = this.view.findItemFromChild(t);
43006             
43007             if(item){
43008                 var index = this.view.indexOf(item);
43009                 this.select(index, false);
43010             }
43011         },
43012
43013         // private
43014         onViewClick : function(view, doFocus, el, e)
43015         {
43016             var index = this.view.getSelectedIndexes()[0];
43017             
43018             var r = this.store.getAt(index);
43019             
43020             if(r){
43021                 this.onSelect(r, index);
43022             }
43023             if(doFocus !== false && !this.blockFocus){
43024                 this.inputEl().focus();
43025             }
43026         },
43027         
43028         onViewMove : function(e, t)
43029         {
43030             this.inKeyMode = false;
43031         },
43032         
43033         select : function(index, scrollIntoView)
43034         {
43035             this.selectedIndex = index;
43036             this.view.select(index);
43037             if(scrollIntoView !== false){
43038                 var el = this.view.getNode(index);
43039                 if(el){
43040                     this.list.scrollChildIntoView(el, false);
43041                 }
43042             }
43043         },
43044         
43045         createList : function()
43046         {
43047             this.list = Roo.get(document.body).createChild({
43048                 tag: 'ul',
43049                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43050                 style: 'display:none'
43051             });
43052             
43053             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43054         },
43055         
43056         collapseIf : function(e)
43057         {
43058             var in_combo  = e.within(this.el);
43059             var in_list =  e.within(this.list);
43060             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43061             
43062             if (in_combo || in_list || is_list) {
43063                 return;
43064             }
43065             this.collapse();
43066         },
43067         
43068         onSelect : function(record, index)
43069         {
43070             if(this.fireEvent('beforeselect', this, record, index) !== false){
43071                 
43072                 this.setFlagClass(record.data.iso2);
43073                 this.setDialCode(record.data.dialCode);
43074                 this.hasFocus = false;
43075                 this.collapse();
43076                 this.fireEvent('select', this, record, index);
43077             }
43078         },
43079         
43080         flagEl : function()
43081         {
43082             var flag = this.el.select('div.flag',true).first();
43083             if(!flag){
43084                 return false;
43085             }
43086             return flag;
43087         },
43088         
43089         dialCodeHolderEl : function()
43090         {
43091             var d = this.el.select('input.dial-code-holder',true).first();
43092             if(!d){
43093                 return false;
43094             }
43095             return d;
43096         },
43097         
43098         setDialCode : function(v)
43099         {
43100             this.dialCodeHolder.dom.value = '+'+v;
43101         },
43102         
43103         setFlagClass : function(n)
43104         {
43105             this.flag.dom.className = 'flag '+n;
43106         },
43107         
43108         getValue : function()
43109         {
43110             var v = this.inputEl().getValue();
43111             if(this.dialCodeHolder) {
43112                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43113             }
43114             return v;
43115         },
43116         
43117         setValue : function(v)
43118         {
43119             var d = this.getDialCode(v);
43120             
43121             //invalid dial code
43122             if(v.length == 0 || !d || d.length == 0) {
43123                 if(this.rendered){
43124                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43125                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43126                 }
43127                 return;
43128             }
43129             
43130             //valid dial code
43131             this.setFlagClass(this.dialCodeMapping[d].iso2);
43132             this.setDialCode(d);
43133             this.inputEl().dom.value = v.replace('+'+d,'');
43134             this.hiddenEl().dom.value = this.getValue();
43135             
43136             this.validate();
43137         },
43138         
43139         getDialCode : function(v)
43140         {
43141             v = v ||  '';
43142             
43143             if (v.length == 0) {
43144                 return this.dialCodeHolder.dom.value;
43145             }
43146             
43147             var dialCode = "";
43148             if (v.charAt(0) != "+") {
43149                 return false;
43150             }
43151             var numericChars = "";
43152             for (var i = 1; i < v.length; i++) {
43153               var c = v.charAt(i);
43154               if (!isNaN(c)) {
43155                 numericChars += c;
43156                 if (this.dialCodeMapping[numericChars]) {
43157                   dialCode = v.substr(1, i);
43158                 }
43159                 if (numericChars.length == 4) {
43160                   break;
43161                 }
43162               }
43163             }
43164             return dialCode;
43165         },
43166         
43167         reset : function()
43168         {
43169             this.setValue(this.defaultDialCode);
43170             this.validate();
43171         },
43172         
43173         hiddenEl : function()
43174         {
43175             return this.el.select('input.hidden-tel-input',true).first();
43176         },
43177         
43178         // after setting val
43179         onKeyUp : function(e){
43180             this.setValue(this.getValue());
43181         },
43182         
43183         onKeyPress : function(e){
43184             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43185                 e.stopEvent();
43186             }
43187         }
43188         
43189 });
43190 /**
43191  * @class Roo.bootstrap.MoneyField
43192  * @extends Roo.bootstrap.ComboBox
43193  * Bootstrap MoneyField class
43194  * 
43195  * @constructor
43196  * Create a new MoneyField.
43197  * @param {Object} config Configuration options
43198  */
43199
43200 Roo.bootstrap.MoneyField = function(config) {
43201     
43202     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43203     
43204 };
43205
43206 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43207     
43208     /**
43209      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43210      */
43211     allowDecimals : true,
43212     /**
43213      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43214      */
43215     decimalSeparator : ".",
43216     /**
43217      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43218      */
43219     decimalPrecision : 0,
43220     /**
43221      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43222      */
43223     allowNegative : true,
43224     /**
43225      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43226      */
43227     allowZero: true,
43228     /**
43229      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43230      */
43231     minValue : Number.NEGATIVE_INFINITY,
43232     /**
43233      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43234      */
43235     maxValue : Number.MAX_VALUE,
43236     /**
43237      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43238      */
43239     minText : "The minimum value for this field is {0}",
43240     /**
43241      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43242      */
43243     maxText : "The maximum value for this field is {0}",
43244     /**
43245      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43246      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43247      */
43248     nanText : "{0} is not a valid number",
43249     /**
43250      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43251      */
43252     castInt : true,
43253     /**
43254      * @cfg {String} defaults currency of the MoneyField
43255      * value should be in lkey
43256      */
43257     defaultCurrency : false,
43258     /**
43259      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43260      */
43261     thousandsDelimiter : false,
43262     /**
43263      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43264      */
43265     max_length: false,
43266     
43267     inputlg : 9,
43268     inputmd : 9,
43269     inputsm : 9,
43270     inputxs : 6,
43271     
43272     store : false,
43273     
43274     getAutoCreate : function()
43275     {
43276         var align = this.labelAlign || this.parentLabelAlign();
43277         
43278         var id = Roo.id();
43279
43280         var cfg = {
43281             cls: 'form-group',
43282             cn: []
43283         };
43284
43285         var input =  {
43286             tag: 'input',
43287             id : id,
43288             cls : 'form-control roo-money-amount-input',
43289             autocomplete: 'new-password'
43290         };
43291         
43292         var hiddenInput = {
43293             tag: 'input',
43294             type: 'hidden',
43295             id: Roo.id(),
43296             cls: 'hidden-number-input'
43297         };
43298         
43299         if(this.max_length) {
43300             input.maxlength = this.max_length; 
43301         }
43302         
43303         if (this.name) {
43304             hiddenInput.name = this.name;
43305         }
43306
43307         if (this.disabled) {
43308             input.disabled = true;
43309         }
43310
43311         var clg = 12 - this.inputlg;
43312         var cmd = 12 - this.inputmd;
43313         var csm = 12 - this.inputsm;
43314         var cxs = 12 - this.inputxs;
43315         
43316         var container = {
43317             tag : 'div',
43318             cls : 'row roo-money-field',
43319             cn : [
43320                 {
43321                     tag : 'div',
43322                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43323                     cn : [
43324                         {
43325                             tag : 'div',
43326                             cls: 'roo-select2-container input-group',
43327                             cn: [
43328                                 {
43329                                     tag : 'input',
43330                                     cls : 'form-control roo-money-currency-input',
43331                                     autocomplete: 'new-password',
43332                                     readOnly : 1,
43333                                     name : this.currencyName
43334                                 },
43335                                 {
43336                                     tag :'span',
43337                                     cls : 'input-group-addon',
43338                                     cn : [
43339                                         {
43340                                             tag: 'span',
43341                                             cls: 'caret'
43342                                         }
43343                                     ]
43344                                 }
43345                             ]
43346                         }
43347                     ]
43348                 },
43349                 {
43350                     tag : 'div',
43351                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43352                     cn : [
43353                         {
43354                             tag: 'div',
43355                             cls: this.hasFeedback ? 'has-feedback' : '',
43356                             cn: [
43357                                 input
43358                             ]
43359                         }
43360                     ]
43361                 }
43362             ]
43363             
43364         };
43365         
43366         if (this.fieldLabel.length) {
43367             var indicator = {
43368                 tag: 'i',
43369                 tooltip: 'This field is required'
43370             };
43371
43372             var label = {
43373                 tag: 'label',
43374                 'for':  id,
43375                 cls: 'control-label',
43376                 cn: []
43377             };
43378
43379             var label_text = {
43380                 tag: 'span',
43381                 html: this.fieldLabel
43382             };
43383
43384             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43385             label.cn = [
43386                 indicator,
43387                 label_text
43388             ];
43389
43390             if(this.indicatorpos == 'right') {
43391                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43392                 label.cn = [
43393                     label_text,
43394                     indicator
43395                 ];
43396             }
43397
43398             if(align == 'left') {
43399                 container = {
43400                     tag: 'div',
43401                     cn: [
43402                         container
43403                     ]
43404                 };
43405
43406                 if(this.labelWidth > 12){
43407                     label.style = "width: " + this.labelWidth + 'px';
43408                 }
43409                 if(this.labelWidth < 13 && this.labelmd == 0){
43410                     this.labelmd = this.labelWidth;
43411                 }
43412                 if(this.labellg > 0){
43413                     label.cls += ' col-lg-' + this.labellg;
43414                     input.cls += ' col-lg-' + (12 - this.labellg);
43415                 }
43416                 if(this.labelmd > 0){
43417                     label.cls += ' col-md-' + this.labelmd;
43418                     container.cls += ' col-md-' + (12 - this.labelmd);
43419                 }
43420                 if(this.labelsm > 0){
43421                     label.cls += ' col-sm-' + this.labelsm;
43422                     container.cls += ' col-sm-' + (12 - this.labelsm);
43423                 }
43424                 if(this.labelxs > 0){
43425                     label.cls += ' col-xs-' + this.labelxs;
43426                     container.cls += ' col-xs-' + (12 - this.labelxs);
43427                 }
43428             }
43429         }
43430
43431         cfg.cn = [
43432             label,
43433             container,
43434             hiddenInput
43435         ];
43436         
43437         var settings = this;
43438
43439         ['xs','sm','md','lg'].map(function(size){
43440             if (settings[size]) {
43441                 cfg.cls += ' col-' + size + '-' + settings[size];
43442             }
43443         });
43444         
43445         return cfg;
43446     },
43447     
43448     initEvents : function()
43449     {
43450         this.indicator = this.indicatorEl();
43451         
43452         this.initCurrencyEvent();
43453         
43454         this.initNumberEvent();
43455     },
43456     
43457     initCurrencyEvent : function()
43458     {
43459         if (!this.store) {
43460             throw "can not find store for combo";
43461         }
43462         
43463         this.store = Roo.factory(this.store, Roo.data);
43464         this.store.parent = this;
43465         
43466         this.createList();
43467         
43468         this.triggerEl = this.el.select('.input-group-addon', true).first();
43469         
43470         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43471         
43472         var _this = this;
43473         
43474         (function(){
43475             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43476             _this.list.setWidth(lw);
43477         }).defer(100);
43478         
43479         this.list.on('mouseover', this.onViewOver, this);
43480         this.list.on('mousemove', this.onViewMove, this);
43481         this.list.on('scroll', this.onViewScroll, this);
43482         
43483         if(!this.tpl){
43484             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43485         }
43486         
43487         this.view = new Roo.View(this.list, this.tpl, {
43488             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43489         });
43490         
43491         this.view.on('click', this.onViewClick, this);
43492         
43493         this.store.on('beforeload', this.onBeforeLoad, this);
43494         this.store.on('load', this.onLoad, this);
43495         this.store.on('loadexception', this.onLoadException, this);
43496         
43497         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43498             "up" : function(e){
43499                 this.inKeyMode = true;
43500                 this.selectPrev();
43501             },
43502
43503             "down" : function(e){
43504                 if(!this.isExpanded()){
43505                     this.onTriggerClick();
43506                 }else{
43507                     this.inKeyMode = true;
43508                     this.selectNext();
43509                 }
43510             },
43511
43512             "enter" : function(e){
43513                 this.collapse();
43514                 
43515                 if(this.fireEvent("specialkey", this, e)){
43516                     this.onViewClick(false);
43517                 }
43518                 
43519                 return true;
43520             },
43521
43522             "esc" : function(e){
43523                 this.collapse();
43524             },
43525
43526             "tab" : function(e){
43527                 this.collapse();
43528                 
43529                 if(this.fireEvent("specialkey", this, e)){
43530                     this.onViewClick(false);
43531                 }
43532                 
43533                 return true;
43534             },
43535
43536             scope : this,
43537
43538             doRelay : function(foo, bar, hname){
43539                 if(hname == 'down' || this.scope.isExpanded()){
43540                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43541                 }
43542                 return true;
43543             },
43544
43545             forceKeyDown: true
43546         });
43547         
43548         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43549         
43550     },
43551     
43552     initNumberEvent : function(e)
43553     {
43554         this.inputEl().on("keydown" , this.fireKey,  this);
43555         this.inputEl().on("focus", this.onFocus,  this);
43556         this.inputEl().on("blur", this.onBlur,  this);
43557         
43558         this.inputEl().relayEvent('keyup', this);
43559         
43560         if(this.indicator){
43561             this.indicator.addClass('invisible');
43562         }
43563  
43564         this.originalValue = this.getValue();
43565         
43566         if(this.validationEvent == 'keyup'){
43567             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43568             this.inputEl().on('keyup', this.filterValidation, this);
43569         }
43570         else if(this.validationEvent !== false){
43571             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43572         }
43573         
43574         if(this.selectOnFocus){
43575             this.on("focus", this.preFocus, this);
43576             
43577         }
43578         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43579             this.inputEl().on("keypress", this.filterKeys, this);
43580         } else {
43581             this.inputEl().relayEvent('keypress', this);
43582         }
43583         
43584         var allowed = "0123456789";
43585         
43586         if(this.allowDecimals){
43587             allowed += this.decimalSeparator;
43588         }
43589         
43590         if(this.allowNegative){
43591             allowed += "-";
43592         }
43593         
43594         if(this.thousandsDelimiter) {
43595             allowed += ",";
43596         }
43597         
43598         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43599         
43600         var keyPress = function(e){
43601             
43602             var k = e.getKey();
43603             
43604             var c = e.getCharCode();
43605             
43606             if(
43607                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43608                     allowed.indexOf(String.fromCharCode(c)) === -1
43609             ){
43610                 e.stopEvent();
43611                 return;
43612             }
43613             
43614             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43615                 return;
43616             }
43617             
43618             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43619                 e.stopEvent();
43620             }
43621         };
43622         
43623         this.inputEl().on("keypress", keyPress, this);
43624         
43625     },
43626     
43627     onTriggerClick : function(e)
43628     {   
43629         if(this.disabled){
43630             return;
43631         }
43632         
43633         this.page = 0;
43634         this.loadNext = false;
43635         
43636         if(this.isExpanded()){
43637             this.collapse();
43638             return;
43639         }
43640         
43641         this.hasFocus = true;
43642         
43643         if(this.triggerAction == 'all') {
43644             this.doQuery(this.allQuery, true);
43645             return;
43646         }
43647         
43648         this.doQuery(this.getRawValue());
43649     },
43650     
43651     getCurrency : function()
43652     {   
43653         var v = this.currencyEl().getValue();
43654         
43655         return v;
43656     },
43657     
43658     restrictHeight : function()
43659     {
43660         this.list.alignTo(this.currencyEl(), this.listAlign);
43661         this.list.alignTo(this.currencyEl(), this.listAlign);
43662     },
43663     
43664     onViewClick : function(view, doFocus, el, e)
43665     {
43666         var index = this.view.getSelectedIndexes()[0];
43667         
43668         var r = this.store.getAt(index);
43669         
43670         if(r){
43671             this.onSelect(r, index);
43672         }
43673     },
43674     
43675     onSelect : function(record, index){
43676         
43677         if(this.fireEvent('beforeselect', this, record, index) !== false){
43678         
43679             this.setFromCurrencyData(index > -1 ? record.data : false);
43680             
43681             this.collapse();
43682             
43683             this.fireEvent('select', this, record, index);
43684         }
43685     },
43686     
43687     setFromCurrencyData : function(o)
43688     {
43689         var currency = '';
43690         
43691         this.lastCurrency = o;
43692         
43693         if (this.currencyField) {
43694             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43695         } else {
43696             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43697         }
43698         
43699         this.lastSelectionText = currency;
43700         
43701         //setting default currency
43702         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43703             this.setCurrency(this.defaultCurrency);
43704             return;
43705         }
43706         
43707         this.setCurrency(currency);
43708     },
43709     
43710     setFromData : function(o)
43711     {
43712         var c = {};
43713         
43714         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43715         
43716         this.setFromCurrencyData(c);
43717         
43718         var value = '';
43719         
43720         if (this.name) {
43721             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43722         } else {
43723             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43724         }
43725         
43726         this.setValue(value);
43727         
43728     },
43729     
43730     setCurrency : function(v)
43731     {   
43732         this.currencyValue = v;
43733         
43734         if(this.rendered){
43735             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43736             this.validate();
43737         }
43738     },
43739     
43740     setValue : function(v)
43741     {
43742         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43743         
43744         this.value = v;
43745         
43746         if(this.rendered){
43747             
43748             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43749             
43750             this.inputEl().dom.value = (v == '') ? '' :
43751                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43752             
43753             if(!this.allowZero && v === '0') {
43754                 this.hiddenEl().dom.value = '';
43755                 this.inputEl().dom.value = '';
43756             }
43757             
43758             this.validate();
43759         }
43760     },
43761     
43762     getRawValue : function()
43763     {
43764         var v = this.inputEl().getValue();
43765         
43766         return v;
43767     },
43768     
43769     getValue : function()
43770     {
43771         return this.fixPrecision(this.parseValue(this.getRawValue()));
43772     },
43773     
43774     parseValue : function(value)
43775     {
43776         if(this.thousandsDelimiter) {
43777             value += "";
43778             r = new RegExp(",", "g");
43779             value = value.replace(r, "");
43780         }
43781         
43782         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43783         return isNaN(value) ? '' : value;
43784         
43785     },
43786     
43787     fixPrecision : function(value)
43788     {
43789         if(this.thousandsDelimiter) {
43790             value += "";
43791             r = new RegExp(",", "g");
43792             value = value.replace(r, "");
43793         }
43794         
43795         var nan = isNaN(value);
43796         
43797         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43798             return nan ? '' : value;
43799         }
43800         return parseFloat(value).toFixed(this.decimalPrecision);
43801     },
43802     
43803     decimalPrecisionFcn : function(v)
43804     {
43805         return Math.floor(v);
43806     },
43807     
43808     validateValue : function(value)
43809     {
43810         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43811             return false;
43812         }
43813         
43814         var num = this.parseValue(value);
43815         
43816         if(isNaN(num)){
43817             this.markInvalid(String.format(this.nanText, value));
43818             return false;
43819         }
43820         
43821         if(num < this.minValue){
43822             this.markInvalid(String.format(this.minText, this.minValue));
43823             return false;
43824         }
43825         
43826         if(num > this.maxValue){
43827             this.markInvalid(String.format(this.maxText, this.maxValue));
43828             return false;
43829         }
43830         
43831         return true;
43832     },
43833     
43834     validate : function()
43835     {
43836         if(this.disabled || this.allowBlank){
43837             this.markValid();
43838             return true;
43839         }
43840         
43841         var currency = this.getCurrency();
43842         
43843         if(this.validateValue(this.getRawValue()) && currency.length){
43844             this.markValid();
43845             return true;
43846         }
43847         
43848         this.markInvalid();
43849         return false;
43850     },
43851     
43852     getName: function()
43853     {
43854         return this.name;
43855     },
43856     
43857     beforeBlur : function()
43858     {
43859         if(!this.castInt){
43860             return;
43861         }
43862         
43863         var v = this.parseValue(this.getRawValue());
43864         
43865         if(v || v == 0){
43866             this.setValue(v);
43867         }
43868     },
43869     
43870     onBlur : function()
43871     {
43872         this.beforeBlur();
43873         
43874         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43875             //this.el.removeClass(this.focusClass);
43876         }
43877         
43878         this.hasFocus = false;
43879         
43880         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43881             this.validate();
43882         }
43883         
43884         var v = this.getValue();
43885         
43886         if(String(v) !== String(this.startValue)){
43887             this.fireEvent('change', this, v, this.startValue);
43888         }
43889         
43890         this.fireEvent("blur", this);
43891     },
43892     
43893     inputEl : function()
43894     {
43895         return this.el.select('.roo-money-amount-input', true).first();
43896     },
43897     
43898     currencyEl : function()
43899     {
43900         return this.el.select('.roo-money-currency-input', true).first();
43901     },
43902     
43903     hiddenEl : function()
43904     {
43905         return this.el.select('input.hidden-number-input',true).first();
43906     }
43907     
43908 });/**
43909  * @class Roo.bootstrap.BezierSignature
43910  * @extends Roo.bootstrap.Component
43911  * Bootstrap BezierSignature class
43912  * This script refer to:
43913  *    Title: Signature Pad
43914  *    Author: szimek
43915  *    Availability: https://github.com/szimek/signature_pad
43916  *
43917  * @constructor
43918  * Create a new BezierSignature
43919  * @param {Object} config The config object
43920  */
43921
43922 Roo.bootstrap.BezierSignature = function(config){
43923     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43924     this.addEvents({
43925         "resize" : true
43926     });
43927 };
43928
43929 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43930 {
43931      
43932     curve_data: [],
43933     
43934     is_empty: true,
43935     
43936     mouse_btn_down: true,
43937     
43938     /**
43939      * @cfg {int} canvas height
43940      */
43941     canvas_height: '200px',
43942     
43943     /**
43944      * @cfg {float|function} Radius of a single dot.
43945      */ 
43946     dot_size: false,
43947     
43948     /**
43949      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43950      */
43951     min_width: 0.5,
43952     
43953     /**
43954      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43955      */
43956     max_width: 2.5,
43957     
43958     /**
43959      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43960      */
43961     throttle: 16,
43962     
43963     /**
43964      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43965      */
43966     min_distance: 5,
43967     
43968     /**
43969      * @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.
43970      */
43971     bg_color: 'rgba(0, 0, 0, 0)',
43972     
43973     /**
43974      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43975      */
43976     dot_color: 'black',
43977     
43978     /**
43979      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43980      */ 
43981     velocity_filter_weight: 0.7,
43982     
43983     /**
43984      * @cfg {function} Callback when stroke begin. 
43985      */
43986     onBegin: false,
43987     
43988     /**
43989      * @cfg {function} Callback when stroke end.
43990      */
43991     onEnd: false,
43992     
43993     getAutoCreate : function()
43994     {
43995         var cls = 'roo-signature column';
43996         
43997         if(this.cls){
43998             cls += ' ' + this.cls;
43999         }
44000         
44001         var col_sizes = [
44002             'lg',
44003             'md',
44004             'sm',
44005             'xs'
44006         ];
44007         
44008         for(var i = 0; i < col_sizes.length; i++) {
44009             if(this[col_sizes[i]]) {
44010                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44011             }
44012         }
44013         
44014         var cfg = {
44015             tag: 'div',
44016             cls: cls,
44017             cn: [
44018                 {
44019                     tag: 'div',
44020                     cls: 'roo-signature-body',
44021                     cn: [
44022                         {
44023                             tag: 'canvas',
44024                             cls: 'roo-signature-body-canvas',
44025                             height: this.canvas_height,
44026                             width: this.canvas_width
44027                         }
44028                     ]
44029                 },
44030                 {
44031                     tag: 'input',
44032                     type: 'file',
44033                     style: 'display: none'
44034                 }
44035             ]
44036         };
44037         
44038         return cfg;
44039     },
44040     
44041     initEvents: function() 
44042     {
44043         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44044         
44045         var canvas = this.canvasEl();
44046         
44047         // mouse && touch event swapping...
44048         canvas.dom.style.touchAction = 'none';
44049         canvas.dom.style.msTouchAction = 'none';
44050         
44051         this.mouse_btn_down = false;
44052         canvas.on('mousedown', this._handleMouseDown, this);
44053         canvas.on('mousemove', this._handleMouseMove, this);
44054         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44055         
44056         if (window.PointerEvent) {
44057             canvas.on('pointerdown', this._handleMouseDown, this);
44058             canvas.on('pointermove', this._handleMouseMove, this);
44059             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44060         }
44061         
44062         if ('ontouchstart' in window) {
44063             canvas.on('touchstart', this._handleTouchStart, this);
44064             canvas.on('touchmove', this._handleTouchMove, this);
44065             canvas.on('touchend', this._handleTouchEnd, this);
44066         }
44067         
44068         Roo.EventManager.onWindowResize(this.resize, this, true);
44069         
44070         // file input event
44071         this.fileEl().on('change', this.uploadImage, this);
44072         
44073         this.clear();
44074         
44075         this.resize();
44076     },
44077     
44078     resize: function(){
44079         
44080         var canvas = this.canvasEl().dom;
44081         var ctx = this.canvasElCtx();
44082         var img_data = false;
44083         
44084         if(canvas.width > 0) {
44085             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44086         }
44087         // setting canvas width will clean img data
44088         canvas.width = 0;
44089         
44090         var style = window.getComputedStyle ? 
44091             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44092             
44093         var padding_left = parseInt(style.paddingLeft) || 0;
44094         var padding_right = parseInt(style.paddingRight) || 0;
44095         
44096         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44097         
44098         if(img_data) {
44099             ctx.putImageData(img_data, 0, 0);
44100         }
44101     },
44102     
44103     _handleMouseDown: function(e)
44104     {
44105         if (e.browserEvent.which === 1) {
44106             this.mouse_btn_down = true;
44107             this.strokeBegin(e);
44108         }
44109     },
44110     
44111     _handleMouseMove: function (e)
44112     {
44113         if (this.mouse_btn_down) {
44114             this.strokeMoveUpdate(e);
44115         }
44116     },
44117     
44118     _handleMouseUp: function (e)
44119     {
44120         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44121             this.mouse_btn_down = false;
44122             this.strokeEnd(e);
44123         }
44124     },
44125     
44126     _handleTouchStart: function (e) {
44127         
44128         e.preventDefault();
44129         if (e.browserEvent.targetTouches.length === 1) {
44130             // var touch = e.browserEvent.changedTouches[0];
44131             // this.strokeBegin(touch);
44132             
44133              this.strokeBegin(e); // assume e catching the correct xy...
44134         }
44135     },
44136     
44137     _handleTouchMove: function (e) {
44138         e.preventDefault();
44139         // var touch = event.targetTouches[0];
44140         // _this._strokeMoveUpdate(touch);
44141         this.strokeMoveUpdate(e);
44142     },
44143     
44144     _handleTouchEnd: function (e) {
44145         var wasCanvasTouched = e.target === this.canvasEl().dom;
44146         if (wasCanvasTouched) {
44147             e.preventDefault();
44148             // var touch = event.changedTouches[0];
44149             // _this._strokeEnd(touch);
44150             this.strokeEnd(e);
44151         }
44152     },
44153     
44154     reset: function () {
44155         this._lastPoints = [];
44156         this._lastVelocity = 0;
44157         this._lastWidth = (this.min_width + this.max_width) / 2;
44158         this.canvasElCtx().fillStyle = this.dot_color;
44159     },
44160     
44161     strokeMoveUpdate: function(e)
44162     {
44163         this.strokeUpdate(e);
44164         
44165         if (this.throttle) {
44166             this.throttleStroke(this.strokeUpdate, this.throttle);
44167         }
44168         else {
44169             this.strokeUpdate(e);
44170         }
44171     },
44172     
44173     strokeBegin: function(e)
44174     {
44175         var newPointGroup = {
44176             color: this.dot_color,
44177             points: []
44178         };
44179         
44180         if (typeof this.onBegin === 'function') {
44181             this.onBegin(e);
44182         }
44183         
44184         this.curve_data.push(newPointGroup);
44185         this.reset();
44186         this.strokeUpdate(e);
44187     },
44188     
44189     strokeUpdate: function(e)
44190     {
44191         var rect = this.canvasEl().dom.getBoundingClientRect();
44192         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44193         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44194         var lastPoints = lastPointGroup.points;
44195         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44196         var isLastPointTooClose = lastPoint
44197             ? point.distanceTo(lastPoint) <= this.min_distance
44198             : false;
44199         var color = lastPointGroup.color;
44200         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44201             var curve = this.addPoint(point);
44202             if (!lastPoint) {
44203                 this.drawDot({color: color, point: point});
44204             }
44205             else if (curve) {
44206                 this.drawCurve({color: color, curve: curve});
44207             }
44208             lastPoints.push({
44209                 time: point.time,
44210                 x: point.x,
44211                 y: point.y
44212             });
44213         }
44214     },
44215     
44216     strokeEnd: function(e)
44217     {
44218         this.strokeUpdate(e);
44219         if (typeof this.onEnd === 'function') {
44220             this.onEnd(e);
44221         }
44222     },
44223     
44224     addPoint:  function (point) {
44225         var _lastPoints = this._lastPoints;
44226         _lastPoints.push(point);
44227         if (_lastPoints.length > 2) {
44228             if (_lastPoints.length === 3) {
44229                 _lastPoints.unshift(_lastPoints[0]);
44230             }
44231             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44232             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44233             _lastPoints.shift();
44234             return curve;
44235         }
44236         return null;
44237     },
44238     
44239     calculateCurveWidths: function (startPoint, endPoint) {
44240         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44241             (1 - this.velocity_filter_weight) * this._lastVelocity;
44242
44243         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44244         var widths = {
44245             end: newWidth,
44246             start: this._lastWidth
44247         };
44248         
44249         this._lastVelocity = velocity;
44250         this._lastWidth = newWidth;
44251         return widths;
44252     },
44253     
44254     drawDot: function (_a) {
44255         var color = _a.color, point = _a.point;
44256         var ctx = this.canvasElCtx();
44257         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44258         ctx.beginPath();
44259         this.drawCurveSegment(point.x, point.y, width);
44260         ctx.closePath();
44261         ctx.fillStyle = color;
44262         ctx.fill();
44263     },
44264     
44265     drawCurve: function (_a) {
44266         var color = _a.color, curve = _a.curve;
44267         var ctx = this.canvasElCtx();
44268         var widthDelta = curve.endWidth - curve.startWidth;
44269         var drawSteps = Math.floor(curve.length()) * 2;
44270         ctx.beginPath();
44271         ctx.fillStyle = color;
44272         for (var i = 0; i < drawSteps; i += 1) {
44273         var t = i / drawSteps;
44274         var tt = t * t;
44275         var ttt = tt * t;
44276         var u = 1 - t;
44277         var uu = u * u;
44278         var uuu = uu * u;
44279         var x = uuu * curve.startPoint.x;
44280         x += 3 * uu * t * curve.control1.x;
44281         x += 3 * u * tt * curve.control2.x;
44282         x += ttt * curve.endPoint.x;
44283         var y = uuu * curve.startPoint.y;
44284         y += 3 * uu * t * curve.control1.y;
44285         y += 3 * u * tt * curve.control2.y;
44286         y += ttt * curve.endPoint.y;
44287         var width = curve.startWidth + ttt * widthDelta;
44288         this.drawCurveSegment(x, y, width);
44289         }
44290         ctx.closePath();
44291         ctx.fill();
44292     },
44293     
44294     drawCurveSegment: function (x, y, width) {
44295         var ctx = this.canvasElCtx();
44296         ctx.moveTo(x, y);
44297         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44298         this.is_empty = false;
44299     },
44300     
44301     clear: function()
44302     {
44303         var ctx = this.canvasElCtx();
44304         var canvas = this.canvasEl().dom;
44305         ctx.fillStyle = this.bg_color;
44306         ctx.clearRect(0, 0, canvas.width, canvas.height);
44307         ctx.fillRect(0, 0, canvas.width, canvas.height);
44308         this.curve_data = [];
44309         this.reset();
44310         this.is_empty = true;
44311     },
44312     
44313     fileEl: function()
44314     {
44315         return  this.el.select('input',true).first();
44316     },
44317     
44318     canvasEl: function()
44319     {
44320         return this.el.select('canvas',true).first();
44321     },
44322     
44323     canvasElCtx: function()
44324     {
44325         return this.el.select('canvas',true).first().dom.getContext('2d');
44326     },
44327     
44328     getImage: function(type)
44329     {
44330         if(this.is_empty) {
44331             return false;
44332         }
44333         
44334         // encryption ?
44335         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44336     },
44337     
44338     drawFromImage: function(img_src)
44339     {
44340         var img = new Image();
44341         
44342         img.onload = function(){
44343             this.canvasElCtx().drawImage(img, 0, 0);
44344         }.bind(this);
44345         
44346         img.src = img_src;
44347         
44348         this.is_empty = false;
44349     },
44350     
44351     selectImage: function()
44352     {
44353         this.fileEl().dom.click();
44354     },
44355     
44356     uploadImage: function(e)
44357     {
44358         var reader = new FileReader();
44359         
44360         reader.onload = function(e){
44361             var img = new Image();
44362             img.onload = function(){
44363                 this.reset();
44364                 this.canvasElCtx().drawImage(img, 0, 0);
44365             }.bind(this);
44366             img.src = e.target.result;
44367         }.bind(this);
44368         
44369         reader.readAsDataURL(e.target.files[0]);
44370     },
44371     
44372     // Bezier Point Constructor
44373     Point: (function () {
44374         function Point(x, y, time) {
44375             this.x = x;
44376             this.y = y;
44377             this.time = time || Date.now();
44378         }
44379         Point.prototype.distanceTo = function (start) {
44380             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44381         };
44382         Point.prototype.equals = function (other) {
44383             return this.x === other.x && this.y === other.y && this.time === other.time;
44384         };
44385         Point.prototype.velocityFrom = function (start) {
44386             return this.time !== start.time
44387             ? this.distanceTo(start) / (this.time - start.time)
44388             : 0;
44389         };
44390         return Point;
44391     }()),
44392     
44393     
44394     // Bezier Constructor
44395     Bezier: (function () {
44396         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44397             this.startPoint = startPoint;
44398             this.control2 = control2;
44399             this.control1 = control1;
44400             this.endPoint = endPoint;
44401             this.startWidth = startWidth;
44402             this.endWidth = endWidth;
44403         }
44404         Bezier.fromPoints = function (points, widths, scope) {
44405             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44406             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44407             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44408         };
44409         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44410             var dx1 = s1.x - s2.x;
44411             var dy1 = s1.y - s2.y;
44412             var dx2 = s2.x - s3.x;
44413             var dy2 = s2.y - s3.y;
44414             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44415             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44416             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44417             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44418             var dxm = m1.x - m2.x;
44419             var dym = m1.y - m2.y;
44420             var k = l2 / (l1 + l2);
44421             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44422             var tx = s2.x - cm.x;
44423             var ty = s2.y - cm.y;
44424             return {
44425                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44426                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44427             };
44428         };
44429         Bezier.prototype.length = function () {
44430             var steps = 10;
44431             var length = 0;
44432             var px;
44433             var py;
44434             for (var i = 0; i <= steps; i += 1) {
44435                 var t = i / steps;
44436                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44437                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44438                 if (i > 0) {
44439                     var xdiff = cx - px;
44440                     var ydiff = cy - py;
44441                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44442                 }
44443                 px = cx;
44444                 py = cy;
44445             }
44446             return length;
44447         };
44448         Bezier.prototype.point = function (t, start, c1, c2, end) {
44449             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44450             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44451             + (3.0 * c2 * (1.0 - t) * t * t)
44452             + (end * t * t * t);
44453         };
44454         return Bezier;
44455     }()),
44456     
44457     throttleStroke: function(fn, wait) {
44458       if (wait === void 0) { wait = 250; }
44459       var previous = 0;
44460       var timeout = null;
44461       var result;
44462       var storedContext;
44463       var storedArgs;
44464       var later = function () {
44465           previous = Date.now();
44466           timeout = null;
44467           result = fn.apply(storedContext, storedArgs);
44468           if (!timeout) {
44469               storedContext = null;
44470               storedArgs = [];
44471           }
44472       };
44473       return function wrapper() {
44474           var args = [];
44475           for (var _i = 0; _i < arguments.length; _i++) {
44476               args[_i] = arguments[_i];
44477           }
44478           var now = Date.now();
44479           var remaining = wait - (now - previous);
44480           storedContext = this;
44481           storedArgs = args;
44482           if (remaining <= 0 || remaining > wait) {
44483               if (timeout) {
44484                   clearTimeout(timeout);
44485                   timeout = null;
44486               }
44487               previous = now;
44488               result = fn.apply(storedContext, storedArgs);
44489               if (!timeout) {
44490                   storedContext = null;
44491                   storedArgs = [];
44492               }
44493           }
44494           else if (!timeout) {
44495               timeout = window.setTimeout(later, remaining);
44496           }
44497           return result;
44498       };
44499   }
44500   
44501 });
44502
44503  
44504
44505