Fix #6641 - add events for handling view and download
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.header = html;
2683         if (this.headerContainerEl) {
2684             this.headerContainerEl.dom.innerHTML = html;
2685         }
2686     }
2687
2688     
2689 });
2690
2691 /*
2692  * - LGPL
2693  *
2694  * Card header - holder for the card header elements.
2695  * 
2696  */
2697
2698 /**
2699  * @class Roo.bootstrap.CardHeader
2700  * @extends Roo.bootstrap.Element
2701  * Bootstrap CardHeader class
2702  * @constructor
2703  * Create a new Card Header - that you can embed children into
2704  * @param {Object} config The config object
2705  */
2706
2707 Roo.bootstrap.CardHeader = function(config){
2708     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2709 };
2710
2711 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2712     
2713     
2714     container_method : 'getCardHeader' 
2715     
2716      
2717     
2718     
2719    
2720 });
2721
2722  
2723
2724  /*
2725  * - LGPL
2726  *
2727  * Card footer - holder for the card footer elements.
2728  * 
2729  */
2730
2731 /**
2732  * @class Roo.bootstrap.CardFooter
2733  * @extends Roo.bootstrap.Element
2734  * Bootstrap CardFooter class
2735  * @constructor
2736  * Create a new Card Footer - that you can embed children into
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.CardFooter = function(config){
2741     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2745     
2746     
2747     container_method : 'getCardFooter' 
2748     
2749      
2750     
2751     
2752    
2753 });
2754
2755  
2756
2757  /*
2758  * - LGPL
2759  *
2760  * Card header - holder for the card header elements.
2761  * 
2762  */
2763
2764 /**
2765  * @class Roo.bootstrap.CardImageTop
2766  * @extends Roo.bootstrap.Element
2767  * Bootstrap CardImageTop class
2768  * @constructor
2769  * Create a new Card Image Top container
2770  * @param {Object} config The config object
2771  */
2772
2773 Roo.bootstrap.CardImageTop = function(config){
2774     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2775 };
2776
2777 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2778     
2779    
2780     container_method : 'getCardImageTop' 
2781     
2782      
2783     
2784    
2785 });
2786
2787  
2788
2789  /*
2790  * - LGPL
2791  *
2792  * image
2793  * 
2794  */
2795
2796
2797 /**
2798  * @class Roo.bootstrap.Img
2799  * @extends Roo.bootstrap.Component
2800  * Bootstrap Img class
2801  * @cfg {Boolean} imgResponsive false | true
2802  * @cfg {String} border rounded | circle | thumbnail
2803  * @cfg {String} src image source
2804  * @cfg {String} alt image alternative text
2805  * @cfg {String} href a tag href
2806  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2807  * @cfg {String} xsUrl xs image source
2808  * @cfg {String} smUrl sm image source
2809  * @cfg {String} mdUrl md image source
2810  * @cfg {String} lgUrl lg image source
2811  * 
2812  * @constructor
2813  * Create a new Input
2814  * @param {Object} config The config object
2815  */
2816
2817 Roo.bootstrap.Img = function(config){
2818     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2819     
2820     this.addEvents({
2821         // img events
2822         /**
2823          * @event click
2824          * The img click event for the img.
2825          * @param {Roo.EventObject} e
2826          */
2827         "click" : true
2828     });
2829 };
2830
2831 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2832     
2833     imgResponsive: true,
2834     border: '',
2835     src: 'about:blank',
2836     href: false,
2837     target: false,
2838     xsUrl: '',
2839     smUrl: '',
2840     mdUrl: '',
2841     lgUrl: '',
2842
2843     getAutoCreate : function()
2844     {   
2845         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2846             return this.createSingleImg();
2847         }
2848         
2849         var cfg = {
2850             tag: 'div',
2851             cls: 'roo-image-responsive-group',
2852             cn: []
2853         };
2854         var _this = this;
2855         
2856         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2857             
2858             if(!_this[size + 'Url']){
2859                 return;
2860             }
2861             
2862             var img = {
2863                 tag: 'img',
2864                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2865                 html: _this.html || cfg.html,
2866                 src: _this[size + 'Url']
2867             };
2868             
2869             img.cls += ' roo-image-responsive-' + size;
2870             
2871             var s = ['xs', 'sm', 'md', 'lg'];
2872             
2873             s.splice(s.indexOf(size), 1);
2874             
2875             Roo.each(s, function(ss){
2876                 img.cls += ' hidden-' + ss;
2877             });
2878             
2879             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2880                 cfg.cls += ' img-' + _this.border;
2881             }
2882             
2883             if(_this.alt){
2884                 cfg.alt = _this.alt;
2885             }
2886             
2887             if(_this.href){
2888                 var a = {
2889                     tag: 'a',
2890                     href: _this.href,
2891                     cn: [
2892                         img
2893                     ]
2894                 };
2895
2896                 if(this.target){
2897                     a.target = _this.target;
2898                 }
2899             }
2900             
2901             cfg.cn.push((_this.href) ? a : img);
2902             
2903         });
2904         
2905         return cfg;
2906     },
2907     
2908     createSingleImg : function()
2909     {
2910         var cfg = {
2911             tag: 'img',
2912             cls: (this.imgResponsive) ? 'img-responsive' : '',
2913             html : null,
2914             src : 'about:blank'  // just incase src get's set to undefined?!?
2915         };
2916         
2917         cfg.html = this.html || cfg.html;
2918         
2919         cfg.src = this.src || cfg.src;
2920         
2921         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2922             cfg.cls += ' img-' + this.border;
2923         }
2924         
2925         if(this.alt){
2926             cfg.alt = this.alt;
2927         }
2928         
2929         if(this.href){
2930             var a = {
2931                 tag: 'a',
2932                 href: this.href,
2933                 cn: [
2934                     cfg
2935                 ]
2936             };
2937             
2938             if(this.target){
2939                 a.target = this.target;
2940             }
2941             
2942         }
2943         
2944         return (this.href) ? a : cfg;
2945     },
2946     
2947     initEvents: function() 
2948     {
2949         if(!this.href){
2950             this.el.on('click', this.onClick, this);
2951         }
2952         
2953     },
2954     
2955     onClick : function(e)
2956     {
2957         Roo.log('img onclick');
2958         this.fireEvent('click', this, e);
2959     },
2960     /**
2961      * Sets the url of the image - used to update it
2962      * @param {String} url the url of the image
2963      */
2964     
2965     setSrc : function(url)
2966     {
2967         this.src =  url;
2968         
2969         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2970             this.el.dom.src =  url;
2971             return;
2972         }
2973         
2974         this.el.select('img', true).first().dom.src =  url;
2975     }
2976     
2977     
2978    
2979 });
2980
2981  /*
2982  * - LGPL
2983  *
2984  * image
2985  * 
2986  */
2987
2988
2989 /**
2990  * @class Roo.bootstrap.Link
2991  * @extends Roo.bootstrap.Component
2992  * Bootstrap Link Class
2993  * @cfg {String} alt image alternative text
2994  * @cfg {String} href a tag href
2995  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2996  * @cfg {String} html the content of the link.
2997  * @cfg {String} anchor name for the anchor link
2998  * @cfg {String} fa - favicon
2999
3000  * @cfg {Boolean} preventDefault (true | false) default false
3001
3002  * 
3003  * @constructor
3004  * Create a new Input
3005  * @param {Object} config The config object
3006  */
3007
3008 Roo.bootstrap.Link = function(config){
3009     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3010     
3011     this.addEvents({
3012         // img events
3013         /**
3014          * @event click
3015          * The img click event for the img.
3016          * @param {Roo.EventObject} e
3017          */
3018         "click" : true
3019     });
3020 };
3021
3022 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3023     
3024     href: false,
3025     target: false,
3026     preventDefault: false,
3027     anchor : false,
3028     alt : false,
3029     fa: false,
3030
3031
3032     getAutoCreate : function()
3033     {
3034         var html = this.html || '';
3035         
3036         if (this.fa !== false) {
3037             html = '<i class="fa fa-' + this.fa + '"></i>';
3038         }
3039         var cfg = {
3040             tag: 'a'
3041         };
3042         // anchor's do not require html/href...
3043         if (this.anchor === false) {
3044             cfg.html = html;
3045             cfg.href = this.href || '#';
3046         } else {
3047             cfg.name = this.anchor;
3048             if (this.html !== false || this.fa !== false) {
3049                 cfg.html = html;
3050             }
3051             if (this.href !== false) {
3052                 cfg.href = this.href;
3053             }
3054         }
3055         
3056         if(this.alt !== false){
3057             cfg.alt = this.alt;
3058         }
3059         
3060         
3061         if(this.target !== false) {
3062             cfg.target = this.target;
3063         }
3064         
3065         return cfg;
3066     },
3067     
3068     initEvents: function() {
3069         
3070         if(!this.href || this.preventDefault){
3071             this.el.on('click', this.onClick, this);
3072         }
3073     },
3074     
3075     onClick : function(e)
3076     {
3077         if(this.preventDefault){
3078             e.preventDefault();
3079         }
3080         //Roo.log('img onclick');
3081         this.fireEvent('click', this, e);
3082     }
3083    
3084 });
3085
3086  /*
3087  * - LGPL
3088  *
3089  * header
3090  * 
3091  */
3092
3093 /**
3094  * @class Roo.bootstrap.Header
3095  * @extends Roo.bootstrap.Component
3096  * Bootstrap Header class
3097  * @cfg {String} html content of header
3098  * @cfg {Number} level (1|2|3|4|5|6) default 1
3099  * 
3100  * @constructor
3101  * Create a new Header
3102  * @param {Object} config The config object
3103  */
3104
3105
3106 Roo.bootstrap.Header  = function(config){
3107     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3108 };
3109
3110 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3111     
3112     //href : false,
3113     html : false,
3114     level : 1,
3115     
3116     
3117     
3118     getAutoCreate : function(){
3119         
3120         
3121         
3122         var cfg = {
3123             tag: 'h' + (1 *this.level),
3124             html: this.html || ''
3125         } ;
3126         
3127         return cfg;
3128     }
3129    
3130 });
3131
3132  
3133
3134  /*
3135  * Based on:
3136  * Ext JS Library 1.1.1
3137  * Copyright(c) 2006-2007, Ext JS, LLC.
3138  *
3139  * Originally Released Under LGPL - original licence link has changed is not relivant.
3140  *
3141  * Fork - LGPL
3142  * <script type="text/javascript">
3143  */
3144  
3145 /**
3146  * @class Roo.bootstrap.MenuMgr
3147  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3148  * @singleton
3149  */
3150 Roo.bootstrap.MenuMgr = function(){
3151    var menus, active, groups = {}, attached = false, lastShow = new Date();
3152
3153    // private - called when first menu is created
3154    function init(){
3155        menus = {};
3156        active = new Roo.util.MixedCollection();
3157        Roo.get(document).addKeyListener(27, function(){
3158            if(active.length > 0){
3159                hideAll();
3160            }
3161        });
3162    }
3163
3164    // private
3165    function hideAll(){
3166        if(active && active.length > 0){
3167            var c = active.clone();
3168            c.each(function(m){
3169                m.hide();
3170            });
3171        }
3172    }
3173
3174    // private
3175    function onHide(m){
3176        active.remove(m);
3177        if(active.length < 1){
3178            Roo.get(document).un("mouseup", onMouseDown);
3179             
3180            attached = false;
3181        }
3182    }
3183
3184    // private
3185    function onShow(m){
3186        var last = active.last();
3187        lastShow = new Date();
3188        active.add(m);
3189        if(!attached){
3190           Roo.get(document).on("mouseup", onMouseDown);
3191            
3192            attached = true;
3193        }
3194        if(m.parentMenu){
3195           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3196           m.parentMenu.activeChild = m;
3197        }else if(last && last.isVisible()){
3198           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3199        }
3200    }
3201
3202    // private
3203    function onBeforeHide(m){
3204        if(m.activeChild){
3205            m.activeChild.hide();
3206        }
3207        if(m.autoHideTimer){
3208            clearTimeout(m.autoHideTimer);
3209            delete m.autoHideTimer;
3210        }
3211    }
3212
3213    // private
3214    function onBeforeShow(m){
3215        var pm = m.parentMenu;
3216        if(!pm && !m.allowOtherMenus){
3217            hideAll();
3218        }else if(pm && pm.activeChild && active != m){
3219            pm.activeChild.hide();
3220        }
3221    }
3222
3223    // private this should really trigger on mouseup..
3224    function onMouseDown(e){
3225         Roo.log("on Mouse Up");
3226         
3227         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3228             Roo.log("MenuManager hideAll");
3229             hideAll();
3230             e.stopEvent();
3231         }
3232         
3233         
3234    }
3235
3236    // private
3237    function onBeforeCheck(mi, state){
3238        if(state){
3239            var g = groups[mi.group];
3240            for(var i = 0, l = g.length; i < l; i++){
3241                if(g[i] != mi){
3242                    g[i].setChecked(false);
3243                }
3244            }
3245        }
3246    }
3247
3248    return {
3249
3250        /**
3251         * Hides all menus that are currently visible
3252         */
3253        hideAll : function(){
3254             hideAll();  
3255        },
3256
3257        // private
3258        register : function(menu){
3259            if(!menus){
3260                init();
3261            }
3262            menus[menu.id] = menu;
3263            menu.on("beforehide", onBeforeHide);
3264            menu.on("hide", onHide);
3265            menu.on("beforeshow", onBeforeShow);
3266            menu.on("show", onShow);
3267            var g = menu.group;
3268            if(g && menu.events["checkchange"]){
3269                if(!groups[g]){
3270                    groups[g] = [];
3271                }
3272                groups[g].push(menu);
3273                menu.on("checkchange", onCheck);
3274            }
3275        },
3276
3277         /**
3278          * Returns a {@link Roo.menu.Menu} object
3279          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3280          * be used to generate and return a new Menu instance.
3281          */
3282        get : function(menu){
3283            if(typeof menu == "string"){ // menu id
3284                return menus[menu];
3285            }else if(menu.events){  // menu instance
3286                return menu;
3287            }
3288            /*else if(typeof menu.length == 'number'){ // array of menu items?
3289                return new Roo.bootstrap.Menu({items:menu});
3290            }else{ // otherwise, must be a config
3291                return new Roo.bootstrap.Menu(menu);
3292            }
3293            */
3294            return false;
3295        },
3296
3297        // private
3298        unregister : function(menu){
3299            delete menus[menu.id];
3300            menu.un("beforehide", onBeforeHide);
3301            menu.un("hide", onHide);
3302            menu.un("beforeshow", onBeforeShow);
3303            menu.un("show", onShow);
3304            var g = menu.group;
3305            if(g && menu.events["checkchange"]){
3306                groups[g].remove(menu);
3307                menu.un("checkchange", onCheck);
3308            }
3309        },
3310
3311        // private
3312        registerCheckable : function(menuItem){
3313            var g = menuItem.group;
3314            if(g){
3315                if(!groups[g]){
3316                    groups[g] = [];
3317                }
3318                groups[g].push(menuItem);
3319                menuItem.on("beforecheckchange", onBeforeCheck);
3320            }
3321        },
3322
3323        // private
3324        unregisterCheckable : function(menuItem){
3325            var g = menuItem.group;
3326            if(g){
3327                groups[g].remove(menuItem);
3328                menuItem.un("beforecheckchange", onBeforeCheck);
3329            }
3330        }
3331    };
3332 }();/*
3333  * - LGPL
3334  *
3335  * menu
3336  * 
3337  */
3338
3339 /**
3340  * @class Roo.bootstrap.Menu
3341  * @extends Roo.bootstrap.Component
3342  * Bootstrap Menu class - container for MenuItems
3343  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3344  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3345  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3346  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3347  * 
3348  * @constructor
3349  * Create a new Menu
3350  * @param {Object} config The config object
3351  */
3352
3353
3354 Roo.bootstrap.Menu = function(config){
3355     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3356     if (this.registerMenu && this.type != 'treeview')  {
3357         Roo.bootstrap.MenuMgr.register(this);
3358     }
3359     
3360     
3361     this.addEvents({
3362         /**
3363          * @event beforeshow
3364          * Fires before this menu is displayed (return false to block)
3365          * @param {Roo.menu.Menu} this
3366          */
3367         beforeshow : true,
3368         /**
3369          * @event beforehide
3370          * Fires before this menu is hidden (return false to block)
3371          * @param {Roo.menu.Menu} this
3372          */
3373         beforehide : true,
3374         /**
3375          * @event show
3376          * Fires after this menu is displayed
3377          * @param {Roo.menu.Menu} this
3378          */
3379         show : true,
3380         /**
3381          * @event hide
3382          * Fires after this menu is hidden
3383          * @param {Roo.menu.Menu} this
3384          */
3385         hide : true,
3386         /**
3387          * @event click
3388          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3389          * @param {Roo.menu.Menu} this
3390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391          * @param {Roo.EventObject} e
3392          */
3393         click : true,
3394         /**
3395          * @event mouseover
3396          * Fires when the mouse is hovering over this menu
3397          * @param {Roo.menu.Menu} this
3398          * @param {Roo.EventObject} e
3399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3400          */
3401         mouseover : true,
3402         /**
3403          * @event mouseout
3404          * Fires when the mouse exits this menu
3405          * @param {Roo.menu.Menu} this
3406          * @param {Roo.EventObject} e
3407          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3408          */
3409         mouseout : true,
3410         /**
3411          * @event itemclick
3412          * Fires when a menu item contained in this menu is clicked
3413          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3414          * @param {Roo.EventObject} e
3415          */
3416         itemclick: true
3417     });
3418     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3419 };
3420
3421 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3422     
3423    /// html : false,
3424     //align : '',
3425     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3426     type: false,
3427     /**
3428      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3429      */
3430     registerMenu : true,
3431     
3432     menuItems :false, // stores the menu items..
3433     
3434     hidden:true,
3435         
3436     parentMenu : false,
3437     
3438     stopEvent : true,
3439     
3440     isLink : false,
3441     
3442     getChildContainer : function() {
3443         return this.el;  
3444     },
3445     
3446     getAutoCreate : function(){
3447          
3448         //if (['right'].indexOf(this.align)!==-1) {
3449         //    cfg.cn[1].cls += ' pull-right'
3450         //}
3451         
3452         
3453         var cfg = {
3454             tag : 'ul',
3455             cls : 'dropdown-menu' ,
3456             style : 'z-index:1000'
3457             
3458         };
3459         
3460         if (this.type === 'submenu') {
3461             cfg.cls = 'submenu active';
3462         }
3463         if (this.type === 'treeview') {
3464             cfg.cls = 'treeview-menu';
3465         }
3466         
3467         return cfg;
3468     },
3469     initEvents : function() {
3470         
3471        // Roo.log("ADD event");
3472        // Roo.log(this.triggerEl.dom);
3473         
3474         this.triggerEl.on('click', this.onTriggerClick, this);
3475         
3476         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3477         
3478         
3479         if (this.triggerEl.hasClass('nav-item')) {
3480             // dropdown toggle on the 'a' in BS4?
3481             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3482         } else {
3483             this.triggerEl.addClass('dropdown-toggle');
3484         }
3485         if (Roo.isTouch) {
3486             this.el.on('touchstart'  , this.onTouch, this);
3487         }
3488         this.el.on('click' , this.onClick, this);
3489
3490         this.el.on("mouseover", this.onMouseOver, this);
3491         this.el.on("mouseout", this.onMouseOut, this);
3492         
3493     },
3494     
3495     findTargetItem : function(e)
3496     {
3497         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3498         if(!t){
3499             return false;
3500         }
3501         //Roo.log(t);         Roo.log(t.id);
3502         if(t && t.id){
3503             //Roo.log(this.menuitems);
3504             return this.menuitems.get(t.id);
3505             
3506             //return this.items.get(t.menuItemId);
3507         }
3508         
3509         return false;
3510     },
3511     
3512     onTouch : function(e) 
3513     {
3514         Roo.log("menu.onTouch");
3515         //e.stopEvent(); this make the user popdown broken
3516         this.onClick(e);
3517     },
3518     
3519     onClick : function(e)
3520     {
3521         Roo.log("menu.onClick");
3522         
3523         var t = this.findTargetItem(e);
3524         if(!t || t.isContainer){
3525             return;
3526         }
3527         Roo.log(e);
3528         /*
3529         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3530             if(t == this.activeItem && t.shouldDeactivate(e)){
3531                 this.activeItem.deactivate();
3532                 delete this.activeItem;
3533                 return;
3534             }
3535             if(t.canActivate){
3536                 this.setActiveItem(t, true);
3537             }
3538             return;
3539             
3540             
3541         }
3542         */
3543        
3544         Roo.log('pass click event');
3545         
3546         t.onClick(e);
3547         
3548         this.fireEvent("click", this, t, e);
3549         
3550         var _this = this;
3551         
3552         if(!t.href.length || t.href == '#'){
3553             (function() { _this.hide(); }).defer(100);
3554         }
3555         
3556     },
3557     
3558     onMouseOver : function(e){
3559         var t  = this.findTargetItem(e);
3560         //Roo.log(t);
3561         //if(t){
3562         //    if(t.canActivate && !t.disabled){
3563         //        this.setActiveItem(t, true);
3564         //    }
3565         //}
3566         
3567         this.fireEvent("mouseover", this, e, t);
3568     },
3569     isVisible : function(){
3570         return !this.hidden;
3571     },
3572     onMouseOut : function(e){
3573         var t  = this.findTargetItem(e);
3574         
3575         //if(t ){
3576         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3577         //        this.activeItem.deactivate();
3578         //        delete this.activeItem;
3579         //    }
3580         //}
3581         this.fireEvent("mouseout", this, e, t);
3582     },
3583     
3584     
3585     /**
3586      * Displays this menu relative to another element
3587      * @param {String/HTMLElement/Roo.Element} element The element to align to
3588      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3589      * the element (defaults to this.defaultAlign)
3590      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3591      */
3592     show : function(el, pos, parentMenu)
3593     {
3594         if (false === this.fireEvent("beforeshow", this)) {
3595             Roo.log("show canceled");
3596             return;
3597         }
3598         this.parentMenu = parentMenu;
3599         if(!this.el){
3600             this.render();
3601         }
3602         
3603         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3604     },
3605      /**
3606      * Displays this menu at a specific xy position
3607      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3608      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3609      */
3610     showAt : function(xy, parentMenu, /* private: */_e){
3611         this.parentMenu = parentMenu;
3612         if(!this.el){
3613             this.render();
3614         }
3615         if(_e !== false){
3616             this.fireEvent("beforeshow", this);
3617             //xy = this.el.adjustForConstraints(xy);
3618         }
3619         
3620         //this.el.show();
3621         this.hideMenuItems();
3622         this.hidden = false;
3623         this.triggerEl.addClass('open');
3624         this.el.addClass('show');
3625         
3626         // reassign x when hitting right
3627         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3628             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3629         }
3630         
3631         // reassign y when hitting bottom
3632         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3633             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3634         }
3635         
3636         // but the list may align on trigger left or trigger top... should it be a properity?
3637         
3638         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3639             this.el.setXY(xy);
3640         }
3641         
3642         this.focus();
3643         this.fireEvent("show", this);
3644     },
3645     
3646     focus : function(){
3647         return;
3648         if(!this.hidden){
3649             this.doFocus.defer(50, this);
3650         }
3651     },
3652
3653     doFocus : function(){
3654         if(!this.hidden){
3655             this.focusEl.focus();
3656         }
3657     },
3658
3659     /**
3660      * Hides this menu and optionally all parent menus
3661      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3662      */
3663     hide : function(deep)
3664     {
3665         if (false === this.fireEvent("beforehide", this)) {
3666             Roo.log("hide canceled");
3667             return;
3668         }
3669         this.hideMenuItems();
3670         if(this.el && this.isVisible()){
3671            
3672             if(this.activeItem){
3673                 this.activeItem.deactivate();
3674                 this.activeItem = null;
3675             }
3676             this.triggerEl.removeClass('open');;
3677             this.el.removeClass('show');
3678             this.hidden = true;
3679             this.fireEvent("hide", this);
3680         }
3681         if(deep === true && this.parentMenu){
3682             this.parentMenu.hide(true);
3683         }
3684     },
3685     
3686     onTriggerClick : function(e)
3687     {
3688         Roo.log('trigger click');
3689         
3690         var target = e.getTarget();
3691         
3692         Roo.log(target.nodeName.toLowerCase());
3693         
3694         if(target.nodeName.toLowerCase() === 'i'){
3695             e.preventDefault();
3696         }
3697         
3698     },
3699     
3700     onTriggerPress  : function(e)
3701     {
3702         Roo.log('trigger press');
3703         //Roo.log(e.getTarget());
3704        // Roo.log(this.triggerEl.dom);
3705        
3706         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3707         var pel = Roo.get(e.getTarget());
3708         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3709             Roo.log('is treeview or dropdown?');
3710             return;
3711         }
3712         
3713         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714             return;
3715         }
3716         
3717         if (this.isVisible()) {
3718             Roo.log('hide');
3719             this.hide();
3720         } else {
3721             Roo.log('show');
3722             this.show(this.triggerEl, '?', false);
3723         }
3724         
3725         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3726             e.stopEvent();
3727         }
3728         
3729     },
3730        
3731     
3732     hideMenuItems : function()
3733     {
3734         Roo.log("hide Menu Items");
3735         if (!this.el) { 
3736             return;
3737         }
3738         
3739         this.el.select('.open',true).each(function(aa) {
3740             
3741             aa.removeClass('open');
3742          
3743         });
3744     },
3745     addxtypeChild : function (tree, cntr) {
3746         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3747           
3748         this.menuitems.add(comp);
3749         return comp;
3750
3751     },
3752     getEl : function()
3753     {
3754         Roo.log(this.el);
3755         return this.el;
3756     },
3757     
3758     clear : function()
3759     {
3760         this.getEl().dom.innerHTML = '';
3761         this.menuitems.clear();
3762     }
3763 });
3764
3765  
3766  /*
3767  * - LGPL
3768  *
3769  * menu item
3770  * 
3771  */
3772
3773
3774 /**
3775  * @class Roo.bootstrap.MenuItem
3776  * @extends Roo.bootstrap.Component
3777  * Bootstrap MenuItem class
3778  * @cfg {String} html the menu label
3779  * @cfg {String} href the link
3780  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3781  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3782  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3783  * @cfg {String} fa favicon to show on left of menu item.
3784  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785  * 
3786  * 
3787  * @constructor
3788  * Create a new MenuItem
3789  * @param {Object} config The config object
3790  */
3791
3792
3793 Roo.bootstrap.MenuItem = function(config){
3794     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3795     this.addEvents({
3796         // raw events
3797         /**
3798          * @event click
3799          * The raw click event for the entire grid.
3800          * @param {Roo.bootstrap.MenuItem} this
3801          * @param {Roo.EventObject} e
3802          */
3803         "click" : true
3804     });
3805 };
3806
3807 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3808     
3809     href : false,
3810     html : false,
3811     preventDefault: false,
3812     isContainer : false,
3813     active : false,
3814     fa: false,
3815     
3816     getAutoCreate : function(){
3817         
3818         if(this.isContainer){
3819             return {
3820                 tag: 'li',
3821                 cls: 'dropdown-menu-item '
3822             };
3823         }
3824         var ctag = {
3825             tag: 'span',
3826             html: 'Link'
3827         };
3828         
3829         var anc = {
3830             tag : 'a',
3831             cls : 'dropdown-item',
3832             href : '#',
3833             cn : [  ]
3834         };
3835         
3836         if (this.fa !== false) {
3837             anc.cn.push({
3838                 tag : 'i',
3839                 cls : 'fa fa-' + this.fa
3840             });
3841         }
3842         
3843         anc.cn.push(ctag);
3844         
3845         
3846         var cfg= {
3847             tag: 'li',
3848             cls: 'dropdown-menu-item',
3849             cn: [ anc ]
3850         };
3851         if (this.parent().type == 'treeview') {
3852             cfg.cls = 'treeview-menu';
3853         }
3854         if (this.active) {
3855             cfg.cls += ' active';
3856         }
3857         
3858         
3859         
3860         anc.href = this.href || cfg.cn[0].href ;
3861         ctag.html = this.html || cfg.cn[0].html ;
3862         return cfg;
3863     },
3864     
3865     initEvents: function()
3866     {
3867         if (this.parent().type == 'treeview') {
3868             this.el.select('a').on('click', this.onClick, this);
3869         }
3870         
3871         if (this.menu) {
3872             this.menu.parentType = this.xtype;
3873             this.menu.triggerEl = this.el;
3874             this.menu = this.addxtype(Roo.apply({}, this.menu));
3875         }
3876         
3877     },
3878     onClick : function(e)
3879     {
3880         Roo.log('item on click ');
3881         
3882         if(this.preventDefault){
3883             e.preventDefault();
3884         }
3885         //this.parent().hideMenuItems();
3886         
3887         this.fireEvent('click', this, e);
3888     },
3889     getEl : function()
3890     {
3891         return this.el;
3892     } 
3893 });
3894
3895  
3896
3897  /*
3898  * - LGPL
3899  *
3900  * menu separator
3901  * 
3902  */
3903
3904
3905 /**
3906  * @class Roo.bootstrap.MenuSeparator
3907  * @extends Roo.bootstrap.Component
3908  * Bootstrap MenuSeparator class
3909  * 
3910  * @constructor
3911  * Create a new MenuItem
3912  * @param {Object} config The config object
3913  */
3914
3915
3916 Roo.bootstrap.MenuSeparator = function(config){
3917     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3918 };
3919
3920 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3921     
3922     getAutoCreate : function(){
3923         var cfg = {
3924             cls: 'divider',
3925             tag : 'li'
3926         };
3927         
3928         return cfg;
3929     }
3930    
3931 });
3932
3933  
3934
3935  
3936 /*
3937 * Licence: LGPL
3938 */
3939
3940 /**
3941  * @class Roo.bootstrap.Modal
3942  * @extends Roo.bootstrap.Component
3943  * Bootstrap Modal class
3944  * @cfg {String} title Title of dialog
3945  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3946  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3947  * @cfg {Boolean} specificTitle default false
3948  * @cfg {Array} buttons Array of buttons or standard button set..
3949  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3950  * @cfg {Boolean} animate default true
3951  * @cfg {Boolean} allow_close default true
3952  * @cfg {Boolean} fitwindow default false
3953  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3954  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3955  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3956  * @cfg {String} size (sm|lg|xl) default empty
3957  * @cfg {Number} max_width set the max width of modal
3958  * @cfg {Boolean} editableTitle can the title be edited
3959
3960  *
3961  *
3962  * @constructor
3963  * Create a new Modal Dialog
3964  * @param {Object} config The config object
3965  */
3966
3967 Roo.bootstrap.Modal = function(config){
3968     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3969     this.addEvents({
3970         // raw events
3971         /**
3972          * @event btnclick
3973          * The raw btnclick event for the button
3974          * @param {Roo.EventObject} e
3975          */
3976         "btnclick" : true,
3977         /**
3978          * @event resize
3979          * Fire when dialog resize
3980          * @param {Roo.bootstrap.Modal} this
3981          * @param {Roo.EventObject} e
3982          */
3983         "resize" : true,
3984         /**
3985          * @event titlechanged
3986          * Fire when the editable title has been changed
3987          * @param {Roo.bootstrap.Modal} this
3988          * @param {Roo.EventObject} value
3989          */
3990         "titlechanged" : true 
3991         
3992     });
3993     this.buttons = this.buttons || [];
3994
3995     if (this.tmpl) {
3996         this.tmpl = Roo.factory(this.tmpl);
3997     }
3998
3999 };
4000
4001 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4002
4003     title : 'test dialog',
4004
4005     buttons : false,
4006
4007     // set on load...
4008
4009     html: false,
4010
4011     tmp: false,
4012
4013     specificTitle: false,
4014
4015     buttonPosition: 'right',
4016
4017     allow_close : true,
4018
4019     animate : true,
4020
4021     fitwindow: false,
4022     
4023      // private
4024     dialogEl: false,
4025     bodyEl:  false,
4026     footerEl:  false,
4027     titleEl:  false,
4028     closeEl:  false,
4029
4030     size: '',
4031     
4032     max_width: 0,
4033     
4034     max_height: 0,
4035     
4036     fit_content: false,
4037     editableTitle  : false,
4038
4039     onRender : function(ct, position)
4040     {
4041         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4042
4043         if(!this.el){
4044             var cfg = Roo.apply({},  this.getAutoCreate());
4045             cfg.id = Roo.id();
4046             //if(!cfg.name){
4047             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4048             //}
4049             //if (!cfg.name.length) {
4050             //    delete cfg.name;
4051            // }
4052             if (this.cls) {
4053                 cfg.cls += ' ' + this.cls;
4054             }
4055             if (this.style) {
4056                 cfg.style = this.style;
4057             }
4058             this.el = Roo.get(document.body).createChild(cfg, position);
4059         }
4060         //var type = this.el.dom.type;
4061
4062
4063         if(this.tabIndex !== undefined){
4064             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4065         }
4066
4067         this.dialogEl = this.el.select('.modal-dialog',true).first();
4068         this.bodyEl = this.el.select('.modal-body',true).first();
4069         this.closeEl = this.el.select('.modal-header .close', true).first();
4070         this.headerEl = this.el.select('.modal-header',true).first();
4071         this.titleEl = this.el.select('.modal-title',true).first();
4072         this.footerEl = this.el.select('.modal-footer',true).first();
4073
4074         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4075         
4076         //this.el.addClass("x-dlg-modal");
4077
4078         if (this.buttons.length) {
4079             Roo.each(this.buttons, function(bb) {
4080                 var b = Roo.apply({}, bb);
4081                 b.xns = b.xns || Roo.bootstrap;
4082                 b.xtype = b.xtype || 'Button';
4083                 if (typeof(b.listeners) == 'undefined') {
4084                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4085                 }
4086
4087                 var btn = Roo.factory(b);
4088
4089                 btn.render(this.getButtonContainer());
4090
4091             },this);
4092         }
4093         // render the children.
4094         var nitems = [];
4095
4096         if(typeof(this.items) != 'undefined'){
4097             var items = this.items;
4098             delete this.items;
4099
4100             for(var i =0;i < items.length;i++) {
4101                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4102             }
4103         }
4104
4105         this.items = nitems;
4106
4107         // where are these used - they used to be body/close/footer
4108
4109
4110         this.initEvents();
4111         //this.el.addClass([this.fieldClass, this.cls]);
4112
4113     },
4114
4115     getAutoCreate : function()
4116     {
4117         // we will default to modal-body-overflow - might need to remove or make optional later.
4118         var bdy = {
4119                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4120                 html : this.html || ''
4121         };
4122
4123         var title = {
4124             tag: 'h5',
4125             cls : 'modal-title',
4126             html : this.title
4127         };
4128
4129         if(this.specificTitle){ // WTF is this?
4130             title = this.title;
4131         }
4132
4133         var header = [];
4134         if (this.allow_close && Roo.bootstrap.version == 3) {
4135             header.push({
4136                 tag: 'button',
4137                 cls : 'close',
4138                 html : '&times'
4139             });
4140         }
4141
4142         header.push(title);
4143
4144         if (this.editableTitle) {
4145             header.push({
4146                 cls: 'form-control roo-editable-title d-none',
4147                 tag: 'input',
4148                 type: 'text'
4149             });
4150         }
4151         
4152         if (this.allow_close && Roo.bootstrap.version == 4) {
4153             header.push({
4154                 tag: 'button',
4155                 cls : 'close',
4156                 html : '&times'
4157             });
4158         }
4159         
4160         var size = '';
4161
4162         if(this.size.length){
4163             size = 'modal-' + this.size;
4164         }
4165         
4166         var footer = Roo.bootstrap.version == 3 ?
4167             {
4168                 cls : 'modal-footer',
4169                 cn : [
4170                     {
4171                         tag: 'div',
4172                         cls: 'btn-' + this.buttonPosition
4173                     }
4174                 ]
4175
4176             } :
4177             {  // BS4 uses mr-auto on left buttons....
4178                 cls : 'modal-footer'
4179             };
4180
4181             
4182
4183         
4184         
4185         var modal = {
4186             cls: "modal",
4187              cn : [
4188                 {
4189                     cls: "modal-dialog " + size,
4190                     cn : [
4191                         {
4192                             cls : "modal-content",
4193                             cn : [
4194                                 {
4195                                     cls : 'modal-header',
4196                                     cn : header
4197                                 },
4198                                 bdy,
4199                                 footer
4200                             ]
4201
4202                         }
4203                     ]
4204
4205                 }
4206             ]
4207         };
4208
4209         if(this.animate){
4210             modal.cls += ' fade';
4211         }
4212
4213         return modal;
4214
4215     },
4216     getChildContainer : function() {
4217
4218          return this.bodyEl;
4219
4220     },
4221     getButtonContainer : function() {
4222         
4223          return Roo.bootstrap.version == 4 ?
4224             this.el.select('.modal-footer',true).first()
4225             : this.el.select('.modal-footer div',true).first();
4226
4227     },
4228     initEvents : function()
4229     {
4230         if (this.allow_close) {
4231             this.closeEl.on('click', this.hide, this);
4232         }
4233         Roo.EventManager.onWindowResize(this.resize, this, true);
4234         if (this.editableTitle) {
4235             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4236             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4237             this.headerEditEl.on('keyup', function(e) {
4238                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4239                         this.toggleHeaderInput(false)
4240                     }
4241                 }, this);
4242             this.headerEditEl.on('blur', function(e) {
4243                 this.toggleHeaderInput(false)
4244             },this);
4245         }
4246
4247     },
4248   
4249
4250     resize : function()
4251     {
4252         this.maskEl.setSize(
4253             Roo.lib.Dom.getViewWidth(true),
4254             Roo.lib.Dom.getViewHeight(true)
4255         );
4256         
4257         if (this.fitwindow) {
4258             
4259            this.dialogEl.setStyle( { 'max-width' : '100%' });
4260             this.setSize(
4261                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4262                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4263             );
4264             return;
4265         }
4266         
4267         if(this.max_width !== 0) {
4268             
4269             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4270             
4271             if(this.height) {
4272                 this.setSize(w, this.height);
4273                 return;
4274             }
4275             
4276             if(this.max_height) {
4277                 this.setSize(w,Math.min(
4278                     this.max_height,
4279                     Roo.lib.Dom.getViewportHeight(true) - 60
4280                 ));
4281                 
4282                 return;
4283             }
4284             
4285             if(!this.fit_content) {
4286                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4287                 return;
4288             }
4289             
4290             this.setSize(w, Math.min(
4291                 60 +
4292                 this.headerEl.getHeight() + 
4293                 this.footerEl.getHeight() + 
4294                 this.getChildHeight(this.bodyEl.dom.childNodes),
4295                 Roo.lib.Dom.getViewportHeight(true) - 60)
4296             );
4297         }
4298         
4299     },
4300
4301     setSize : function(w,h)
4302     {
4303         if (!w && !h) {
4304             return;
4305         }
4306         
4307         this.resizeTo(w,h);
4308     },
4309
4310     show : function() {
4311
4312         if (!this.rendered) {
4313             this.render();
4314         }
4315         this.toggleHeaderInput(false);
4316         //this.el.setStyle('display', 'block');
4317         this.el.removeClass('hideing');
4318         this.el.dom.style.display='block';
4319         
4320         Roo.get(document.body).addClass('modal-open');
4321  
4322         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4323             
4324             (function(){
4325                 this.el.addClass('show');
4326                 this.el.addClass('in');
4327             }).defer(50, this);
4328         }else{
4329             this.el.addClass('show');
4330             this.el.addClass('in');
4331         }
4332
4333         // not sure how we can show data in here..
4334         //if (this.tmpl) {
4335         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4336         //}
4337
4338         Roo.get(document.body).addClass("x-body-masked");
4339         
4340         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4341         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342         this.maskEl.dom.style.display = 'block';
4343         this.maskEl.addClass('show');
4344         
4345         
4346         this.resize();
4347         
4348         this.fireEvent('show', this);
4349
4350         // set zindex here - otherwise it appears to be ignored...
4351         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4352
4353         (function () {
4354             this.items.forEach( function(e) {
4355                 e.layout ? e.layout() : false;
4356
4357             });
4358         }).defer(100,this);
4359
4360     },
4361     hide : function()
4362     {
4363         if(this.fireEvent("beforehide", this) !== false){
4364             
4365             this.maskEl.removeClass('show');
4366             
4367             this.maskEl.dom.style.display = '';
4368             Roo.get(document.body).removeClass("x-body-masked");
4369             this.el.removeClass('in');
4370             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4371
4372             if(this.animate){ // why
4373                 this.el.addClass('hideing');
4374                 this.el.removeClass('show');
4375                 (function(){
4376                     if (!this.el.hasClass('hideing')) {
4377                         return; // it's been shown again...
4378                     }
4379                     
4380                     this.el.dom.style.display='';
4381
4382                     Roo.get(document.body).removeClass('modal-open');
4383                     this.el.removeClass('hideing');
4384                 }).defer(150,this);
4385                 
4386             }else{
4387                 this.el.removeClass('show');
4388                 this.el.dom.style.display='';
4389                 Roo.get(document.body).removeClass('modal-open');
4390
4391             }
4392             this.fireEvent('hide', this);
4393         }
4394     },
4395     isVisible : function()
4396     {
4397         
4398         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4399         
4400     },
4401
4402     addButton : function(str, cb)
4403     {
4404
4405
4406         var b = Roo.apply({}, { html : str } );
4407         b.xns = b.xns || Roo.bootstrap;
4408         b.xtype = b.xtype || 'Button';
4409         if (typeof(b.listeners) == 'undefined') {
4410             b.listeners = { click : cb.createDelegate(this)  };
4411         }
4412
4413         var btn = Roo.factory(b);
4414
4415         btn.render(this.getButtonContainer());
4416
4417         return btn;
4418
4419     },
4420
4421     setDefaultButton : function(btn)
4422     {
4423         //this.el.select('.modal-footer').()
4424     },
4425
4426     resizeTo: function(w,h)
4427     {
4428         this.dialogEl.setWidth(w);
4429         
4430         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4431
4432         this.bodyEl.setHeight(h - diff);
4433         
4434         this.fireEvent('resize', this);
4435     },
4436     
4437     setContentSize  : function(w, h)
4438     {
4439
4440     },
4441     onButtonClick: function(btn,e)
4442     {
4443         //Roo.log([a,b,c]);
4444         this.fireEvent('btnclick', btn.name, e);
4445     },
4446      /**
4447      * Set the title of the Dialog
4448      * @param {String} str new Title
4449      */
4450     setTitle: function(str) {
4451         this.titleEl.dom.innerHTML = str;
4452         this.title = str;
4453     },
4454     /**
4455      * Set the body of the Dialog
4456      * @param {String} str new Title
4457      */
4458     setBody: function(str) {
4459         this.bodyEl.dom.innerHTML = str;
4460     },
4461     /**
4462      * Set the body of the Dialog using the template
4463      * @param {Obj} data - apply this data to the template and replace the body contents.
4464      */
4465     applyBody: function(obj)
4466     {
4467         if (!this.tmpl) {
4468             Roo.log("Error - using apply Body without a template");
4469             //code
4470         }
4471         this.tmpl.overwrite(this.bodyEl, obj);
4472     },
4473     
4474     getChildHeight : function(child_nodes)
4475     {
4476         if(
4477             !child_nodes ||
4478             child_nodes.length == 0
4479         ) {
4480             return 0;
4481         }
4482         
4483         var child_height = 0;
4484         
4485         for(var i = 0; i < child_nodes.length; i++) {
4486             
4487             /*
4488             * for modal with tabs...
4489             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4490                 
4491                 var layout_childs = child_nodes[i].childNodes;
4492                 
4493                 for(var j = 0; j < layout_childs.length; j++) {
4494                     
4495                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4496                         
4497                         var layout_body_childs = layout_childs[j].childNodes;
4498                         
4499                         for(var k = 0; k < layout_body_childs.length; k++) {
4500                             
4501                             if(layout_body_childs[k].classList.contains('navbar')) {
4502                                 child_height += layout_body_childs[k].offsetHeight;
4503                                 continue;
4504                             }
4505                             
4506                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4507                                 
4508                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4509                                 
4510                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4511                                     
4512                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4513                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4514                                         continue;
4515                                     }
4516                                     
4517                                 }
4518                                 
4519                             }
4520                             
4521                         }
4522                     }
4523                 }
4524                 continue;
4525             }
4526             */
4527             
4528             child_height += child_nodes[i].offsetHeight;
4529             // Roo.log(child_nodes[i].offsetHeight);
4530         }
4531         
4532         return child_height;
4533     },
4534     toggleHeaderInput : function(is_edit)
4535     {
4536         if (!this.editableTitle) {
4537             return; // not editable.
4538         }
4539         if (is_edit && this.is_header_editing) {
4540             return; // already editing..
4541         }
4542         if (is_edit) {
4543     
4544             this.headerEditEl.dom.value = this.title;
4545             this.headerEditEl.removeClass('d-none');
4546             this.headerEditEl.dom.focus();
4547             this.titleEl.addClass('d-none');
4548             
4549             this.is_header_editing = true;
4550             return
4551         }
4552         // flip back to not editing.
4553         this.title = this.headerEditEl.dom.value;
4554         this.headerEditEl.addClass('d-none');
4555         this.titleEl.removeClass('d-none');
4556         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4557         this.is_header_editing = false;
4558         this.fireEvent('titlechanged', this, this.title);
4559     
4560             
4561         
4562     }
4563
4564 });
4565
4566
4567 Roo.apply(Roo.bootstrap.Modal,  {
4568     /**
4569          * Button config that displays a single OK button
4570          * @type Object
4571          */
4572         OK :  [{
4573             name : 'ok',
4574             weight : 'primary',
4575             html : 'OK'
4576         }],
4577         /**
4578          * Button config that displays Yes and No buttons
4579          * @type Object
4580          */
4581         YESNO : [
4582             {
4583                 name  : 'no',
4584                 html : 'No'
4585             },
4586             {
4587                 name  :'yes',
4588                 weight : 'primary',
4589                 html : 'Yes'
4590             }
4591         ],
4592
4593         /**
4594          * Button config that displays OK and Cancel buttons
4595          * @type Object
4596          */
4597         OKCANCEL : [
4598             {
4599                name : 'cancel',
4600                 html : 'Cancel'
4601             },
4602             {
4603                 name : 'ok',
4604                 weight : 'primary',
4605                 html : 'OK'
4606             }
4607         ],
4608         /**
4609          * Button config that displays Yes, No and Cancel buttons
4610          * @type Object
4611          */
4612         YESNOCANCEL : [
4613             {
4614                 name : 'yes',
4615                 weight : 'primary',
4616                 html : 'Yes'
4617             },
4618             {
4619                 name : 'no',
4620                 html : 'No'
4621             },
4622             {
4623                 name : 'cancel',
4624                 html : 'Cancel'
4625             }
4626         ],
4627         
4628         zIndex : 10001
4629 });
4630
4631 /*
4632  * - LGPL
4633  *
4634  * messagebox - can be used as a replace
4635  * 
4636  */
4637 /**
4638  * @class Roo.MessageBox
4639  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4640  * Example usage:
4641  *<pre><code>
4642 // Basic alert:
4643 Roo.Msg.alert('Status', 'Changes saved successfully.');
4644
4645 // Prompt for user data:
4646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4647     if (btn == 'ok'){
4648         // process text value...
4649     }
4650 });
4651
4652 // Show a dialog using config options:
4653 Roo.Msg.show({
4654    title:'Save Changes?',
4655    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4656    buttons: Roo.Msg.YESNOCANCEL,
4657    fn: processResult,
4658    animEl: 'elId'
4659 });
4660 </code></pre>
4661  * @singleton
4662  */
4663 Roo.bootstrap.MessageBox = function(){
4664     var dlg, opt, mask, waitTimer;
4665     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4666     var buttons, activeTextEl, bwidth;
4667
4668     
4669     // private
4670     var handleButton = function(button){
4671         dlg.hide();
4672         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4673     };
4674
4675     // private
4676     var handleHide = function(){
4677         if(opt && opt.cls){
4678             dlg.el.removeClass(opt.cls);
4679         }
4680         //if(waitTimer){
4681         //    Roo.TaskMgr.stop(waitTimer);
4682         //    waitTimer = null;
4683         //}
4684     };
4685
4686     // private
4687     var updateButtons = function(b){
4688         var width = 0;
4689         if(!b){
4690             buttons["ok"].hide();
4691             buttons["cancel"].hide();
4692             buttons["yes"].hide();
4693             buttons["no"].hide();
4694             dlg.footerEl.hide();
4695             
4696             return width;
4697         }
4698         dlg.footerEl.show();
4699         for(var k in buttons){
4700             if(typeof buttons[k] != "function"){
4701                 if(b[k]){
4702                     buttons[k].show();
4703                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4704                     width += buttons[k].el.getWidth()+15;
4705                 }else{
4706                     buttons[k].hide();
4707                 }
4708             }
4709         }
4710         return width;
4711     };
4712
4713     // private
4714     var handleEsc = function(d, k, e){
4715         if(opt && opt.closable !== false){
4716             dlg.hide();
4717         }
4718         if(e){
4719             e.stopEvent();
4720         }
4721     };
4722
4723     return {
4724         /**
4725          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4726          * @return {Roo.BasicDialog} The BasicDialog element
4727          */
4728         getDialog : function(){
4729            if(!dlg){
4730                 dlg = new Roo.bootstrap.Modal( {
4731                     //draggable: true,
4732                     //resizable:false,
4733                     //constraintoviewport:false,
4734                     //fixedcenter:true,
4735                     //collapsible : false,
4736                     //shim:true,
4737                     //modal: true,
4738                 //    width: 'auto',
4739                   //  height:100,
4740                     //buttonAlign:"center",
4741                     closeClick : function(){
4742                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4743                             handleButton("no");
4744                         }else{
4745                             handleButton("cancel");
4746                         }
4747                     }
4748                 });
4749                 dlg.render();
4750                 dlg.on("hide", handleHide);
4751                 mask = dlg.mask;
4752                 //dlg.addKeyListener(27, handleEsc);
4753                 buttons = {};
4754                 this.buttons = buttons;
4755                 var bt = this.buttonText;
4756                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4757                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4758                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4759                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4760                 //Roo.log(buttons);
4761                 bodyEl = dlg.bodyEl.createChild({
4762
4763                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4764                         '<textarea class="roo-mb-textarea"></textarea>' +
4765                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4766                 });
4767                 msgEl = bodyEl.dom.firstChild;
4768                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4769                 textboxEl.enableDisplayMode();
4770                 textboxEl.addKeyListener([10,13], function(){
4771                     if(dlg.isVisible() && opt && opt.buttons){
4772                         if(opt.buttons.ok){
4773                             handleButton("ok");
4774                         }else if(opt.buttons.yes){
4775                             handleButton("yes");
4776                         }
4777                     }
4778                 });
4779                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4780                 textareaEl.enableDisplayMode();
4781                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4782                 progressEl.enableDisplayMode();
4783                 
4784                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4785                 var pf = progressEl.dom.firstChild;
4786                 if (pf) {
4787                     pp = Roo.get(pf.firstChild);
4788                     pp.setHeight(pf.offsetHeight);
4789                 }
4790                 
4791             }
4792             return dlg;
4793         },
4794
4795         /**
4796          * Updates the message box body text
4797          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4798          * the XHTML-compliant non-breaking space character '&amp;#160;')
4799          * @return {Roo.MessageBox} This message box
4800          */
4801         updateText : function(text)
4802         {
4803             if(!dlg.isVisible() && !opt.width){
4804                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4805                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4806             }
4807             msgEl.innerHTML = text || '&#160;';
4808       
4809             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4810             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4811             var w = Math.max(
4812                     Math.min(opt.width || cw , this.maxWidth), 
4813                     Math.max(opt.minWidth || this.minWidth, bwidth)
4814             );
4815             if(opt.prompt){
4816                 activeTextEl.setWidth(w);
4817             }
4818             if(dlg.isVisible()){
4819                 dlg.fixedcenter = false;
4820             }
4821             // to big, make it scroll. = But as usual stupid IE does not support
4822             // !important..
4823             
4824             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4825                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4826                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4827             } else {
4828                 bodyEl.dom.style.height = '';
4829                 bodyEl.dom.style.overflowY = '';
4830             }
4831             if (cw > w) {
4832                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4833             } else {
4834                 bodyEl.dom.style.overflowX = '';
4835             }
4836             
4837             dlg.setContentSize(w, bodyEl.getHeight());
4838             if(dlg.isVisible()){
4839                 dlg.fixedcenter = true;
4840             }
4841             return this;
4842         },
4843
4844         /**
4845          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4846          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4847          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4848          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4849          * @return {Roo.MessageBox} This message box
4850          */
4851         updateProgress : function(value, text){
4852             if(text){
4853                 this.updateText(text);
4854             }
4855             
4856             if (pp) { // weird bug on my firefox - for some reason this is not defined
4857                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4858                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4859             }
4860             return this;
4861         },        
4862
4863         /**
4864          * Returns true if the message box is currently displayed
4865          * @return {Boolean} True if the message box is visible, else false
4866          */
4867         isVisible : function(){
4868             return dlg && dlg.isVisible();  
4869         },
4870
4871         /**
4872          * Hides the message box if it is displayed
4873          */
4874         hide : function(){
4875             if(this.isVisible()){
4876                 dlg.hide();
4877             }  
4878         },
4879
4880         /**
4881          * Displays a new message box, or reinitializes an existing message box, based on the config options
4882          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4883          * The following config object properties are supported:
4884          * <pre>
4885 Property    Type             Description
4886 ----------  ---------------  ------------------------------------------------------------------------------------
4887 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4888                                    closes (defaults to undefined)
4889 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4890                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4891 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4892                                    progress and wait dialogs will ignore this property and always hide the
4893                                    close button as they can only be closed programmatically.
4894 cls               String           A custom CSS class to apply to the message box element
4895 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4896                                    displayed (defaults to 75)
4897 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4898                                    function will be btn (the name of the button that was clicked, if applicable,
4899                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4900                                    Progress and wait dialogs will ignore this option since they do not respond to
4901                                    user actions and can only be closed programmatically, so any required function
4902                                    should be called by the same code after it closes the dialog.
4903 icon              String           A CSS class that provides a background image to be used as an icon for
4904                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4905 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4906 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4907 modal             Boolean          False to allow user interaction with the page while the message box is
4908                                    displayed (defaults to true)
4909 msg               String           A string that will replace the existing message box body text (defaults
4910                                    to the XHTML-compliant non-breaking space character '&#160;')
4911 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4912 progress          Boolean          True to display a progress bar (defaults to false)
4913 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4914 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4915 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4916 title             String           The title text
4917 value             String           The string value to set into the active textbox element if displayed
4918 wait              Boolean          True to display a progress bar (defaults to false)
4919 width             Number           The width of the dialog in pixels
4920 </pre>
4921          *
4922          * Example usage:
4923          * <pre><code>
4924 Roo.Msg.show({
4925    title: 'Address',
4926    msg: 'Please enter your address:',
4927    width: 300,
4928    buttons: Roo.MessageBox.OKCANCEL,
4929    multiline: true,
4930    fn: saveAddress,
4931    animEl: 'addAddressBtn'
4932 });
4933 </code></pre>
4934          * @param {Object} config Configuration options
4935          * @return {Roo.MessageBox} This message box
4936          */
4937         show : function(options)
4938         {
4939             
4940             // this causes nightmares if you show one dialog after another
4941             // especially on callbacks..
4942              
4943             if(this.isVisible()){
4944                 
4945                 this.hide();
4946                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4947                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4948                 Roo.log("New Dialog Message:" +  options.msg )
4949                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4950                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4951                 
4952             }
4953             var d = this.getDialog();
4954             opt = options;
4955             d.setTitle(opt.title || "&#160;");
4956             d.closeEl.setDisplayed(opt.closable !== false);
4957             activeTextEl = textboxEl;
4958             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4959             if(opt.prompt){
4960                 if(opt.multiline){
4961                     textboxEl.hide();
4962                     textareaEl.show();
4963                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4964                         opt.multiline : this.defaultTextHeight);
4965                     activeTextEl = textareaEl;
4966                 }else{
4967                     textboxEl.show();
4968                     textareaEl.hide();
4969                 }
4970             }else{
4971                 textboxEl.hide();
4972                 textareaEl.hide();
4973             }
4974             progressEl.setDisplayed(opt.progress === true);
4975             if (opt.progress) {
4976                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4977             }
4978             this.updateProgress(0);
4979             activeTextEl.dom.value = opt.value || "";
4980             if(opt.prompt){
4981                 dlg.setDefaultButton(activeTextEl);
4982             }else{
4983                 var bs = opt.buttons;
4984                 var db = null;
4985                 if(bs && bs.ok){
4986                     db = buttons["ok"];
4987                 }else if(bs && bs.yes){
4988                     db = buttons["yes"];
4989                 }
4990                 dlg.setDefaultButton(db);
4991             }
4992             bwidth = updateButtons(opt.buttons);
4993             this.updateText(opt.msg);
4994             if(opt.cls){
4995                 d.el.addClass(opt.cls);
4996             }
4997             d.proxyDrag = opt.proxyDrag === true;
4998             d.modal = opt.modal !== false;
4999             d.mask = opt.modal !== false ? mask : false;
5000             if(!d.isVisible()){
5001                 // force it to the end of the z-index stack so it gets a cursor in FF
5002                 document.body.appendChild(dlg.el.dom);
5003                 d.animateTarget = null;
5004                 d.show(options.animEl);
5005             }
5006             return this;
5007         },
5008
5009         /**
5010          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5011          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5012          * and closing the message box when the process is complete.
5013          * @param {String} title The title bar text
5014          * @param {String} msg The message box body text
5015          * @return {Roo.MessageBox} This message box
5016          */
5017         progress : function(title, msg){
5018             this.show({
5019                 title : title,
5020                 msg : msg,
5021                 buttons: false,
5022                 progress:true,
5023                 closable:false,
5024                 minWidth: this.minProgressWidth,
5025                 modal : true
5026             });
5027             return this;
5028         },
5029
5030         /**
5031          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5032          * If a callback function is passed it will be called after the user clicks the button, and the
5033          * id of the button that was clicked will be passed as the only parameter to the callback
5034          * (could also be the top-right close button).
5035          * @param {String} title The title bar text
5036          * @param {String} msg The message box body text
5037          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5038          * @param {Object} scope (optional) The scope of the callback function
5039          * @return {Roo.MessageBox} This message box
5040          */
5041         alert : function(title, msg, fn, scope)
5042         {
5043             this.show({
5044                 title : title,
5045                 msg : msg,
5046                 buttons: this.OK,
5047                 fn: fn,
5048                 closable : false,
5049                 scope : scope,
5050                 modal : true
5051             });
5052             return this;
5053         },
5054
5055         /**
5056          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5057          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5058          * You are responsible for closing the message box when the process is complete.
5059          * @param {String} msg The message box body text
5060          * @param {String} title (optional) The title bar text
5061          * @return {Roo.MessageBox} This message box
5062          */
5063         wait : function(msg, title){
5064             this.show({
5065                 title : title,
5066                 msg : msg,
5067                 buttons: false,
5068                 closable:false,
5069                 progress:true,
5070                 modal:true,
5071                 width:300,
5072                 wait:true
5073             });
5074             waitTimer = Roo.TaskMgr.start({
5075                 run: function(i){
5076                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5077                 },
5078                 interval: 1000
5079             });
5080             return this;
5081         },
5082
5083         /**
5084          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5085          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5086          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5087          * @param {String} title The title bar text
5088          * @param {String} msg The message box body text
5089          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5090          * @param {Object} scope (optional) The scope of the callback function
5091          * @return {Roo.MessageBox} This message box
5092          */
5093         confirm : function(title, msg, fn, scope){
5094             this.show({
5095                 title : title,
5096                 msg : msg,
5097                 buttons: this.YESNO,
5098                 fn: fn,
5099                 scope : scope,
5100                 modal : true
5101             });
5102             return this;
5103         },
5104
5105         /**
5106          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5107          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5108          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5109          * (could also be the top-right close button) and the text that was entered will be passed as the two
5110          * parameters to the callback.
5111          * @param {String} title The title bar text
5112          * @param {String} msg The message box body text
5113          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114          * @param {Object} scope (optional) The scope of the callback function
5115          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5116          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5117          * @return {Roo.MessageBox} This message box
5118          */
5119         prompt : function(title, msg, fn, scope, multiline){
5120             this.show({
5121                 title : title,
5122                 msg : msg,
5123                 buttons: this.OKCANCEL,
5124                 fn: fn,
5125                 minWidth:250,
5126                 scope : scope,
5127                 prompt:true,
5128                 multiline: multiline,
5129                 modal : true
5130             });
5131             return this;
5132         },
5133
5134         /**
5135          * Button config that displays a single OK button
5136          * @type Object
5137          */
5138         OK : {ok:true},
5139         /**
5140          * Button config that displays Yes and No buttons
5141          * @type Object
5142          */
5143         YESNO : {yes:true, no:true},
5144         /**
5145          * Button config that displays OK and Cancel buttons
5146          * @type Object
5147          */
5148         OKCANCEL : {ok:true, cancel:true},
5149         /**
5150          * Button config that displays Yes, No and Cancel buttons
5151          * @type Object
5152          */
5153         YESNOCANCEL : {yes:true, no:true, cancel:true},
5154
5155         /**
5156          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5157          * @type Number
5158          */
5159         defaultTextHeight : 75,
5160         /**
5161          * The maximum width in pixels of the message box (defaults to 600)
5162          * @type Number
5163          */
5164         maxWidth : 600,
5165         /**
5166          * The minimum width in pixels of the message box (defaults to 100)
5167          * @type Number
5168          */
5169         minWidth : 100,
5170         /**
5171          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5172          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5173          * @type Number
5174          */
5175         minProgressWidth : 250,
5176         /**
5177          * An object containing the default button text strings that can be overriden for localized language support.
5178          * Supported properties are: ok, cancel, yes and no.
5179          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5180          * @type Object
5181          */
5182         buttonText : {
5183             ok : "OK",
5184             cancel : "Cancel",
5185             yes : "Yes",
5186             no : "No"
5187         }
5188     };
5189 }();
5190
5191 /**
5192  * Shorthand for {@link Roo.MessageBox}
5193  */
5194 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5195 Roo.Msg = Roo.Msg || Roo.MessageBox;
5196 /*
5197  * - LGPL
5198  *
5199  * navbar
5200  * 
5201  */
5202
5203 /**
5204  * @class Roo.bootstrap.Navbar
5205  * @extends Roo.bootstrap.Component
5206  * Bootstrap Navbar class
5207
5208  * @constructor
5209  * Create a new Navbar
5210  * @param {Object} config The config object
5211  */
5212
5213
5214 Roo.bootstrap.Navbar = function(config){
5215     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5216     this.addEvents({
5217         // raw events
5218         /**
5219          * @event beforetoggle
5220          * Fire before toggle the menu
5221          * @param {Roo.EventObject} e
5222          */
5223         "beforetoggle" : true
5224     });
5225 };
5226
5227 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5228     
5229     
5230    
5231     // private
5232     navItems : false,
5233     loadMask : false,
5234     
5235     
5236     getAutoCreate : function(){
5237         
5238         
5239         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5240         
5241     },
5242     
5243     initEvents :function ()
5244     {
5245         //Roo.log(this.el.select('.navbar-toggle',true));
5246         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5247         
5248         var mark = {
5249             tag: "div",
5250             cls:"x-dlg-mask"
5251         };
5252         
5253         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5254         
5255         var size = this.el.getSize();
5256         this.maskEl.setSize(size.width, size.height);
5257         this.maskEl.enableDisplayMode("block");
5258         this.maskEl.hide();
5259         
5260         if(this.loadMask){
5261             this.maskEl.show();
5262         }
5263     },
5264     
5265     
5266     getChildContainer : function()
5267     {
5268         if (this.el && this.el.select('.collapse').getCount()) {
5269             return this.el.select('.collapse',true).first();
5270         }
5271         
5272         return this.el;
5273     },
5274     
5275     mask : function()
5276     {
5277         this.maskEl.show();
5278     },
5279     
5280     unmask : function()
5281     {
5282         this.maskEl.hide();
5283     },
5284     onToggle : function()
5285     {
5286         
5287         if(this.fireEvent('beforetoggle', this) === false){
5288             return;
5289         }
5290         var ce = this.el.select('.navbar-collapse',true).first();
5291       
5292         if (!ce.hasClass('show')) {
5293            this.expand();
5294         } else {
5295             this.collapse();
5296         }
5297         
5298         
5299     
5300     },
5301     /**
5302      * Expand the navbar pulldown 
5303      */
5304     expand : function ()
5305     {
5306        
5307         var ce = this.el.select('.navbar-collapse',true).first();
5308         if (ce.hasClass('collapsing')) {
5309             return;
5310         }
5311         ce.dom.style.height = '';
5312                // show it...
5313         ce.addClass('in'); // old...
5314         ce.removeClass('collapse');
5315         ce.addClass('show');
5316         var h = ce.getHeight();
5317         Roo.log(h);
5318         ce.removeClass('show');
5319         // at this point we should be able to see it..
5320         ce.addClass('collapsing');
5321         
5322         ce.setHeight(0); // resize it ...
5323         ce.on('transitionend', function() {
5324             //Roo.log('done transition');
5325             ce.removeClass('collapsing');
5326             ce.addClass('show');
5327             ce.removeClass('collapse');
5328
5329             ce.dom.style.height = '';
5330         }, this, { single: true} );
5331         ce.setHeight(h);
5332         ce.dom.scrollTop = 0;
5333     },
5334     /**
5335      * Collapse the navbar pulldown 
5336      */
5337     collapse : function()
5338     {
5339          var ce = this.el.select('.navbar-collapse',true).first();
5340        
5341         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5342             // it's collapsed or collapsing..
5343             return;
5344         }
5345         ce.removeClass('in'); // old...
5346         ce.setHeight(ce.getHeight());
5347         ce.removeClass('show');
5348         ce.addClass('collapsing');
5349         
5350         ce.on('transitionend', function() {
5351             ce.dom.style.height = '';
5352             ce.removeClass('collapsing');
5353             ce.addClass('collapse');
5354         }, this, { single: true} );
5355         ce.setHeight(0);
5356     }
5357     
5358     
5359     
5360 });
5361
5362
5363
5364  
5365
5366  /*
5367  * - LGPL
5368  *
5369  * navbar
5370  * 
5371  */
5372
5373 /**
5374  * @class Roo.bootstrap.NavSimplebar
5375  * @extends Roo.bootstrap.Navbar
5376  * Bootstrap Sidebar class
5377  *
5378  * @cfg {Boolean} inverse is inverted color
5379  * 
5380  * @cfg {String} type (nav | pills | tabs)
5381  * @cfg {Boolean} arrangement stacked | justified
5382  * @cfg {String} align (left | right) alignment
5383  * 
5384  * @cfg {Boolean} main (true|false) main nav bar? default false
5385  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5386  * 
5387  * @cfg {String} tag (header|footer|nav|div) default is nav 
5388
5389  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5390  * 
5391  * 
5392  * @constructor
5393  * Create a new Sidebar
5394  * @param {Object} config The config object
5395  */
5396
5397
5398 Roo.bootstrap.NavSimplebar = function(config){
5399     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5400 };
5401
5402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5403     
5404     inverse: false,
5405     
5406     type: false,
5407     arrangement: '',
5408     align : false,
5409     
5410     weight : 'light',
5411     
5412     main : false,
5413     
5414     
5415     tag : false,
5416     
5417     
5418     getAutoCreate : function(){
5419         
5420         
5421         var cfg = {
5422             tag : this.tag || 'div',
5423             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5424         };
5425         if (['light','white'].indexOf(this.weight) > -1) {
5426             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5427         }
5428         cfg.cls += ' bg-' + this.weight;
5429         
5430         if (this.inverse) {
5431             cfg.cls += ' navbar-inverse';
5432             
5433         }
5434         
5435         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5436         
5437         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5438             return cfg;
5439         }
5440         
5441         
5442     
5443         
5444         cfg.cn = [
5445             {
5446                 cls: 'nav nav-' + this.xtype,
5447                 tag : 'ul'
5448             }
5449         ];
5450         
5451          
5452         this.type = this.type || 'nav';
5453         if (['tabs','pills'].indexOf(this.type) != -1) {
5454             cfg.cn[0].cls += ' nav-' + this.type
5455         
5456         
5457         } else {
5458             if (this.type!=='nav') {
5459                 Roo.log('nav type must be nav/tabs/pills')
5460             }
5461             cfg.cn[0].cls += ' navbar-nav'
5462         }
5463         
5464         
5465         
5466         
5467         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5468             cfg.cn[0].cls += ' nav-' + this.arrangement;
5469         }
5470         
5471         
5472         if (this.align === 'right') {
5473             cfg.cn[0].cls += ' navbar-right';
5474         }
5475         
5476         
5477         
5478         
5479         return cfg;
5480     
5481         
5482     }
5483     
5484     
5485     
5486 });
5487
5488
5489
5490  
5491
5492  
5493        /*
5494  * - LGPL
5495  *
5496  * navbar
5497  * navbar-fixed-top
5498  * navbar-expand-md  fixed-top 
5499  */
5500
5501 /**
5502  * @class Roo.bootstrap.NavHeaderbar
5503  * @extends Roo.bootstrap.NavSimplebar
5504  * Bootstrap Sidebar class
5505  *
5506  * @cfg {String} brand what is brand
5507  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5508  * @cfg {String} brand_href href of the brand
5509  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5510  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5511  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5512  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5513  * 
5514  * @constructor
5515  * Create a new Sidebar
5516  * @param {Object} config The config object
5517  */
5518
5519
5520 Roo.bootstrap.NavHeaderbar = function(config){
5521     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5522       
5523 };
5524
5525 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5526     
5527     position: '',
5528     brand: '',
5529     brand_href: false,
5530     srButton : true,
5531     autohide : false,
5532     desktopCenter : false,
5533    
5534     
5535     getAutoCreate : function(){
5536         
5537         var   cfg = {
5538             tag: this.nav || 'nav',
5539             cls: 'navbar navbar-expand-md',
5540             role: 'navigation',
5541             cn: []
5542         };
5543         
5544         var cn = cfg.cn;
5545         if (this.desktopCenter) {
5546             cn.push({cls : 'container', cn : []});
5547             cn = cn[0].cn;
5548         }
5549         
5550         if(this.srButton){
5551             var btn = {
5552                 tag: 'button',
5553                 type: 'button',
5554                 cls: 'navbar-toggle navbar-toggler',
5555                 'data-toggle': 'collapse',
5556                 cn: [
5557                     {
5558                         tag: 'span',
5559                         cls: 'sr-only',
5560                         html: 'Toggle navigation'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar navbar-toggler-icon'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     },
5570                     {
5571                         tag: 'span',
5572                         cls: 'icon-bar'
5573                     }
5574                 ]
5575             };
5576             
5577             cn.push( Roo.bootstrap.version == 4 ? btn : {
5578                 tag: 'div',
5579                 cls: 'navbar-header',
5580                 cn: [
5581                     btn
5582                 ]
5583             });
5584         }
5585         
5586         cn.push({
5587             tag: 'div',
5588             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5589             cn : []
5590         });
5591         
5592         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5593         
5594         if (['light','white'].indexOf(this.weight) > -1) {
5595             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5596         }
5597         cfg.cls += ' bg-' + this.weight;
5598         
5599         
5600         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5601             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5602             
5603             // tag can override this..
5604             
5605             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5606         }
5607         
5608         if (this.brand !== '') {
5609             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5610             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5611                 tag: 'a',
5612                 href: this.brand_href ? this.brand_href : '#',
5613                 cls: 'navbar-brand',
5614                 cn: [
5615                 this.brand
5616                 ]
5617             });
5618         }
5619         
5620         if(this.main){
5621             cfg.cls += ' main-nav';
5622         }
5623         
5624         
5625         return cfg;
5626
5627         
5628     },
5629     getHeaderChildContainer : function()
5630     {
5631         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5632             return this.el.select('.navbar-header',true).first();
5633         }
5634         
5635         return this.getChildContainer();
5636     },
5637     
5638     getChildContainer : function()
5639     {
5640          
5641         return this.el.select('.roo-navbar-collapse',true).first();
5642          
5643         
5644     },
5645     
5646     initEvents : function()
5647     {
5648         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5649         
5650         if (this.autohide) {
5651             
5652             var prevScroll = 0;
5653             var ft = this.el;
5654             
5655             Roo.get(document).on('scroll',function(e) {
5656                 var ns = Roo.get(document).getScroll().top;
5657                 var os = prevScroll;
5658                 prevScroll = ns;
5659                 
5660                 if(ns > os){
5661                     ft.removeClass('slideDown');
5662                     ft.addClass('slideUp');
5663                     return;
5664                 }
5665                 ft.removeClass('slideUp');
5666                 ft.addClass('slideDown');
5667                  
5668               
5669           },this);
5670         }
5671     }    
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSidebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  * 
5691  * @constructor
5692  * Create a new Sidebar
5693  * @param {Object} config The config object
5694  */
5695
5696
5697 Roo.bootstrap.NavSidebar = function(config){
5698     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5699 };
5700
5701 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5702     
5703     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5704     
5705     getAutoCreate : function(){
5706         
5707         
5708         return  {
5709             tag: 'div',
5710             cls: 'sidebar sidebar-nav'
5711         };
5712     
5713         
5714     }
5715     
5716     
5717     
5718 });
5719
5720
5721
5722  
5723
5724  /*
5725  * - LGPL
5726  *
5727  * nav group
5728  * 
5729  */
5730
5731 /**
5732  * @class Roo.bootstrap.NavGroup
5733  * @extends Roo.bootstrap.Component
5734  * Bootstrap NavGroup class
5735  * @cfg {String} align (left|right)
5736  * @cfg {Boolean} inverse
5737  * @cfg {String} type (nav|pills|tab) default nav
5738  * @cfg {String} navId - reference Id for navbar.
5739  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5740  * 
5741  * @constructor
5742  * Create a new nav group
5743  * @param {Object} config The config object
5744  */
5745
5746 Roo.bootstrap.NavGroup = function(config){
5747     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5748     this.navItems = [];
5749    
5750     Roo.bootstrap.NavGroup.register(this);
5751      this.addEvents({
5752         /**
5753              * @event changed
5754              * Fires when the active item changes
5755              * @param {Roo.bootstrap.NavGroup} this
5756              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5757              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5758          */
5759         'changed': true
5760      });
5761     
5762 };
5763
5764 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5765     
5766     align: '',
5767     inverse: false,
5768     form: false,
5769     type: 'nav',
5770     navId : '',
5771     // private
5772     pilltype : true,
5773     
5774     navItems : false, 
5775     
5776     getAutoCreate : function()
5777     {
5778         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5779         
5780         cfg = {
5781             tag : 'ul',
5782             cls: 'nav' 
5783         };
5784         if (Roo.bootstrap.version == 4) {
5785             if (['tabs','pills'].indexOf(this.type) != -1) {
5786                 cfg.cls += ' nav-' + this.type; 
5787             } else {
5788                 // trying to remove so header bar can right align top?
5789                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5790                     // do not use on header bar... 
5791                     cfg.cls += ' navbar-nav';
5792                 }
5793             }
5794             
5795         } else {
5796             if (['tabs','pills'].indexOf(this.type) != -1) {
5797                 cfg.cls += ' nav-' + this.type
5798             } else {
5799                 if (this.type !== 'nav') {
5800                     Roo.log('nav type must be nav/tabs/pills')
5801                 }
5802                 cfg.cls += ' navbar-nav'
5803             }
5804         }
5805         
5806         if (this.parent() && this.parent().sidebar) {
5807             cfg = {
5808                 tag: 'ul',
5809                 cls: 'dashboard-menu sidebar-menu'
5810             };
5811             
5812             return cfg;
5813         }
5814         
5815         if (this.form === true) {
5816             cfg = {
5817                 tag: 'form',
5818                 cls: 'navbar-form form-inline'
5819             };
5820             //nav navbar-right ml-md-auto
5821             if (this.align === 'right') {
5822                 cfg.cls += ' navbar-right ml-md-auto';
5823             } else {
5824                 cfg.cls += ' navbar-left';
5825             }
5826         }
5827         
5828         if (this.align === 'right') {
5829             cfg.cls += ' navbar-right ml-md-auto';
5830         } else {
5831             cfg.cls += ' mr-auto';
5832         }
5833         
5834         if (this.inverse) {
5835             cfg.cls += ' navbar-inverse';
5836             
5837         }
5838         
5839         
5840         return cfg;
5841     },
5842     /**
5843     * sets the active Navigation item
5844     * @param {Roo.bootstrap.NavItem} the new current navitem
5845     */
5846     setActiveItem : function(item)
5847     {
5848         var prev = false;
5849         Roo.each(this.navItems, function(v){
5850             if (v == item) {
5851                 return ;
5852             }
5853             if (v.isActive()) {
5854                 v.setActive(false, true);
5855                 prev = v;
5856                 
5857             }
5858             
5859         });
5860
5861         item.setActive(true, true);
5862         this.fireEvent('changed', this, item, prev);
5863         
5864         
5865     },
5866     /**
5867     * gets the active Navigation item
5868     * @return {Roo.bootstrap.NavItem} the current navitem
5869     */
5870     getActive : function()
5871     {
5872         
5873         var prev = false;
5874         Roo.each(this.navItems, function(v){
5875             
5876             if (v.isActive()) {
5877                 prev = v;
5878                 
5879             }
5880             
5881         });
5882         return prev;
5883     },
5884     
5885     indexOfNav : function()
5886     {
5887         
5888         var prev = false;
5889         Roo.each(this.navItems, function(v,i){
5890             
5891             if (v.isActive()) {
5892                 prev = i;
5893                 
5894             }
5895             
5896         });
5897         return prev;
5898     },
5899     /**
5900     * adds a Navigation item
5901     * @param {Roo.bootstrap.NavItem} the navitem to add
5902     */
5903     addItem : function(cfg)
5904     {
5905         if (this.form && Roo.bootstrap.version == 4) {
5906             cfg.tag = 'div';
5907         }
5908         var cn = new Roo.bootstrap.NavItem(cfg);
5909         this.register(cn);
5910         cn.parentId = this.id;
5911         cn.onRender(this.el, null);
5912         return cn;
5913     },
5914     /**
5915     * register a Navigation item
5916     * @param {Roo.bootstrap.NavItem} the navitem to add
5917     */
5918     register : function(item)
5919     {
5920         this.navItems.push( item);
5921         item.navId = this.navId;
5922     
5923     },
5924     
5925     /**
5926     * clear all the Navigation item
5927     */
5928    
5929     clearAll : function()
5930     {
5931         this.navItems = [];
5932         this.el.dom.innerHTML = '';
5933     },
5934     
5935     getNavItem: function(tabId)
5936     {
5937         var ret = false;
5938         Roo.each(this.navItems, function(e) {
5939             if (e.tabId == tabId) {
5940                ret =  e;
5941                return false;
5942             }
5943             return true;
5944             
5945         });
5946         return ret;
5947     },
5948     
5949     setActiveNext : function()
5950     {
5951         var i = this.indexOfNav(this.getActive());
5952         if (i > this.navItems.length) {
5953             return;
5954         }
5955         this.setActiveItem(this.navItems[i+1]);
5956     },
5957     setActivePrev : function()
5958     {
5959         var i = this.indexOfNav(this.getActive());
5960         if (i  < 1) {
5961             return;
5962         }
5963         this.setActiveItem(this.navItems[i-1]);
5964     },
5965     clearWasActive : function(except) {
5966         Roo.each(this.navItems, function(e) {
5967             if (e.tabId != except.tabId && e.was_active) {
5968                e.was_active = false;
5969                return false;
5970             }
5971             return true;
5972             
5973         });
5974     },
5975     getWasActive : function ()
5976     {
5977         var r = false;
5978         Roo.each(this.navItems, function(e) {
5979             if (e.was_active) {
5980                r = e;
5981                return false;
5982             }
5983             return true;
5984             
5985         });
5986         return r;
5987     }
5988     
5989     
5990 });
5991
5992  
5993 Roo.apply(Roo.bootstrap.NavGroup, {
5994     
5995     groups: {},
5996      /**
5997     * register a Navigation Group
5998     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5999     */
6000     register : function(navgrp)
6001     {
6002         this.groups[navgrp.navId] = navgrp;
6003         
6004     },
6005     /**
6006     * fetch a Navigation Group based on the navigation ID
6007     * @param {string} the navgroup to add
6008     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6009     */
6010     get: function(navId) {
6011         if (typeof(this.groups[navId]) == 'undefined') {
6012             return false;
6013             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6014         }
6015         return this.groups[navId] ;
6016     }
6017     
6018     
6019     
6020 });
6021
6022  /*
6023  * - LGPL
6024  *
6025  * row
6026  * 
6027  */
6028
6029 /**
6030  * @class Roo.bootstrap.NavItem
6031  * @extends Roo.bootstrap.Component
6032  * Bootstrap Navbar.NavItem class
6033  * @cfg {String} href  link to
6034  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6035  * @cfg {Boolean} button_outline show and outlined button
6036  * @cfg {String} html content of button
6037  * @cfg {String} badge text inside badge
6038  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6039  * @cfg {String} glyphicon DEPRICATED - use fa
6040  * @cfg {String} icon DEPRICATED - use fa
6041  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6042  * @cfg {Boolean} active Is item active
6043  * @cfg {Boolean} disabled Is item disabled
6044  * @cfg {String} linkcls  Link Class
6045  * @cfg {Boolean} preventDefault (true | false) default false
6046  * @cfg {String} tabId the tab that this item activates.
6047  * @cfg {String} tagtype (a|span) render as a href or span?
6048  * @cfg {Boolean} animateRef (true|false) link to element default false  
6049   
6050  * @constructor
6051  * Create a new Navbar Item
6052  * @param {Object} config The config object
6053  */
6054 Roo.bootstrap.NavItem = function(config){
6055     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6056     this.addEvents({
6057         // raw events
6058         /**
6059          * @event click
6060          * The raw click event for the entire grid.
6061          * @param {Roo.EventObject} e
6062          */
6063         "click" : true,
6064          /**
6065             * @event changed
6066             * Fires when the active item active state changes
6067             * @param {Roo.bootstrap.NavItem} this
6068             * @param {boolean} state the new state
6069              
6070          */
6071         'changed': true,
6072         /**
6073             * @event scrollto
6074             * Fires when scroll to element
6075             * @param {Roo.bootstrap.NavItem} this
6076             * @param {Object} options
6077             * @param {Roo.EventObject} e
6078              
6079          */
6080         'scrollto': true
6081     });
6082    
6083 };
6084
6085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6086     
6087     href: false,
6088     html: '',
6089     badge: '',
6090     icon: false,
6091     fa : false,
6092     glyphicon: false,
6093     active: false,
6094     preventDefault : false,
6095     tabId : false,
6096     tagtype : 'a',
6097     tag: 'li',
6098     disabled : false,
6099     animateRef : false,
6100     was_active : false,
6101     button_weight : '',
6102     button_outline : false,
6103     linkcls : '',
6104     navLink: false,
6105     
6106     getAutoCreate : function(){
6107          
6108         var cfg = {
6109             tag: this.tag,
6110             cls: 'nav-item'
6111         };
6112         
6113         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6114         
6115         if (this.active) {
6116             cfg.cls +=  ' active' ;
6117         }
6118         if (this.disabled) {
6119             cfg.cls += ' disabled';
6120         }
6121         
6122         // BS4 only?
6123         if (this.button_weight.length) {
6124             cfg.tag = this.href ? 'a' : 'button';
6125             cfg.html = this.html || '';
6126             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6127             if (this.href) {
6128                 cfg.href = this.href;
6129             }
6130             if (this.fa) {
6131                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6132             }
6133             
6134             // menu .. should add dropdown-menu class - so no need for carat..
6135             
6136             if (this.badge !== '') {
6137                  
6138                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6139             }
6140             return cfg;
6141         }
6142         
6143         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6144             cfg.cn = [
6145                 {
6146                     tag: this.tagtype,
6147                     href : this.href || "#",
6148                     html: this.html || ''
6149                 }
6150             ];
6151             if (this.tagtype == 'a') {
6152                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6153         
6154             }
6155             if (this.icon) {
6156                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if (this.fa) {
6159                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6160             }
6161             if(this.glyphicon) {
6162                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6163             }
6164             
6165             if (this.menu) {
6166                 
6167                 cfg.cn[0].html += " <span class='caret'></span>";
6168              
6169             }
6170             
6171             if (this.badge !== '') {
6172                  
6173                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6174             }
6175         }
6176         
6177         
6178         
6179         return cfg;
6180     },
6181     onRender : function(ct, position)
6182     {
6183        // Roo.log("Call onRender: " + this.xtype);
6184         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6185             this.tag = 'div';
6186         }
6187         
6188         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6189         this.navLink = this.el.select('.nav-link',true).first();
6190         return ret;
6191     },
6192       
6193     
6194     initEvents: function() 
6195     {
6196         if (typeof (this.menu) != 'undefined') {
6197             this.menu.parentType = this.xtype;
6198             this.menu.triggerEl = this.el;
6199             this.menu = this.addxtype(Roo.apply({}, this.menu));
6200         }
6201         
6202         this.el.on('click', this.onClick, this);
6203         
6204         //if(this.tagtype == 'span'){
6205         //    this.el.select('span',true).on('click', this.onClick, this);
6206         //}
6207        
6208         // at this point parent should be available..
6209         this.parent().register(this);
6210     },
6211     
6212     onClick : function(e)
6213     {
6214         if (e.getTarget('.dropdown-menu-item')) {
6215             // did you click on a menu itemm.... - then don't trigger onclick..
6216             return;
6217         }
6218         
6219         if(
6220                 this.preventDefault || 
6221                 this.href == '#' 
6222         ){
6223             Roo.log("NavItem - prevent Default?");
6224             e.preventDefault();
6225         }
6226         
6227         if (this.disabled) {
6228             return;
6229         }
6230         
6231         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6232         if (tg && tg.transition) {
6233             Roo.log("waiting for the transitionend");
6234             return;
6235         }
6236         
6237         
6238         
6239         //Roo.log("fire event clicked");
6240         if(this.fireEvent('click', this, e) === false){
6241             return;
6242         };
6243         
6244         if(this.tagtype == 'span'){
6245             return;
6246         }
6247         
6248         //Roo.log(this.href);
6249         var ael = this.el.select('a',true).first();
6250         //Roo.log(ael);
6251         
6252         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6253             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6254             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6255                 return; // ignore... - it's a 'hash' to another page.
6256             }
6257             Roo.log("NavItem - prevent Default?");
6258             e.preventDefault();
6259             this.scrollToElement(e);
6260         }
6261         
6262         
6263         var p =  this.parent();
6264    
6265         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6266             if (typeof(p.setActiveItem) !== 'undefined') {
6267                 p.setActiveItem(this);
6268             }
6269         }
6270         
6271         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6272         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6273             // remove the collapsed menu expand...
6274             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6275         }
6276     },
6277     
6278     isActive: function () {
6279         return this.active
6280     },
6281     setActive : function(state, fire, is_was_active)
6282     {
6283         if (this.active && !state && this.navId) {
6284             this.was_active = true;
6285             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6286             if (nv) {
6287                 nv.clearWasActive(this);
6288             }
6289             
6290         }
6291         this.active = state;
6292         
6293         if (!state ) {
6294             this.el.removeClass('active');
6295             this.navLink ? this.navLink.removeClass('active') : false;
6296         } else if (!this.el.hasClass('active')) {
6297             
6298             this.el.addClass('active');
6299             if (Roo.bootstrap.version == 4 && this.navLink ) {
6300                 this.navLink.addClass('active');
6301             }
6302             
6303         }
6304         if (fire) {
6305             this.fireEvent('changed', this, state);
6306         }
6307         
6308         // show a panel if it's registered and related..
6309         
6310         if (!this.navId || !this.tabId || !state || is_was_active) {
6311             return;
6312         }
6313         
6314         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6315         if (!tg) {
6316             return;
6317         }
6318         var pan = tg.getPanelByName(this.tabId);
6319         if (!pan) {
6320             return;
6321         }
6322         // if we can not flip to new panel - go back to old nav highlight..
6323         if (false == tg.showPanel(pan)) {
6324             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6325             if (nv) {
6326                 var onav = nv.getWasActive();
6327                 if (onav) {
6328                     onav.setActive(true, false, true);
6329                 }
6330             }
6331             
6332         }
6333         
6334         
6335         
6336     },
6337      // this should not be here...
6338     setDisabled : function(state)
6339     {
6340         this.disabled = state;
6341         if (!state ) {
6342             this.el.removeClass('disabled');
6343         } else if (!this.el.hasClass('disabled')) {
6344             this.el.addClass('disabled');
6345         }
6346         
6347     },
6348     
6349     /**
6350      * Fetch the element to display the tooltip on.
6351      * @return {Roo.Element} defaults to this.el
6352      */
6353     tooltipEl : function()
6354     {
6355         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6356     },
6357     
6358     scrollToElement : function(e)
6359     {
6360         var c = document.body;
6361         
6362         /*
6363          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6364          */
6365         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6366             c = document.documentElement;
6367         }
6368         
6369         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6370         
6371         if(!target){
6372             return;
6373         }
6374
6375         var o = target.calcOffsetsTo(c);
6376         
6377         var options = {
6378             target : target,
6379             value : o[1]
6380         };
6381         
6382         this.fireEvent('scrollto', this, options, e);
6383         
6384         Roo.get(c).scrollTo('top', options.value, true);
6385         
6386         return;
6387     }
6388 });
6389  
6390
6391  /*
6392  * - LGPL
6393  *
6394  * sidebar item
6395  *
6396  *  li
6397  *    <span> icon </span>
6398  *    <span> text </span>
6399  *    <span>badge </span>
6400  */
6401
6402 /**
6403  * @class Roo.bootstrap.NavSidebarItem
6404  * @extends Roo.bootstrap.NavItem
6405  * Bootstrap Navbar.NavSidebarItem class
6406  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6407  * {Boolean} open is the menu open
6408  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6409  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6410  * {String} buttonSize (sm|md|lg)the extra classes for the button
6411  * {Boolean} showArrow show arrow next to the text (default true)
6412  * @constructor
6413  * Create a new Navbar Button
6414  * @param {Object} config The config object
6415  */
6416 Roo.bootstrap.NavSidebarItem = function(config){
6417     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6418     this.addEvents({
6419         // raw events
6420         /**
6421          * @event click
6422          * The raw click event for the entire grid.
6423          * @param {Roo.EventObject} e
6424          */
6425         "click" : true,
6426          /**
6427             * @event changed
6428             * Fires when the active item active state changes
6429             * @param {Roo.bootstrap.NavSidebarItem} this
6430             * @param {boolean} state the new state
6431              
6432          */
6433         'changed': true
6434     });
6435    
6436 };
6437
6438 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6439     
6440     badgeWeight : 'default',
6441     
6442     open: false,
6443     
6444     buttonView : false,
6445     
6446     buttonWeight : 'default',
6447     
6448     buttonSize : 'md',
6449     
6450     showArrow : true,
6451     
6452     getAutoCreate : function(){
6453         
6454         
6455         var a = {
6456                 tag: 'a',
6457                 href : this.href || '#',
6458                 cls: '',
6459                 html : '',
6460                 cn : []
6461         };
6462         
6463         if(this.buttonView){
6464             a = {
6465                 tag: 'button',
6466                 href : this.href || '#',
6467                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6468                 html : this.html,
6469                 cn : []
6470             };
6471         }
6472         
6473         var cfg = {
6474             tag: 'li',
6475             cls: '',
6476             cn: [ a ]
6477         };
6478         
6479         if (this.active) {
6480             cfg.cls += ' active';
6481         }
6482         
6483         if (this.disabled) {
6484             cfg.cls += ' disabled';
6485         }
6486         if (this.open) {
6487             cfg.cls += ' open x-open';
6488         }
6489         // left icon..
6490         if (this.glyphicon || this.icon) {
6491             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6492             a.cn.push({ tag : 'i', cls : c }) ;
6493         }
6494         
6495         if(!this.buttonView){
6496             var span = {
6497                 tag: 'span',
6498                 html : this.html || ''
6499             };
6500
6501             a.cn.push(span);
6502             
6503         }
6504         
6505         if (this.badge !== '') {
6506             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6507         }
6508         
6509         if (this.menu) {
6510             
6511             if(this.showArrow){
6512                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6513             }
6514             
6515             a.cls += ' dropdown-toggle treeview' ;
6516         }
6517         
6518         return cfg;
6519     },
6520     
6521     initEvents : function()
6522     { 
6523         if (typeof (this.menu) != 'undefined') {
6524             this.menu.parentType = this.xtype;
6525             this.menu.triggerEl = this.el;
6526             this.menu = this.addxtype(Roo.apply({}, this.menu));
6527         }
6528         
6529         this.el.on('click', this.onClick, this);
6530         
6531         if(this.badge !== ''){
6532             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6533         }
6534         
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if(this.disabled){
6540             e.preventDefault();
6541             return;
6542         }
6543         
6544         if(this.preventDefault){
6545             e.preventDefault();
6546         }
6547         
6548         this.fireEvent('click', this, e);
6549     },
6550     
6551     disable : function()
6552     {
6553         this.setDisabled(true);
6554     },
6555     
6556     enable : function()
6557     {
6558         this.setDisabled(false);
6559     },
6560     
6561     setDisabled : function(state)
6562     {
6563         if(this.disabled == state){
6564             return;
6565         }
6566         
6567         this.disabled = state;
6568         
6569         if (state) {
6570             this.el.addClass('disabled');
6571             return;
6572         }
6573         
6574         this.el.removeClass('disabled');
6575         
6576         return;
6577     },
6578     
6579     setActive : function(state)
6580     {
6581         if(this.active == state){
6582             return;
6583         }
6584         
6585         this.active = state;
6586         
6587         if (state) {
6588             this.el.addClass('active');
6589             return;
6590         }
6591         
6592         this.el.removeClass('active');
6593         
6594         return;
6595     },
6596     
6597     isActive: function () 
6598     {
6599         return this.active;
6600     },
6601     
6602     setBadge : function(str)
6603     {
6604         if(!this.badgeEl){
6605             return;
6606         }
6607         
6608         this.badgeEl.dom.innerHTML = str;
6609     }
6610     
6611    
6612      
6613  
6614 });
6615  
6616
6617  /*
6618  * - LGPL
6619  *
6620  *  Breadcrumb Nav
6621  * 
6622  */
6623 Roo.namespace('Roo.bootstrap.breadcrumb');
6624
6625
6626 /**
6627  * @class Roo.bootstrap.breadcrumb.Nav
6628  * @extends Roo.bootstrap.Component
6629  * Bootstrap Breadcrumb Nav Class
6630  *  
6631  * @children Roo.bootstrap.breadcrumb.Item
6632  * 
6633  * @constructor
6634  * Create a new breadcrumb.Nav
6635  * @param {Object} config The config object
6636  */
6637
6638
6639 Roo.bootstrap.breadcrumb.Nav = function(config){
6640     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6641     
6642     
6643 };
6644
6645 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6646     
6647     getAutoCreate : function()
6648     {
6649
6650         var cfg = {
6651             tag: 'nav',
6652             cn : [
6653                 {
6654                     tag : 'ol',
6655                     cls : 'breadcrumb'
6656                 }
6657             ]
6658             
6659         };
6660           
6661         return cfg;
6662     },
6663     
6664     initEvents: function()
6665     {
6666         this.olEl = this.el.select('ol',true).first();    
6667     },
6668     getChildContainer : function()
6669     {
6670         return this.olEl;  
6671     }
6672     
6673 });
6674
6675  /*
6676  * - LGPL
6677  *
6678  *  Breadcrumb Item
6679  * 
6680  */
6681
6682
6683 /**
6684  * @class Roo.bootstrap.breadcrumb.Nav
6685  * @extends Roo.bootstrap.Component
6686  * Bootstrap Breadcrumb Nav Class
6687  *  
6688  * @children Roo.bootstrap.breadcrumb.Component
6689  * @cfg {String} html the content of the link.
6690  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6691  * @cfg {Boolean} active is it active
6692
6693  * 
6694  * @constructor
6695  * Create a new breadcrumb.Nav
6696  * @param {Object} config The config object
6697  */
6698
6699 Roo.bootstrap.breadcrumb.Item = function(config){
6700     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6701     this.addEvents({
6702         // img events
6703         /**
6704          * @event click
6705          * The img click event for the img.
6706          * @param {Roo.EventObject} e
6707          */
6708         "click" : true
6709     });
6710     
6711 };
6712
6713 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6714     
6715     href: false,
6716     html : '',
6717     
6718     getAutoCreate : function()
6719     {
6720
6721         var cfg = {
6722             tag: 'li',
6723             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6724         };
6725         if (this.href !== false) {
6726             cfg.cn = [{
6727                 tag : 'a',
6728                 href : this.href,
6729                 html : this.html
6730             }];
6731         } else {
6732             cfg.html = this.html;
6733         }
6734         
6735         return cfg;
6736     },
6737     
6738     initEvents: function()
6739     {
6740         if (this.href) {
6741             this.el.select('a', true).first().on('click',this.onClick, this)
6742         }
6743         
6744     },
6745     onClick : function(e)
6746     {
6747         e.preventDefault();
6748         this.fireEvent('click',this,  e);
6749     }
6750     
6751 });
6752
6753  /*
6754  * - LGPL
6755  *
6756  * row
6757  * 
6758  */
6759
6760 /**
6761  * @class Roo.bootstrap.Row
6762  * @extends Roo.bootstrap.Component
6763  * Bootstrap Row class (contains columns...)
6764  * 
6765  * @constructor
6766  * Create a new Row
6767  * @param {Object} config The config object
6768  */
6769
6770 Roo.bootstrap.Row = function(config){
6771     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6772 };
6773
6774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6775     
6776     getAutoCreate : function(){
6777        return {
6778             cls: 'row clearfix'
6779        };
6780     }
6781     
6782     
6783 });
6784
6785  
6786
6787  /*
6788  * - LGPL
6789  *
6790  * pagination
6791  * 
6792  */
6793
6794 /**
6795  * @class Roo.bootstrap.Pagination
6796  * @extends Roo.bootstrap.Component
6797  * Bootstrap Pagination class
6798  * @cfg {String} size xs | sm | md | lg
6799  * @cfg {Boolean} inverse false | true
6800  * 
6801  * @constructor
6802  * Create a new Pagination
6803  * @param {Object} config The config object
6804  */
6805
6806 Roo.bootstrap.Pagination = function(config){
6807     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6808 };
6809
6810 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6811     
6812     cls: false,
6813     size: false,
6814     inverse: false,
6815     
6816     getAutoCreate : function(){
6817         var cfg = {
6818             tag: 'ul',
6819                 cls: 'pagination'
6820         };
6821         if (this.inverse) {
6822             cfg.cls += ' inverse';
6823         }
6824         if (this.html) {
6825             cfg.html=this.html;
6826         }
6827         if (this.cls) {
6828             cfg.cls += " " + this.cls;
6829         }
6830         return cfg;
6831     }
6832    
6833 });
6834
6835  
6836
6837  /*
6838  * - LGPL
6839  *
6840  * Pagination item
6841  * 
6842  */
6843
6844
6845 /**
6846  * @class Roo.bootstrap.PaginationItem
6847  * @extends Roo.bootstrap.Component
6848  * Bootstrap PaginationItem class
6849  * @cfg {String} html text
6850  * @cfg {String} href the link
6851  * @cfg {Boolean} preventDefault (true | false) default true
6852  * @cfg {Boolean} active (true | false) default false
6853  * @cfg {Boolean} disabled default false
6854  * 
6855  * 
6856  * @constructor
6857  * Create a new PaginationItem
6858  * @param {Object} config The config object
6859  */
6860
6861
6862 Roo.bootstrap.PaginationItem = function(config){
6863     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6864     this.addEvents({
6865         // raw events
6866         /**
6867          * @event click
6868          * The raw click event for the entire grid.
6869          * @param {Roo.EventObject} e
6870          */
6871         "click" : true
6872     });
6873 };
6874
6875 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6876     
6877     href : false,
6878     html : false,
6879     preventDefault: true,
6880     active : false,
6881     cls : false,
6882     disabled: false,
6883     
6884     getAutoCreate : function(){
6885         var cfg= {
6886             tag: 'li',
6887             cn: [
6888                 {
6889                     tag : 'a',
6890                     href : this.href ? this.href : '#',
6891                     html : this.html ? this.html : ''
6892                 }
6893             ]
6894         };
6895         
6896         if(this.cls){
6897             cfg.cls = this.cls;
6898         }
6899         
6900         if(this.disabled){
6901             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6902         }
6903         
6904         if(this.active){
6905             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6906         }
6907         
6908         return cfg;
6909     },
6910     
6911     initEvents: function() {
6912         
6913         this.el.on('click', this.onClick, this);
6914         
6915     },
6916     onClick : function(e)
6917     {
6918         Roo.log('PaginationItem on click ');
6919         if(this.preventDefault){
6920             e.preventDefault();
6921         }
6922         
6923         if(this.disabled){
6924             return;
6925         }
6926         
6927         this.fireEvent('click', this, e);
6928     }
6929    
6930 });
6931
6932  
6933
6934  /*
6935  * - LGPL
6936  *
6937  * slider
6938  * 
6939  */
6940
6941
6942 /**
6943  * @class Roo.bootstrap.Slider
6944  * @extends Roo.bootstrap.Component
6945  * Bootstrap Slider class
6946  *    
6947  * @constructor
6948  * Create a new Slider
6949  * @param {Object} config The config object
6950  */
6951
6952 Roo.bootstrap.Slider = function(config){
6953     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6954 };
6955
6956 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6957     
6958     getAutoCreate : function(){
6959         
6960         var cfg = {
6961             tag: 'div',
6962             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6963             cn: [
6964                 {
6965                     tag: 'a',
6966                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6967                 }
6968             ]
6969         };
6970         
6971         return cfg;
6972     }
6973    
6974 });
6975
6976  /*
6977  * Based on:
6978  * Ext JS Library 1.1.1
6979  * Copyright(c) 2006-2007, Ext JS, LLC.
6980  *
6981  * Originally Released Under LGPL - original licence link has changed is not relivant.
6982  *
6983  * Fork - LGPL
6984  * <script type="text/javascript">
6985  */
6986  
6987
6988 /**
6989  * @class Roo.grid.ColumnModel
6990  * @extends Roo.util.Observable
6991  * This is the default implementation of a ColumnModel used by the Grid. It defines
6992  * the columns in the grid.
6993  * <br>Usage:<br>
6994  <pre><code>
6995  var colModel = new Roo.grid.ColumnModel([
6996         {header: "Ticker", width: 60, sortable: true, locked: true},
6997         {header: "Company Name", width: 150, sortable: true},
6998         {header: "Market Cap.", width: 100, sortable: true},
6999         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7000         {header: "Employees", width: 100, sortable: true, resizable: false}
7001  ]);
7002  </code></pre>
7003  * <p>
7004  
7005  * The config options listed for this class are options which may appear in each
7006  * individual column definition.
7007  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7008  * @constructor
7009  * @param {Object} config An Array of column config objects. See this class's
7010  * config objects for details.
7011 */
7012 Roo.grid.ColumnModel = function(config){
7013         /**
7014      * The config passed into the constructor
7015      */
7016     this.config = config;
7017     this.lookup = {};
7018
7019     // if no id, create one
7020     // if the column does not have a dataIndex mapping,
7021     // map it to the order it is in the config
7022     for(var i = 0, len = config.length; i < len; i++){
7023         var c = config[i];
7024         if(typeof c.dataIndex == "undefined"){
7025             c.dataIndex = i;
7026         }
7027         if(typeof c.renderer == "string"){
7028             c.renderer = Roo.util.Format[c.renderer];
7029         }
7030         if(typeof c.id == "undefined"){
7031             c.id = Roo.id();
7032         }
7033         if(c.editor && c.editor.xtype){
7034             c.editor  = Roo.factory(c.editor, Roo.grid);
7035         }
7036         if(c.editor && c.editor.isFormField){
7037             c.editor = new Roo.grid.GridEditor(c.editor);
7038         }
7039         this.lookup[c.id] = c;
7040     }
7041
7042     /**
7043      * The width of columns which have no width specified (defaults to 100)
7044      * @type Number
7045      */
7046     this.defaultWidth = 100;
7047
7048     /**
7049      * Default sortable of columns which have no sortable specified (defaults to false)
7050      * @type Boolean
7051      */
7052     this.defaultSortable = false;
7053
7054     this.addEvents({
7055         /**
7056              * @event widthchange
7057              * Fires when the width of a column changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newWidth The new width
7061              */
7062             "widthchange": true,
7063         /**
7064              * @event headerchange
7065              * Fires when the text of a header changes.
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Number} newText The new header text
7069              */
7070             "headerchange": true,
7071         /**
7072              * @event hiddenchange
7073              * Fires when a column is hidden or "unhidden".
7074              * @param {ColumnModel} this
7075              * @param {Number} columnIndex The column index
7076              * @param {Boolean} hidden true if hidden, false otherwise
7077              */
7078             "hiddenchange": true,
7079             /**
7080          * @event columnmoved
7081          * Fires when a column is moved.
7082          * @param {ColumnModel} this
7083          * @param {Number} oldIndex
7084          * @param {Number} newIndex
7085          */
7086         "columnmoved" : true,
7087         /**
7088          * @event columlockchange
7089          * Fires when a column's locked state is changed
7090          * @param {ColumnModel} this
7091          * @param {Number} colIndex
7092          * @param {Boolean} locked true if locked
7093          */
7094         "columnlockchange" : true
7095     });
7096     Roo.grid.ColumnModel.superclass.constructor.call(this);
7097 };
7098 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7099     /**
7100      * @cfg {String} header The header text to display in the Grid view.
7101      */
7102     /**
7103      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7104      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7105      * specified, the column's index is used as an index into the Record's data Array.
7106      */
7107     /**
7108      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7109      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7110      */
7111     /**
7112      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7113      * Defaults to the value of the {@link #defaultSortable} property.
7114      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7115      */
7116     /**
7117      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7121      */
7122     /**
7123      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7124      */
7125     /**
7126      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7127      */
7128     /**
7129      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7130      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7131      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7132      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7133      */
7134        /**
7135      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7136      */
7137     /**
7138      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7142      */
7143     /**
7144      * @cfg {String} cursor (Optional)
7145      */
7146     /**
7147      * @cfg {String} tooltip (Optional)
7148      */
7149     /**
7150      * @cfg {Number} xs (Optional)
7151      */
7152     /**
7153      * @cfg {Number} sm (Optional)
7154      */
7155     /**
7156      * @cfg {Number} md (Optional)
7157      */
7158     /**
7159      * @cfg {Number} lg (Optional)
7160      */
7161     /**
7162      * Returns the id of the column at the specified index.
7163      * @param {Number} index The column index
7164      * @return {String} the id
7165      */
7166     getColumnId : function(index){
7167         return this.config[index].id;
7168     },
7169
7170     /**
7171      * Returns the column for a specified id.
7172      * @param {String} id The column id
7173      * @return {Object} the column
7174      */
7175     getColumnById : function(id){
7176         return this.lookup[id];
7177     },
7178
7179     
7180     /**
7181      * Returns the column for a specified dataIndex.
7182      * @param {String} dataIndex The column dataIndex
7183      * @return {Object|Boolean} the column or false if not found
7184      */
7185     getColumnByDataIndex: function(dataIndex){
7186         var index = this.findColumnIndex(dataIndex);
7187         return index > -1 ? this.config[index] : false;
7188     },
7189     
7190     /**
7191      * Returns the index for a specified column id.
7192      * @param {String} id The column id
7193      * @return {Number} the index, or -1 if not found
7194      */
7195     getIndexById : function(id){
7196         for(var i = 0, len = this.config.length; i < len; i++){
7197             if(this.config[i].id == id){
7198                 return i;
7199             }
7200         }
7201         return -1;
7202     },
7203     
7204     /**
7205      * Returns the index for a specified column dataIndex.
7206      * @param {String} dataIndex The column dataIndex
7207      * @return {Number} the index, or -1 if not found
7208      */
7209     
7210     findColumnIndex : function(dataIndex){
7211         for(var i = 0, len = this.config.length; i < len; i++){
7212             if(this.config[i].dataIndex == dataIndex){
7213                 return i;
7214             }
7215         }
7216         return -1;
7217     },
7218     
7219     
7220     moveColumn : function(oldIndex, newIndex){
7221         var c = this.config[oldIndex];
7222         this.config.splice(oldIndex, 1);
7223         this.config.splice(newIndex, 0, c);
7224         this.dataMap = null;
7225         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7226     },
7227
7228     isLocked : function(colIndex){
7229         return this.config[colIndex].locked === true;
7230     },
7231
7232     setLocked : function(colIndex, value, suppressEvent){
7233         if(this.isLocked(colIndex) == value){
7234             return;
7235         }
7236         this.config[colIndex].locked = value;
7237         if(!suppressEvent){
7238             this.fireEvent("columnlockchange", this, colIndex, value);
7239         }
7240     },
7241
7242     getTotalLockedWidth : function(){
7243         var totalWidth = 0;
7244         for(var i = 0; i < this.config.length; i++){
7245             if(this.isLocked(i) && !this.isHidden(i)){
7246                 this.totalWidth += this.getColumnWidth(i);
7247             }
7248         }
7249         return totalWidth;
7250     },
7251
7252     getLockedCount : function(){
7253         for(var i = 0, len = this.config.length; i < len; i++){
7254             if(!this.isLocked(i)){
7255                 return i;
7256             }
7257         }
7258         
7259         return this.config.length;
7260     },
7261
7262     /**
7263      * Returns the number of columns.
7264      * @return {Number}
7265      */
7266     getColumnCount : function(visibleOnly){
7267         if(visibleOnly === true){
7268             var c = 0;
7269             for(var i = 0, len = this.config.length; i < len; i++){
7270                 if(!this.isHidden(i)){
7271                     c++;
7272                 }
7273             }
7274             return c;
7275         }
7276         return this.config.length;
7277     },
7278
7279     /**
7280      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7281      * @param {Function} fn
7282      * @param {Object} scope (optional)
7283      * @return {Array} result
7284      */
7285     getColumnsBy : function(fn, scope){
7286         var r = [];
7287         for(var i = 0, len = this.config.length; i < len; i++){
7288             var c = this.config[i];
7289             if(fn.call(scope||this, c, i) === true){
7290                 r[r.length] = c;
7291             }
7292         }
7293         return r;
7294     },
7295
7296     /**
7297      * Returns true if the specified column is sortable.
7298      * @param {Number} col The column index
7299      * @return {Boolean}
7300      */
7301     isSortable : function(col){
7302         if(typeof this.config[col].sortable == "undefined"){
7303             return this.defaultSortable;
7304         }
7305         return this.config[col].sortable;
7306     },
7307
7308     /**
7309      * Returns the rendering (formatting) function defined for the column.
7310      * @param {Number} col The column index.
7311      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7312      */
7313     getRenderer : function(col){
7314         if(!this.config[col].renderer){
7315             return Roo.grid.ColumnModel.defaultRenderer;
7316         }
7317         return this.config[col].renderer;
7318     },
7319
7320     /**
7321      * Sets the rendering (formatting) function for a column.
7322      * @param {Number} col The column index
7323      * @param {Function} fn The function to use to process the cell's raw data
7324      * to return HTML markup for the grid view. The render function is called with
7325      * the following parameters:<ul>
7326      * <li>Data value.</li>
7327      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7328      * <li>css A CSS style string to apply to the table cell.</li>
7329      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7330      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7331      * <li>Row index</li>
7332      * <li>Column index</li>
7333      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7334      */
7335     setRenderer : function(col, fn){
7336         this.config[col].renderer = fn;
7337     },
7338
7339     /**
7340      * Returns the width for the specified column.
7341      * @param {Number} col The column index
7342      * @return {Number}
7343      */
7344     getColumnWidth : function(col){
7345         return this.config[col].width * 1 || this.defaultWidth;
7346     },
7347
7348     /**
7349      * Sets the width for a column.
7350      * @param {Number} col The column index
7351      * @param {Number} width The new width
7352      */
7353     setColumnWidth : function(col, width, suppressEvent){
7354         this.config[col].width = width;
7355         this.totalWidth = null;
7356         if(!suppressEvent){
7357              this.fireEvent("widthchange", this, col, width);
7358         }
7359     },
7360
7361     /**
7362      * Returns the total width of all columns.
7363      * @param {Boolean} includeHidden True to include hidden column widths
7364      * @return {Number}
7365      */
7366     getTotalWidth : function(includeHidden){
7367         if(!this.totalWidth){
7368             this.totalWidth = 0;
7369             for(var i = 0, len = this.config.length; i < len; i++){
7370                 if(includeHidden || !this.isHidden(i)){
7371                     this.totalWidth += this.getColumnWidth(i);
7372                 }
7373             }
7374         }
7375         return this.totalWidth;
7376     },
7377
7378     /**
7379      * Returns the header for the specified column.
7380      * @param {Number} col The column index
7381      * @return {String}
7382      */
7383     getColumnHeader : function(col){
7384         return this.config[col].header;
7385     },
7386
7387     /**
7388      * Sets the header for a column.
7389      * @param {Number} col The column index
7390      * @param {String} header The new header
7391      */
7392     setColumnHeader : function(col, header){
7393         this.config[col].header = header;
7394         this.fireEvent("headerchange", this, col, header);
7395     },
7396
7397     /**
7398      * Returns the tooltip for the specified column.
7399      * @param {Number} col The column index
7400      * @return {String}
7401      */
7402     getColumnTooltip : function(col){
7403             return this.config[col].tooltip;
7404     },
7405     /**
7406      * Sets the tooltip for a column.
7407      * @param {Number} col The column index
7408      * @param {String} tooltip The new tooltip
7409      */
7410     setColumnTooltip : function(col, tooltip){
7411             this.config[col].tooltip = tooltip;
7412     },
7413
7414     /**
7415      * Returns the dataIndex for the specified column.
7416      * @param {Number} col The column index
7417      * @return {Number}
7418      */
7419     getDataIndex : function(col){
7420         return this.config[col].dataIndex;
7421     },
7422
7423     /**
7424      * Sets the dataIndex for a column.
7425      * @param {Number} col The column index
7426      * @param {Number} dataIndex The new dataIndex
7427      */
7428     setDataIndex : function(col, dataIndex){
7429         this.config[col].dataIndex = dataIndex;
7430     },
7431
7432     
7433     
7434     /**
7435      * Returns true if the cell is editable.
7436      * @param {Number} colIndex The column index
7437      * @param {Number} rowIndex The row index - this is nto actually used..?
7438      * @return {Boolean}
7439      */
7440     isCellEditable : function(colIndex, rowIndex){
7441         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7442     },
7443
7444     /**
7445      * Returns the editor defined for the cell/column.
7446      * return false or null to disable editing.
7447      * @param {Number} colIndex The column index
7448      * @param {Number} rowIndex The row index
7449      * @return {Object}
7450      */
7451     getCellEditor : function(colIndex, rowIndex){
7452         return this.config[colIndex].editor;
7453     },
7454
7455     /**
7456      * Sets if a column is editable.
7457      * @param {Number} col The column index
7458      * @param {Boolean} editable True if the column is editable
7459      */
7460     setEditable : function(col, editable){
7461         this.config[col].editable = editable;
7462     },
7463
7464
7465     /**
7466      * Returns true if the column is hidden.
7467      * @param {Number} colIndex The column index
7468      * @return {Boolean}
7469      */
7470     isHidden : function(colIndex){
7471         return this.config[colIndex].hidden;
7472     },
7473
7474
7475     /**
7476      * Returns true if the column width cannot be changed
7477      */
7478     isFixed : function(colIndex){
7479         return this.config[colIndex].fixed;
7480     },
7481
7482     /**
7483      * Returns true if the column can be resized
7484      * @return {Boolean}
7485      */
7486     isResizable : function(colIndex){
7487         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7488     },
7489     /**
7490      * Sets if a column is hidden.
7491      * @param {Number} colIndex The column index
7492      * @param {Boolean} hidden True if the column is hidden
7493      */
7494     setHidden : function(colIndex, hidden){
7495         this.config[colIndex].hidden = hidden;
7496         this.totalWidth = null;
7497         this.fireEvent("hiddenchange", this, colIndex, hidden);
7498     },
7499
7500     /**
7501      * Sets the editor for a column.
7502      * @param {Number} col The column index
7503      * @param {Object} editor The editor object
7504      */
7505     setEditor : function(col, editor){
7506         this.config[col].editor = editor;
7507     }
7508 });
7509
7510 Roo.grid.ColumnModel.defaultRenderer = function(value)
7511 {
7512     if(typeof value == "object") {
7513         return value;
7514     }
7515         if(typeof value == "string" && value.length < 1){
7516             return "&#160;";
7517         }
7518     
7519         return String.format("{0}", value);
7520 };
7521
7522 // Alias for backwards compatibility
7523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7524 /*
7525  * Based on:
7526  * Ext JS Library 1.1.1
7527  * Copyright(c) 2006-2007, Ext JS, LLC.
7528  *
7529  * Originally Released Under LGPL - original licence link has changed is not relivant.
7530  *
7531  * Fork - LGPL
7532  * <script type="text/javascript">
7533  */
7534  
7535 /**
7536  * @class Roo.LoadMask
7537  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7538  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7539  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7540  * element's UpdateManager load indicator and will be destroyed after the initial load.
7541  * @constructor
7542  * Create a new LoadMask
7543  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7544  * @param {Object} config The config object
7545  */
7546 Roo.LoadMask = function(el, config){
7547     this.el = Roo.get(el);
7548     Roo.apply(this, config);
7549     if(this.store){
7550         this.store.on('beforeload', this.onBeforeLoad, this);
7551         this.store.on('load', this.onLoad, this);
7552         this.store.on('loadexception', this.onLoadException, this);
7553         this.removeMask = false;
7554     }else{
7555         var um = this.el.getUpdateManager();
7556         um.showLoadIndicator = false; // disable the default indicator
7557         um.on('beforeupdate', this.onBeforeLoad, this);
7558         um.on('update', this.onLoad, this);
7559         um.on('failure', this.onLoad, this);
7560         this.removeMask = true;
7561     }
7562 };
7563
7564 Roo.LoadMask.prototype = {
7565     /**
7566      * @cfg {Boolean} removeMask
7567      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7568      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7569      */
7570     /**
7571      * @cfg {String} msg
7572      * The text to display in a centered loading message box (defaults to 'Loading...')
7573      */
7574     msg : 'Loading...',
7575     /**
7576      * @cfg {String} msgCls
7577      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7578      */
7579     msgCls : 'x-mask-loading',
7580
7581     /**
7582      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7583      * @type Boolean
7584      */
7585     disabled: false,
7586
7587     /**
7588      * Disables the mask to prevent it from being displayed
7589      */
7590     disable : function(){
7591        this.disabled = true;
7592     },
7593
7594     /**
7595      * Enables the mask so that it can be displayed
7596      */
7597     enable : function(){
7598         this.disabled = false;
7599     },
7600     
7601     onLoadException : function()
7602     {
7603         Roo.log(arguments);
7604         
7605         if (typeof(arguments[3]) != 'undefined') {
7606             Roo.MessageBox.alert("Error loading",arguments[3]);
7607         } 
7608         /*
7609         try {
7610             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7611                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7612             }   
7613         } catch(e) {
7614             
7615         }
7616         */
7617     
7618         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7619     },
7620     // private
7621     onLoad : function()
7622     {
7623         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7624     },
7625
7626     // private
7627     onBeforeLoad : function(){
7628         if(!this.disabled){
7629             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7630         }
7631     },
7632
7633     // private
7634     destroy : function(){
7635         if(this.store){
7636             this.store.un('beforeload', this.onBeforeLoad, this);
7637             this.store.un('load', this.onLoad, this);
7638             this.store.un('loadexception', this.onLoadException, this);
7639         }else{
7640             var um = this.el.getUpdateManager();
7641             um.un('beforeupdate', this.onBeforeLoad, this);
7642             um.un('update', this.onLoad, this);
7643             um.un('failure', this.onLoad, this);
7644         }
7645     }
7646 };/*
7647  * - LGPL
7648  *
7649  * table
7650  * 
7651  */
7652
7653 /**
7654  * @class Roo.bootstrap.Table
7655  * @extends Roo.bootstrap.Component
7656  * Bootstrap Table class
7657  * @cfg {String} cls table class
7658  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7659  * @cfg {String} bgcolor Specifies the background color for a table
7660  * @cfg {Number} border Specifies whether the table cells should have borders or not
7661  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7662  * @cfg {Number} cellspacing Specifies the space between cells
7663  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7664  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7665  * @cfg {String} sortable Specifies that the table should be sortable
7666  * @cfg {String} summary Specifies a summary of the content of a table
7667  * @cfg {Number} width Specifies the width of a table
7668  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7669  * 
7670  * @cfg {boolean} striped Should the rows be alternative striped
7671  * @cfg {boolean} bordered Add borders to the table
7672  * @cfg {boolean} hover Add hover highlighting
7673  * @cfg {boolean} condensed Format condensed
7674  * @cfg {boolean} responsive Format condensed
7675  * @cfg {Boolean} loadMask (true|false) default false
7676  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7677  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7678  * @cfg {Boolean} rowSelection (true|false) default false
7679  * @cfg {Boolean} cellSelection (true|false) default false
7680  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7681  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7682  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7683  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7684  
7685  * 
7686  * @constructor
7687  * Create a new Table
7688  * @param {Object} config The config object
7689  */
7690
7691 Roo.bootstrap.Table = function(config){
7692     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7693     
7694   
7695     
7696     // BC...
7697     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7698     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7699     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7700     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7701     
7702     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7703     if (this.sm) {
7704         this.sm.grid = this;
7705         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7706         this.sm = this.selModel;
7707         this.sm.xmodule = this.xmodule || false;
7708     }
7709     
7710     if (this.cm && typeof(this.cm.config) == 'undefined') {
7711         this.colModel = new Roo.grid.ColumnModel(this.cm);
7712         this.cm = this.colModel;
7713         this.cm.xmodule = this.xmodule || false;
7714     }
7715     if (this.store) {
7716         this.store= Roo.factory(this.store, Roo.data);
7717         this.ds = this.store;
7718         this.ds.xmodule = this.xmodule || false;
7719          
7720     }
7721     if (this.footer && this.store) {
7722         this.footer.dataSource = this.ds;
7723         this.footer = Roo.factory(this.footer);
7724     }
7725     
7726     /** @private */
7727     this.addEvents({
7728         /**
7729          * @event cellclick
7730          * Fires when a cell is clicked
7731          * @param {Roo.bootstrap.Table} this
7732          * @param {Roo.Element} el
7733          * @param {Number} rowIndex
7734          * @param {Number} columnIndex
7735          * @param {Roo.EventObject} e
7736          */
7737         "cellclick" : true,
7738         /**
7739          * @event celldblclick
7740          * Fires when a cell is double clicked
7741          * @param {Roo.bootstrap.Table} this
7742          * @param {Roo.Element} el
7743          * @param {Number} rowIndex
7744          * @param {Number} columnIndex
7745          * @param {Roo.EventObject} e
7746          */
7747         "celldblclick" : true,
7748         /**
7749          * @event rowclick
7750          * Fires when a row is clicked
7751          * @param {Roo.bootstrap.Table} this
7752          * @param {Roo.Element} el
7753          * @param {Number} rowIndex
7754          * @param {Roo.EventObject} e
7755          */
7756         "rowclick" : true,
7757         /**
7758          * @event rowdblclick
7759          * Fires when a row is double clicked
7760          * @param {Roo.bootstrap.Table} this
7761          * @param {Roo.Element} el
7762          * @param {Number} rowIndex
7763          * @param {Roo.EventObject} e
7764          */
7765         "rowdblclick" : true,
7766         /**
7767          * @event mouseover
7768          * Fires when a mouseover occur
7769          * @param {Roo.bootstrap.Table} this
7770          * @param {Roo.Element} el
7771          * @param {Number} rowIndex
7772          * @param {Number} columnIndex
7773          * @param {Roo.EventObject} e
7774          */
7775         "mouseover" : true,
7776         /**
7777          * @event mouseout
7778          * Fires when a mouseout occur
7779          * @param {Roo.bootstrap.Table} this
7780          * @param {Roo.Element} el
7781          * @param {Number} rowIndex
7782          * @param {Number} columnIndex
7783          * @param {Roo.EventObject} e
7784          */
7785         "mouseout" : true,
7786         /**
7787          * @event rowclass
7788          * Fires when a row is rendered, so you can change add a style to it.
7789          * @param {Roo.bootstrap.Table} this
7790          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7791          */
7792         'rowclass' : true,
7793           /**
7794          * @event rowsrendered
7795          * Fires when all the  rows have been rendered
7796          * @param {Roo.bootstrap.Table} this
7797          */
7798         'rowsrendered' : true,
7799         /**
7800          * @event contextmenu
7801          * The raw contextmenu event for the entire grid.
7802          * @param {Roo.EventObject} e
7803          */
7804         "contextmenu" : true,
7805         /**
7806          * @event rowcontextmenu
7807          * Fires when a row is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Roo.EventObject} e
7811          */
7812         "rowcontextmenu" : true,
7813         /**
7814          * @event cellcontextmenu
7815          * Fires when a cell is right clicked
7816          * @param {Roo.bootstrap.Table} this
7817          * @param {Number} rowIndex
7818          * @param {Number} cellIndex
7819          * @param {Roo.EventObject} e
7820          */
7821          "cellcontextmenu" : true,
7822          /**
7823          * @event headercontextmenu
7824          * Fires when a header is right clicked
7825          * @param {Roo.bootstrap.Table} this
7826          * @param {Number} columnIndex
7827          * @param {Roo.EventObject} e
7828          */
7829         "headercontextmenu" : true
7830     });
7831 };
7832
7833 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7834     
7835     cls: false,
7836     align: false,
7837     bgcolor: false,
7838     border: false,
7839     cellpadding: false,
7840     cellspacing: false,
7841     frame: false,
7842     rules: false,
7843     sortable: false,
7844     summary: false,
7845     width: false,
7846     striped : false,
7847     scrollBody : false,
7848     bordered: false,
7849     hover:  false,
7850     condensed : false,
7851     responsive : false,
7852     sm : false,
7853     cm : false,
7854     store : false,
7855     loadMask : false,
7856     footerShow : true,
7857     headerShow : true,
7858   
7859     rowSelection : false,
7860     cellSelection : false,
7861     layout : false,
7862     
7863     // Roo.Element - the tbody
7864     mainBody: false,
7865     // Roo.Element - thead element
7866     mainHead: false,
7867     
7868     container: false, // used by gridpanel...
7869     
7870     lazyLoad : false,
7871     
7872     CSS : Roo.util.CSS,
7873     
7874     auto_hide_footer : false,
7875     
7876     getAutoCreate : function()
7877     {
7878         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7879         
7880         cfg = {
7881             tag: 'table',
7882             cls : 'table',
7883             cn : []
7884         };
7885         if (this.scrollBody) {
7886             cfg.cls += ' table-body-fixed';
7887         }    
7888         if (this.striped) {
7889             cfg.cls += ' table-striped';
7890         }
7891         
7892         if (this.hover) {
7893             cfg.cls += ' table-hover';
7894         }
7895         if (this.bordered) {
7896             cfg.cls += ' table-bordered';
7897         }
7898         if (this.condensed) {
7899             cfg.cls += ' table-condensed';
7900         }
7901         if (this.responsive) {
7902             cfg.cls += ' table-responsive';
7903         }
7904         
7905         if (this.cls) {
7906             cfg.cls+=  ' ' +this.cls;
7907         }
7908         
7909         // this lot should be simplifed...
7910         var _t = this;
7911         var cp = [
7912             'align',
7913             'bgcolor',
7914             'border',
7915             'cellpadding',
7916             'cellspacing',
7917             'frame',
7918             'rules',
7919             'sortable',
7920             'summary',
7921             'width'
7922         ].forEach(function(k) {
7923             if (_t[k]) {
7924                 cfg[k] = _t[k];
7925             }
7926         });
7927         
7928         
7929         if (this.layout) {
7930             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7931         }
7932         
7933         if(this.store || this.cm){
7934             if(this.headerShow){
7935                 cfg.cn.push(this.renderHeader());
7936             }
7937             
7938             cfg.cn.push(this.renderBody());
7939             
7940             if(this.footerShow){
7941                 cfg.cn.push(this.renderFooter());
7942             }
7943             // where does this come from?
7944             //cfg.cls+=  ' TableGrid';
7945         }
7946         
7947         return { cn : [ cfg ] };
7948     },
7949     
7950     initEvents : function()
7951     {   
7952         if(!this.store || !this.cm){
7953             return;
7954         }
7955         if (this.selModel) {
7956             this.selModel.initEvents();
7957         }
7958         
7959         
7960         //Roo.log('initEvents with ds!!!!');
7961         
7962         this.mainBody = this.el.select('tbody', true).first();
7963         this.mainHead = this.el.select('thead', true).first();
7964         this.mainFoot = this.el.select('tfoot', true).first();
7965         
7966         
7967         
7968         var _this = this;
7969         
7970         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7971             e.on('click', _this.sort, _this);
7972         });
7973         
7974         this.mainBody.on("click", this.onClick, this);
7975         this.mainBody.on("dblclick", this.onDblClick, this);
7976         
7977         // why is this done????? = it breaks dialogs??
7978         //this.parent().el.setStyle('position', 'relative');
7979         
7980         
7981         if (this.footer) {
7982             this.footer.parentId = this.id;
7983             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7984             
7985             if(this.lazyLoad){
7986                 this.el.select('tfoot tr td').first().addClass('hide');
7987             }
7988         } 
7989         
7990         if(this.loadMask) {
7991             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7992         }
7993         
7994         this.store.on('load', this.onLoad, this);
7995         this.store.on('beforeload', this.onBeforeLoad, this);
7996         this.store.on('update', this.onUpdate, this);
7997         this.store.on('add', this.onAdd, this);
7998         this.store.on("clear", this.clear, this);
7999         
8000         this.el.on("contextmenu", this.onContextMenu, this);
8001         
8002         this.mainBody.on('scroll', this.onBodyScroll, this);
8003         
8004         this.cm.on("headerchange", this.onHeaderChange, this);
8005         
8006         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8007         
8008     },
8009     
8010     onContextMenu : function(e, t)
8011     {
8012         this.processEvent("contextmenu", e);
8013     },
8014     
8015     processEvent : function(name, e)
8016     {
8017         if (name != 'touchstart' ) {
8018             this.fireEvent(name, e);    
8019         }
8020         
8021         var t = e.getTarget();
8022         
8023         var cell = Roo.get(t);
8024         
8025         if(!cell){
8026             return;
8027         }
8028         
8029         if(cell.findParent('tfoot', false, true)){
8030             return;
8031         }
8032         
8033         if(cell.findParent('thead', false, true)){
8034             
8035             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8036                 cell = Roo.get(t).findParent('th', false, true);
8037                 if (!cell) {
8038                     Roo.log("failed to find th in thead?");
8039                     Roo.log(e.getTarget());
8040                     return;
8041                 }
8042             }
8043             
8044             var cellIndex = cell.dom.cellIndex;
8045             
8046             var ename = name == 'touchstart' ? 'click' : name;
8047             this.fireEvent("header" + ename, this, cellIndex, e);
8048             
8049             return;
8050         }
8051         
8052         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8053             cell = Roo.get(t).findParent('td', false, true);
8054             if (!cell) {
8055                 Roo.log("failed to find th in tbody?");
8056                 Roo.log(e.getTarget());
8057                 return;
8058             }
8059         }
8060         
8061         var row = cell.findParent('tr', false, true);
8062         var cellIndex = cell.dom.cellIndex;
8063         var rowIndex = row.dom.rowIndex - 1;
8064         
8065         if(row !== false){
8066             
8067             this.fireEvent("row" + name, this, rowIndex, e);
8068             
8069             if(cell !== false){
8070             
8071                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8072             }
8073         }
8074         
8075     },
8076     
8077     onMouseover : function(e, el)
8078     {
8079         var cell = Roo.get(el);
8080         
8081         if(!cell){
8082             return;
8083         }
8084         
8085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8086             cell = cell.findParent('td', false, true);
8087         }
8088         
8089         var row = cell.findParent('tr', false, true);
8090         var cellIndex = cell.dom.cellIndex;
8091         var rowIndex = row.dom.rowIndex - 1; // start from 0
8092         
8093         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8094         
8095     },
8096     
8097     onMouseout : function(e, el)
8098     {
8099         var cell = Roo.get(el);
8100         
8101         if(!cell){
8102             return;
8103         }
8104         
8105         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8106             cell = cell.findParent('td', false, true);
8107         }
8108         
8109         var row = cell.findParent('tr', false, true);
8110         var cellIndex = cell.dom.cellIndex;
8111         var rowIndex = row.dom.rowIndex - 1; // start from 0
8112         
8113         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8114         
8115     },
8116     
8117     onClick : function(e, el)
8118     {
8119         var cell = Roo.get(el);
8120         
8121         if(!cell || (!this.cellSelection && !this.rowSelection)){
8122             return;
8123         }
8124         
8125         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126             cell = cell.findParent('td', false, true);
8127         }
8128         
8129         if(!cell || typeof(cell) == 'undefined'){
8130             return;
8131         }
8132         
8133         var row = cell.findParent('tr', false, true);
8134         
8135         if(!row || typeof(row) == 'undefined'){
8136             return;
8137         }
8138         
8139         var cellIndex = cell.dom.cellIndex;
8140         var rowIndex = this.getRowIndex(row);
8141         
8142         // why??? - should these not be based on SelectionModel?
8143         if(this.cellSelection){
8144             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8145         }
8146         
8147         if(this.rowSelection){
8148             this.fireEvent('rowclick', this, row, rowIndex, e);
8149         }
8150         
8151         
8152     },
8153         
8154     onDblClick : function(e,el)
8155     {
8156         var cell = Roo.get(el);
8157         
8158         if(!cell || (!this.cellSelection && !this.rowSelection)){
8159             return;
8160         }
8161         
8162         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8163             cell = cell.findParent('td', false, true);
8164         }
8165         
8166         if(!cell || typeof(cell) == 'undefined'){
8167             return;
8168         }
8169         
8170         var row = cell.findParent('tr', false, true);
8171         
8172         if(!row || typeof(row) == 'undefined'){
8173             return;
8174         }
8175         
8176         var cellIndex = cell.dom.cellIndex;
8177         var rowIndex = this.getRowIndex(row);
8178         
8179         if(this.cellSelection){
8180             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8181         }
8182         
8183         if(this.rowSelection){
8184             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8185         }
8186     },
8187     
8188     sort : function(e,el)
8189     {
8190         var col = Roo.get(el);
8191         
8192         if(!col.hasClass('sortable')){
8193             return;
8194         }
8195         
8196         var sort = col.attr('sort');
8197         var dir = 'ASC';
8198         
8199         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8200             dir = 'DESC';
8201         }
8202         
8203         this.store.sortInfo = {field : sort, direction : dir};
8204         
8205         if (this.footer) {
8206             Roo.log("calling footer first");
8207             this.footer.onClick('first');
8208         } else {
8209         
8210             this.store.load({ params : { start : 0 } });
8211         }
8212     },
8213     
8214     renderHeader : function()
8215     {
8216         var header = {
8217             tag: 'thead',
8218             cn : []
8219         };
8220         
8221         var cm = this.cm;
8222         this.totalWidth = 0;
8223         
8224         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8225             
8226             var config = cm.config[i];
8227             
8228             var c = {
8229                 tag: 'th',
8230                 cls : 'x-hcol-' + i,
8231                 style : '',
8232                 html: cm.getColumnHeader(i)
8233             };
8234             
8235             var hh = '';
8236             
8237             if(typeof(config.sortable) != 'undefined' && config.sortable){
8238                 c.cls = 'sortable';
8239                 c.html = '<i class="glyphicon"></i>' + c.html;
8240             }
8241             
8242             // could use BS4 hidden-..-down 
8243             
8244             if(typeof(config.lgHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.mdHeader) != 'undefined'){
8249                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8250             }
8251             
8252             if(typeof(config.smHeader) != 'undefined'){
8253                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8254             }
8255             
8256             if(typeof(config.xsHeader) != 'undefined'){
8257                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8258             }
8259             
8260             if(hh.length){
8261                 c.html = hh;
8262             }
8263             
8264             if(typeof(config.tooltip) != 'undefined'){
8265                 c.tooltip = config.tooltip;
8266             }
8267             
8268             if(typeof(config.colspan) != 'undefined'){
8269                 c.colspan = config.colspan;
8270             }
8271             
8272             if(typeof(config.hidden) != 'undefined' && config.hidden){
8273                 c.style += ' display:none;';
8274             }
8275             
8276             if(typeof(config.dataIndex) != 'undefined'){
8277                 c.sort = config.dataIndex;
8278             }
8279             
8280            
8281             
8282             if(typeof(config.align) != 'undefined' && config.align.length){
8283                 c.style += ' text-align:' + config.align + ';';
8284             }
8285             
8286             if(typeof(config.width) != 'undefined'){
8287                 c.style += ' width:' + config.width + 'px;';
8288                 this.totalWidth += config.width;
8289             } else {
8290                 this.totalWidth += 100; // assume minimum of 100 per column?
8291             }
8292             
8293             if(typeof(config.cls) != 'undefined'){
8294                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8295             }
8296             
8297             ['xs','sm','md','lg'].map(function(size){
8298                 
8299                 if(typeof(config[size]) == 'undefined'){
8300                     return;
8301                 }
8302                  
8303                 if (!config[size]) { // 0 = hidden
8304                     // BS 4 '0' is treated as hide that column and below.
8305                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8306                     return;
8307                 }
8308                 
8309                 c.cls += ' col-' + size + '-' + config[size] + (
8310                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8311                 );
8312                 
8313                 
8314             });
8315             
8316             header.cn.push(c)
8317         }
8318         
8319         return header;
8320     },
8321     
8322     renderBody : function()
8323     {
8324         var body = {
8325             tag: 'tbody',
8326             cn : [
8327                 {
8328                     tag: 'tr',
8329                     cn : [
8330                         {
8331                             tag : 'td',
8332                             colspan :  this.cm.getColumnCount()
8333                         }
8334                     ]
8335                 }
8336             ]
8337         };
8338         
8339         return body;
8340     },
8341     
8342     renderFooter : function()
8343     {
8344         var footer = {
8345             tag: 'tfoot',
8346             cn : [
8347                 {
8348                     tag: 'tr',
8349                     cn : [
8350                         {
8351                             tag : 'td',
8352                             colspan :  this.cm.getColumnCount()
8353                         }
8354                     ]
8355                 }
8356             ]
8357         };
8358         
8359         return footer;
8360     },
8361     
8362     
8363     
8364     onLoad : function()
8365     {
8366 //        Roo.log('ds onload');
8367         this.clear();
8368         
8369         var _this = this;
8370         var cm = this.cm;
8371         var ds = this.store;
8372         
8373         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8374             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8375             if (_this.store.sortInfo) {
8376                     
8377                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8378                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8379                 }
8380                 
8381                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8382                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8383                 }
8384             }
8385         });
8386         
8387         var tbody =  this.mainBody;
8388               
8389         if(ds.getCount() > 0){
8390             ds.data.each(function(d,rowIndex){
8391                 var row =  this.renderRow(cm, ds, rowIndex);
8392                 
8393                 tbody.createChild(row);
8394                 
8395                 var _this = this;
8396                 
8397                 if(row.cellObjects.length){
8398                     Roo.each(row.cellObjects, function(r){
8399                         _this.renderCellObject(r);
8400                     })
8401                 }
8402                 
8403             }, this);
8404         }
8405         
8406         var tfoot = this.el.select('tfoot', true).first();
8407         
8408         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8409             
8410             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8411             
8412             var total = this.ds.getTotalCount();
8413             
8414             if(this.footer.pageSize < total){
8415                 this.mainFoot.show();
8416             }
8417         }
8418         
8419         Roo.each(this.el.select('tbody td', true).elements, function(e){
8420             e.on('mouseover', _this.onMouseover, _this);
8421         });
8422         
8423         Roo.each(this.el.select('tbody td', true).elements, function(e){
8424             e.on('mouseout', _this.onMouseout, _this);
8425         });
8426         this.fireEvent('rowsrendered', this);
8427         
8428         this.autoSize();
8429     },
8430     
8431     
8432     onUpdate : function(ds,record)
8433     {
8434         this.refreshRow(record);
8435         this.autoSize();
8436     },
8437     
8438     onRemove : function(ds, record, index, isUpdate){
8439         if(isUpdate !== true){
8440             this.fireEvent("beforerowremoved", this, index, record);
8441         }
8442         var bt = this.mainBody.dom;
8443         
8444         var rows = this.el.select('tbody > tr', true).elements;
8445         
8446         if(typeof(rows[index]) != 'undefined'){
8447             bt.removeChild(rows[index].dom);
8448         }
8449         
8450 //        if(bt.rows[index]){
8451 //            bt.removeChild(bt.rows[index]);
8452 //        }
8453         
8454         if(isUpdate !== true){
8455             //this.stripeRows(index);
8456             //this.syncRowHeights(index, index);
8457             //this.layout();
8458             this.fireEvent("rowremoved", this, index, record);
8459         }
8460     },
8461     
8462     onAdd : function(ds, records, rowIndex)
8463     {
8464         //Roo.log('on Add called');
8465         // - note this does not handle multiple adding very well..
8466         var bt = this.mainBody.dom;
8467         for (var i =0 ; i < records.length;i++) {
8468             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8469             //Roo.log(records[i]);
8470             //Roo.log(this.store.getAt(rowIndex+i));
8471             this.insertRow(this.store, rowIndex + i, false);
8472             return;
8473         }
8474         
8475     },
8476     
8477     
8478     refreshRow : function(record){
8479         var ds = this.store, index;
8480         if(typeof record == 'number'){
8481             index = record;
8482             record = ds.getAt(index);
8483         }else{
8484             index = ds.indexOf(record);
8485             if (index < 0) {
8486                 return; // should not happen - but seems to 
8487             }
8488         }
8489         this.insertRow(ds, index, true);
8490         this.autoSize();
8491         this.onRemove(ds, record, index+1, true);
8492         this.autoSize();
8493         //this.syncRowHeights(index, index);
8494         //this.layout();
8495         this.fireEvent("rowupdated", this, index, record);
8496     },
8497     
8498     insertRow : function(dm, rowIndex, isUpdate){
8499         
8500         if(!isUpdate){
8501             this.fireEvent("beforerowsinserted", this, rowIndex);
8502         }
8503             //var s = this.getScrollState();
8504         var row = this.renderRow(this.cm, this.store, rowIndex);
8505         // insert before rowIndex..
8506         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8507         
8508         var _this = this;
8509                 
8510         if(row.cellObjects.length){
8511             Roo.each(row.cellObjects, function(r){
8512                 _this.renderCellObject(r);
8513             })
8514         }
8515             
8516         if(!isUpdate){
8517             this.fireEvent("rowsinserted", this, rowIndex);
8518             //this.syncRowHeights(firstRow, lastRow);
8519             //this.stripeRows(firstRow);
8520             //this.layout();
8521         }
8522         
8523     },
8524     
8525     
8526     getRowDom : function(rowIndex)
8527     {
8528         var rows = this.el.select('tbody > tr', true).elements;
8529         
8530         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8531         
8532     },
8533     // returns the object tree for a tr..
8534   
8535     
8536     renderRow : function(cm, ds, rowIndex) 
8537     {
8538         var d = ds.getAt(rowIndex);
8539         
8540         var row = {
8541             tag : 'tr',
8542             cls : 'x-row-' + rowIndex,
8543             cn : []
8544         };
8545             
8546         var cellObjects = [];
8547         
8548         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8549             var config = cm.config[i];
8550             
8551             var renderer = cm.getRenderer(i);
8552             var value = '';
8553             var id = false;
8554             
8555             if(typeof(renderer) !== 'undefined'){
8556                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8557             }
8558             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8559             // and are rendered into the cells after the row is rendered - using the id for the element.
8560             
8561             if(typeof(value) === 'object'){
8562                 id = Roo.id();
8563                 cellObjects.push({
8564                     container : id,
8565                     cfg : value 
8566                 })
8567             }
8568             
8569             var rowcfg = {
8570                 record: d,
8571                 rowIndex : rowIndex,
8572                 colIndex : i,
8573                 rowClass : ''
8574             };
8575
8576             this.fireEvent('rowclass', this, rowcfg);
8577             
8578             var td = {
8579                 tag: 'td',
8580                 cls : rowcfg.rowClass + ' x-col-' + i,
8581                 style: '',
8582                 html: (typeof(value) === 'object') ? '' : value
8583             };
8584             
8585             if (id) {
8586                 td.id = id;
8587             }
8588             
8589             if(typeof(config.colspan) != 'undefined'){
8590                 td.colspan = config.colspan;
8591             }
8592             
8593             if(typeof(config.hidden) != 'undefined' && config.hidden){
8594                 td.style += ' display:none;';
8595             }
8596             
8597             if(typeof(config.align) != 'undefined' && config.align.length){
8598                 td.style += ' text-align:' + config.align + ';';
8599             }
8600             if(typeof(config.valign) != 'undefined' && config.valign.length){
8601                 td.style += ' vertical-align:' + config.valign + ';';
8602             }
8603             
8604             if(typeof(config.width) != 'undefined'){
8605                 td.style += ' width:' +  config.width + 'px;';
8606             }
8607             
8608             if(typeof(config.cursor) != 'undefined'){
8609                 td.style += ' cursor:' +  config.cursor + ';';
8610             }
8611             
8612             if(typeof(config.cls) != 'undefined'){
8613                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8614             }
8615             
8616             ['xs','sm','md','lg'].map(function(size){
8617                 
8618                 if(typeof(config[size]) == 'undefined'){
8619                     return;
8620                 }
8621                 
8622                 
8623                   
8624                 if (!config[size]) { // 0 = hidden
8625                     // BS 4 '0' is treated as hide that column and below.
8626                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8627                     return;
8628                 }
8629                 
8630                 td.cls += ' col-' + size + '-' + config[size] + (
8631                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8632                 );
8633                  
8634
8635             });
8636             
8637             row.cn.push(td);
8638            
8639         }
8640         
8641         row.cellObjects = cellObjects;
8642         
8643         return row;
8644           
8645     },
8646     
8647     
8648     
8649     onBeforeLoad : function()
8650     {
8651         
8652     },
8653      /**
8654      * Remove all rows
8655      */
8656     clear : function()
8657     {
8658         this.el.select('tbody', true).first().dom.innerHTML = '';
8659     },
8660     /**
8661      * Show or hide a row.
8662      * @param {Number} rowIndex to show or hide
8663      * @param {Boolean} state hide
8664      */
8665     setRowVisibility : function(rowIndex, state)
8666     {
8667         var bt = this.mainBody.dom;
8668         
8669         var rows = this.el.select('tbody > tr', true).elements;
8670         
8671         if(typeof(rows[rowIndex]) == 'undefined'){
8672             return;
8673         }
8674         rows[rowIndex].dom.style.display = state ? '' : 'none';
8675     },
8676     
8677     
8678     getSelectionModel : function(){
8679         if(!this.selModel){
8680             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8681         }
8682         return this.selModel;
8683     },
8684     /*
8685      * Render the Roo.bootstrap object from renderder
8686      */
8687     renderCellObject : function(r)
8688     {
8689         var _this = this;
8690         
8691         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8692         
8693         var t = r.cfg.render(r.container);
8694         
8695         if(r.cfg.cn){
8696             Roo.each(r.cfg.cn, function(c){
8697                 var child = {
8698                     container: t.getChildContainer(),
8699                     cfg: c
8700                 };
8701                 _this.renderCellObject(child);
8702             })
8703         }
8704     },
8705     
8706     getRowIndex : function(row)
8707     {
8708         var rowIndex = -1;
8709         
8710         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8711             if(el != row){
8712                 return;
8713             }
8714             
8715             rowIndex = index;
8716         });
8717         
8718         return rowIndex;
8719     },
8720      /**
8721      * Returns the grid's underlying element = used by panel.Grid
8722      * @return {Element} The element
8723      */
8724     getGridEl : function(){
8725         return this.el;
8726     },
8727      /**
8728      * Forces a resize - used by panel.Grid
8729      * @return {Element} The element
8730      */
8731     autoSize : function()
8732     {
8733         //var ctr = Roo.get(this.container.dom.parentElement);
8734         var ctr = Roo.get(this.el.dom);
8735         
8736         var thd = this.getGridEl().select('thead',true).first();
8737         var tbd = this.getGridEl().select('tbody', true).first();
8738         var tfd = this.getGridEl().select('tfoot', true).first();
8739         
8740         var cw = ctr.getWidth();
8741         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8742         
8743         if (tbd) {
8744             
8745             tbd.setWidth(ctr.getWidth());
8746             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8747             // this needs fixing for various usage - currently only hydra job advers I think..
8748             //tdb.setHeight(
8749             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8750             //); 
8751             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8752             cw -= barsize;
8753         }
8754         cw = Math.max(cw, this.totalWidth);
8755         this.getGridEl().select('tbody tr',true).setWidth(cw);
8756         
8757         // resize 'expandable coloumn?
8758         
8759         return; // we doe not have a view in this design..
8760         
8761     },
8762     onBodyScroll: function()
8763     {
8764         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8765         if(this.mainHead){
8766             this.mainHead.setStyle({
8767                 'position' : 'relative',
8768                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8769             });
8770         }
8771         
8772         if(this.lazyLoad){
8773             
8774             var scrollHeight = this.mainBody.dom.scrollHeight;
8775             
8776             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8777             
8778             var height = this.mainBody.getHeight();
8779             
8780             if(scrollHeight - height == scrollTop) {
8781                 
8782                 var total = this.ds.getTotalCount();
8783                 
8784                 if(this.footer.cursor + this.footer.pageSize < total){
8785                     
8786                     this.footer.ds.load({
8787                         params : {
8788                             start : this.footer.cursor + this.footer.pageSize,
8789                             limit : this.footer.pageSize
8790                         },
8791                         add : true
8792                     });
8793                 }
8794             }
8795             
8796         }
8797     },
8798     
8799     onHeaderChange : function()
8800     {
8801         var header = this.renderHeader();
8802         var table = this.el.select('table', true).first();
8803         
8804         this.mainHead.remove();
8805         this.mainHead = table.createChild(header, this.mainBody, false);
8806     },
8807     
8808     onHiddenChange : function(colModel, colIndex, hidden)
8809     {
8810         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8811         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8812         
8813         this.CSS.updateRule(thSelector, "display", "");
8814         this.CSS.updateRule(tdSelector, "display", "");
8815         
8816         if(hidden){
8817             this.CSS.updateRule(thSelector, "display", "none");
8818             this.CSS.updateRule(tdSelector, "display", "none");
8819         }
8820         
8821         this.onHeaderChange();
8822         this.onLoad();
8823     },
8824     
8825     setColumnWidth: function(col_index, width)
8826     {
8827         // width = "md-2 xs-2..."
8828         if(!this.colModel.config[col_index]) {
8829             return;
8830         }
8831         
8832         var w = width.split(" ");
8833         
8834         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8835         
8836         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8837         
8838         
8839         for(var j = 0; j < w.length; j++) {
8840             
8841             if(!w[j]) {
8842                 continue;
8843             }
8844             
8845             var size_cls = w[j].split("-");
8846             
8847             if(!Number.isInteger(size_cls[1] * 1)) {
8848                 continue;
8849             }
8850             
8851             if(!this.colModel.config[col_index][size_cls[0]]) {
8852                 continue;
8853             }
8854             
8855             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8856                 continue;
8857             }
8858             
8859             h_row[0].classList.replace(
8860                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8861                 "col-"+size_cls[0]+"-"+size_cls[1]
8862             );
8863             
8864             for(var i = 0; i < rows.length; i++) {
8865                 
8866                 var size_cls = w[j].split("-");
8867                 
8868                 if(!Number.isInteger(size_cls[1] * 1)) {
8869                     continue;
8870                 }
8871                 
8872                 if(!this.colModel.config[col_index][size_cls[0]]) {
8873                     continue;
8874                 }
8875                 
8876                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8877                     continue;
8878                 }
8879                 
8880                 rows[i].classList.replace(
8881                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882                     "col-"+size_cls[0]+"-"+size_cls[1]
8883                 );
8884             }
8885             
8886             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8887         }
8888     }
8889 });
8890
8891  
8892
8893  /*
8894  * - LGPL
8895  *
8896  * table cell
8897  * 
8898  */
8899
8900 /**
8901  * @class Roo.bootstrap.TableCell
8902  * @extends Roo.bootstrap.Component
8903  * Bootstrap TableCell class
8904  * @cfg {String} html cell contain text
8905  * @cfg {String} cls cell class
8906  * @cfg {String} tag cell tag (td|th) default td
8907  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8908  * @cfg {String} align Aligns the content in a cell
8909  * @cfg {String} axis Categorizes cells
8910  * @cfg {String} bgcolor Specifies the background color of a cell
8911  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8912  * @cfg {Number} colspan Specifies the number of columns a cell should span
8913  * @cfg {String} headers Specifies one or more header cells a cell is related to
8914  * @cfg {Number} height Sets the height of a cell
8915  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8916  * @cfg {Number} rowspan Sets the number of rows a cell should span
8917  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8918  * @cfg {String} valign Vertical aligns the content in a cell
8919  * @cfg {Number} width Specifies the width of a cell
8920  * 
8921  * @constructor
8922  * Create a new TableCell
8923  * @param {Object} config The config object
8924  */
8925
8926 Roo.bootstrap.TableCell = function(config){
8927     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8928 };
8929
8930 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8931     
8932     html: false,
8933     cls: false,
8934     tag: false,
8935     abbr: false,
8936     align: false,
8937     axis: false,
8938     bgcolor: false,
8939     charoff: false,
8940     colspan: false,
8941     headers: false,
8942     height: false,
8943     nowrap: false,
8944     rowspan: false,
8945     scope: false,
8946     valign: false,
8947     width: false,
8948     
8949     
8950     getAutoCreate : function(){
8951         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8952         
8953         cfg = {
8954             tag: 'td'
8955         };
8956         
8957         if(this.tag){
8958             cfg.tag = this.tag;
8959         }
8960         
8961         if (this.html) {
8962             cfg.html=this.html
8963         }
8964         if (this.cls) {
8965             cfg.cls=this.cls
8966         }
8967         if (this.abbr) {
8968             cfg.abbr=this.abbr
8969         }
8970         if (this.align) {
8971             cfg.align=this.align
8972         }
8973         if (this.axis) {
8974             cfg.axis=this.axis
8975         }
8976         if (this.bgcolor) {
8977             cfg.bgcolor=this.bgcolor
8978         }
8979         if (this.charoff) {
8980             cfg.charoff=this.charoff
8981         }
8982         if (this.colspan) {
8983             cfg.colspan=this.colspan
8984         }
8985         if (this.headers) {
8986             cfg.headers=this.headers
8987         }
8988         if (this.height) {
8989             cfg.height=this.height
8990         }
8991         if (this.nowrap) {
8992             cfg.nowrap=this.nowrap
8993         }
8994         if (this.rowspan) {
8995             cfg.rowspan=this.rowspan
8996         }
8997         if (this.scope) {
8998             cfg.scope=this.scope
8999         }
9000         if (this.valign) {
9001             cfg.valign=this.valign
9002         }
9003         if (this.width) {
9004             cfg.width=this.width
9005         }
9006         
9007         
9008         return cfg;
9009     }
9010    
9011 });
9012
9013  
9014
9015  /*
9016  * - LGPL
9017  *
9018  * table row
9019  * 
9020  */
9021
9022 /**
9023  * @class Roo.bootstrap.TableRow
9024  * @extends Roo.bootstrap.Component
9025  * Bootstrap TableRow class
9026  * @cfg {String} cls row class
9027  * @cfg {String} align Aligns the content in a table row
9028  * @cfg {String} bgcolor Specifies a background color for a table row
9029  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9030  * @cfg {String} valign Vertical aligns the content in a table row
9031  * 
9032  * @constructor
9033  * Create a new TableRow
9034  * @param {Object} config The config object
9035  */
9036
9037 Roo.bootstrap.TableRow = function(config){
9038     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9039 };
9040
9041 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9042     
9043     cls: false,
9044     align: false,
9045     bgcolor: false,
9046     charoff: false,
9047     valign: false,
9048     
9049     getAutoCreate : function(){
9050         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9051         
9052         cfg = {
9053             tag: 'tr'
9054         };
9055             
9056         if(this.cls){
9057             cfg.cls = this.cls;
9058         }
9059         if(this.align){
9060             cfg.align = this.align;
9061         }
9062         if(this.bgcolor){
9063             cfg.bgcolor = this.bgcolor;
9064         }
9065         if(this.charoff){
9066             cfg.charoff = this.charoff;
9067         }
9068         if(this.valign){
9069             cfg.valign = this.valign;
9070         }
9071         
9072         return cfg;
9073     }
9074    
9075 });
9076
9077  
9078
9079  /*
9080  * - LGPL
9081  *
9082  * table body
9083  * 
9084  */
9085
9086 /**
9087  * @class Roo.bootstrap.TableBody
9088  * @extends Roo.bootstrap.Component
9089  * Bootstrap TableBody class
9090  * @cfg {String} cls element class
9091  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9092  * @cfg {String} align Aligns the content inside the element
9093  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9094  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9095  * 
9096  * @constructor
9097  * Create a new TableBody
9098  * @param {Object} config The config object
9099  */
9100
9101 Roo.bootstrap.TableBody = function(config){
9102     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9103 };
9104
9105 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9106     
9107     cls: false,
9108     tag: false,
9109     align: false,
9110     charoff: false,
9111     valign: false,
9112     
9113     getAutoCreate : function(){
9114         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9115         
9116         cfg = {
9117             tag: 'tbody'
9118         };
9119             
9120         if (this.cls) {
9121             cfg.cls=this.cls
9122         }
9123         if(this.tag){
9124             cfg.tag = this.tag;
9125         }
9126         
9127         if(this.align){
9128             cfg.align = this.align;
9129         }
9130         if(this.charoff){
9131             cfg.charoff = this.charoff;
9132         }
9133         if(this.valign){
9134             cfg.valign = this.valign;
9135         }
9136         
9137         return cfg;
9138     }
9139     
9140     
9141 //    initEvents : function()
9142 //    {
9143 //        
9144 //        if(!this.store){
9145 //            return;
9146 //        }
9147 //        
9148 //        this.store = Roo.factory(this.store, Roo.data);
9149 //        this.store.on('load', this.onLoad, this);
9150 //        
9151 //        this.store.load();
9152 //        
9153 //    },
9154 //    
9155 //    onLoad: function () 
9156 //    {   
9157 //        this.fireEvent('load', this);
9158 //    }
9159 //    
9160 //   
9161 });
9162
9163  
9164
9165  /*
9166  * Based on:
9167  * Ext JS Library 1.1.1
9168  * Copyright(c) 2006-2007, Ext JS, LLC.
9169  *
9170  * Originally Released Under LGPL - original licence link has changed is not relivant.
9171  *
9172  * Fork - LGPL
9173  * <script type="text/javascript">
9174  */
9175
9176 // as we use this in bootstrap.
9177 Roo.namespace('Roo.form');
9178  /**
9179  * @class Roo.form.Action
9180  * Internal Class used to handle form actions
9181  * @constructor
9182  * @param {Roo.form.BasicForm} el The form element or its id
9183  * @param {Object} config Configuration options
9184  */
9185
9186  
9187  
9188 // define the action interface
9189 Roo.form.Action = function(form, options){
9190     this.form = form;
9191     this.options = options || {};
9192 };
9193 /**
9194  * Client Validation Failed
9195  * @const 
9196  */
9197 Roo.form.Action.CLIENT_INVALID = 'client';
9198 /**
9199  * Server Validation Failed
9200  * @const 
9201  */
9202 Roo.form.Action.SERVER_INVALID = 'server';
9203  /**
9204  * Connect to Server Failed
9205  * @const 
9206  */
9207 Roo.form.Action.CONNECT_FAILURE = 'connect';
9208 /**
9209  * Reading Data from Server Failed
9210  * @const 
9211  */
9212 Roo.form.Action.LOAD_FAILURE = 'load';
9213
9214 Roo.form.Action.prototype = {
9215     type : 'default',
9216     failureType : undefined,
9217     response : undefined,
9218     result : undefined,
9219
9220     // interface method
9221     run : function(options){
9222
9223     },
9224
9225     // interface method
9226     success : function(response){
9227
9228     },
9229
9230     // interface method
9231     handleResponse : function(response){
9232
9233     },
9234
9235     // default connection failure
9236     failure : function(response){
9237         
9238         this.response = response;
9239         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9240         this.form.afterAction(this, false);
9241     },
9242
9243     processResponse : function(response){
9244         this.response = response;
9245         if(!response.responseText){
9246             return true;
9247         }
9248         this.result = this.handleResponse(response);
9249         return this.result;
9250     },
9251
9252     // utility functions used internally
9253     getUrl : function(appendParams){
9254         var url = this.options.url || this.form.url || this.form.el.dom.action;
9255         if(appendParams){
9256             var p = this.getParams();
9257             if(p){
9258                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9259             }
9260         }
9261         return url;
9262     },
9263
9264     getMethod : function(){
9265         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9266     },
9267
9268     getParams : function(){
9269         var bp = this.form.baseParams;
9270         var p = this.options.params;
9271         if(p){
9272             if(typeof p == "object"){
9273                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9274             }else if(typeof p == 'string' && bp){
9275                 p += '&' + Roo.urlEncode(bp);
9276             }
9277         }else if(bp){
9278             p = Roo.urlEncode(bp);
9279         }
9280         return p;
9281     },
9282
9283     createCallback : function(){
9284         return {
9285             success: this.success,
9286             failure: this.failure,
9287             scope: this,
9288             timeout: (this.form.timeout*1000),
9289             upload: this.form.fileUpload ? this.success : undefined
9290         };
9291     }
9292 };
9293
9294 Roo.form.Action.Submit = function(form, options){
9295     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9296 };
9297
9298 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9299     type : 'submit',
9300
9301     haveProgress : false,
9302     uploadComplete : false,
9303     
9304     // uploadProgress indicator.
9305     uploadProgress : function()
9306     {
9307         if (!this.form.progressUrl) {
9308             return;
9309         }
9310         
9311         if (!this.haveProgress) {
9312             Roo.MessageBox.progress("Uploading", "Uploading");
9313         }
9314         if (this.uploadComplete) {
9315            Roo.MessageBox.hide();
9316            return;
9317         }
9318         
9319         this.haveProgress = true;
9320    
9321         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9322         
9323         var c = new Roo.data.Connection();
9324         c.request({
9325             url : this.form.progressUrl,
9326             params: {
9327                 id : uid
9328             },
9329             method: 'GET',
9330             success : function(req){
9331                //console.log(data);
9332                 var rdata = false;
9333                 var edata;
9334                 try  {
9335                    rdata = Roo.decode(req.responseText)
9336                 } catch (e) {
9337                     Roo.log("Invalid data from server..");
9338                     Roo.log(edata);
9339                     return;
9340                 }
9341                 if (!rdata || !rdata.success) {
9342                     Roo.log(rdata);
9343                     Roo.MessageBox.alert(Roo.encode(rdata));
9344                     return;
9345                 }
9346                 var data = rdata.data;
9347                 
9348                 if (this.uploadComplete) {
9349                    Roo.MessageBox.hide();
9350                    return;
9351                 }
9352                    
9353                 if (data){
9354                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9355                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9356                     );
9357                 }
9358                 this.uploadProgress.defer(2000,this);
9359             },
9360        
9361             failure: function(data) {
9362                 Roo.log('progress url failed ');
9363                 Roo.log(data);
9364             },
9365             scope : this
9366         });
9367            
9368     },
9369     
9370     
9371     run : function()
9372     {
9373         // run get Values on the form, so it syncs any secondary forms.
9374         this.form.getValues();
9375         
9376         var o = this.options;
9377         var method = this.getMethod();
9378         var isPost = method == 'POST';
9379         if(o.clientValidation === false || this.form.isValid()){
9380             
9381             if (this.form.progressUrl) {
9382                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9383                     (new Date() * 1) + '' + Math.random());
9384                     
9385             } 
9386             
9387             
9388             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9389                 form:this.form.el.dom,
9390                 url:this.getUrl(!isPost),
9391                 method: method,
9392                 params:isPost ? this.getParams() : null,
9393                 isUpload: this.form.fileUpload,
9394                 formData : this.form.formData
9395             }));
9396             
9397             this.uploadProgress();
9398
9399         }else if (o.clientValidation !== false){ // client validation failed
9400             this.failureType = Roo.form.Action.CLIENT_INVALID;
9401             this.form.afterAction(this, false);
9402         }
9403     },
9404
9405     success : function(response)
9406     {
9407         this.uploadComplete= true;
9408         if (this.haveProgress) {
9409             Roo.MessageBox.hide();
9410         }
9411         
9412         
9413         var result = this.processResponse(response);
9414         if(result === true || result.success){
9415             this.form.afterAction(this, true);
9416             return;
9417         }
9418         if(result.errors){
9419             this.form.markInvalid(result.errors);
9420             this.failureType = Roo.form.Action.SERVER_INVALID;
9421         }
9422         this.form.afterAction(this, false);
9423     },
9424     failure : function(response)
9425     {
9426         this.uploadComplete= true;
9427         if (this.haveProgress) {
9428             Roo.MessageBox.hide();
9429         }
9430         
9431         this.response = response;
9432         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9433         this.form.afterAction(this, false);
9434     },
9435     
9436     handleResponse : function(response){
9437         if(this.form.errorReader){
9438             var rs = this.form.errorReader.read(response);
9439             var errors = [];
9440             if(rs.records){
9441                 for(var i = 0, len = rs.records.length; i < len; i++) {
9442                     var r = rs.records[i];
9443                     errors[i] = r.data;
9444                 }
9445             }
9446             if(errors.length < 1){
9447                 errors = null;
9448             }
9449             return {
9450                 success : rs.success,
9451                 errors : errors
9452             };
9453         }
9454         var ret = false;
9455         try {
9456             ret = Roo.decode(response.responseText);
9457         } catch (e) {
9458             ret = {
9459                 success: false,
9460                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9461                 errors : []
9462             };
9463         }
9464         return ret;
9465         
9466     }
9467 });
9468
9469
9470 Roo.form.Action.Load = function(form, options){
9471     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9472     this.reader = this.form.reader;
9473 };
9474
9475 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9476     type : 'load',
9477
9478     run : function(){
9479         
9480         Roo.Ajax.request(Roo.apply(
9481                 this.createCallback(), {
9482                     method:this.getMethod(),
9483                     url:this.getUrl(false),
9484                     params:this.getParams()
9485         }));
9486     },
9487
9488     success : function(response){
9489         
9490         var result = this.processResponse(response);
9491         if(result === true || !result.success || !result.data){
9492             this.failureType = Roo.form.Action.LOAD_FAILURE;
9493             this.form.afterAction(this, false);
9494             return;
9495         }
9496         this.form.clearInvalid();
9497         this.form.setValues(result.data);
9498         this.form.afterAction(this, true);
9499     },
9500
9501     handleResponse : function(response){
9502         if(this.form.reader){
9503             var rs = this.form.reader.read(response);
9504             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9505             return {
9506                 success : rs.success,
9507                 data : data
9508             };
9509         }
9510         return Roo.decode(response.responseText);
9511     }
9512 });
9513
9514 Roo.form.Action.ACTION_TYPES = {
9515     'load' : Roo.form.Action.Load,
9516     'submit' : Roo.form.Action.Submit
9517 };/*
9518  * - LGPL
9519  *
9520  * form
9521  *
9522  */
9523
9524 /**
9525  * @class Roo.bootstrap.Form
9526  * @extends Roo.bootstrap.Component
9527  * Bootstrap Form class
9528  * @cfg {String} method  GET | POST (default POST)
9529  * @cfg {String} labelAlign top | left (default top)
9530  * @cfg {String} align left  | right - for navbars
9531  * @cfg {Boolean} loadMask load mask when submit (default true)
9532
9533  *
9534  * @constructor
9535  * Create a new Form
9536  * @param {Object} config The config object
9537  */
9538
9539
9540 Roo.bootstrap.Form = function(config){
9541     
9542     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9543     
9544     Roo.bootstrap.Form.popover.apply();
9545     
9546     this.addEvents({
9547         /**
9548          * @event clientvalidation
9549          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9550          * @param {Form} this
9551          * @param {Boolean} valid true if the form has passed client-side validation
9552          */
9553         clientvalidation: true,
9554         /**
9555          * @event beforeaction
9556          * Fires before any action is performed. Return false to cancel the action.
9557          * @param {Form} this
9558          * @param {Action} action The action to be performed
9559          */
9560         beforeaction: true,
9561         /**
9562          * @event actionfailed
9563          * Fires when an action fails.
9564          * @param {Form} this
9565          * @param {Action} action The action that failed
9566          */
9567         actionfailed : true,
9568         /**
9569          * @event actioncomplete
9570          * Fires when an action is completed.
9571          * @param {Form} this
9572          * @param {Action} action The action that completed
9573          */
9574         actioncomplete : true
9575     });
9576 };
9577
9578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9579
9580      /**
9581      * @cfg {String} method
9582      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9583      */
9584     method : 'POST',
9585     /**
9586      * @cfg {String} url
9587      * The URL to use for form actions if one isn't supplied in the action options.
9588      */
9589     /**
9590      * @cfg {Boolean} fileUpload
9591      * Set to true if this form is a file upload.
9592      */
9593
9594     /**
9595      * @cfg {Object} baseParams
9596      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9597      */
9598
9599     /**
9600      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9601      */
9602     timeout: 30,
9603     /**
9604      * @cfg {Sting} align (left|right) for navbar forms
9605      */
9606     align : 'left',
9607
9608     // private
9609     activeAction : null,
9610
9611     /**
9612      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9613      * element by passing it or its id or mask the form itself by passing in true.
9614      * @type Mixed
9615      */
9616     waitMsgTarget : false,
9617
9618     loadMask : true,
9619     
9620     /**
9621      * @cfg {Boolean} errorMask (true|false) default false
9622      */
9623     errorMask : false,
9624     
9625     /**
9626      * @cfg {Number} maskOffset Default 100
9627      */
9628     maskOffset : 100,
9629     
9630     /**
9631      * @cfg {Boolean} maskBody
9632      */
9633     maskBody : false,
9634
9635     getAutoCreate : function(){
9636
9637         var cfg = {
9638             tag: 'form',
9639             method : this.method || 'POST',
9640             id : this.id || Roo.id(),
9641             cls : ''
9642         };
9643         if (this.parent().xtype.match(/^Nav/)) {
9644             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9645
9646         }
9647
9648         if (this.labelAlign == 'left' ) {
9649             cfg.cls += ' form-horizontal';
9650         }
9651
9652
9653         return cfg;
9654     },
9655     initEvents : function()
9656     {
9657         this.el.on('submit', this.onSubmit, this);
9658         // this was added as random key presses on the form where triggering form submit.
9659         this.el.on('keypress', function(e) {
9660             if (e.getCharCode() != 13) {
9661                 return true;
9662             }
9663             // we might need to allow it for textareas.. and some other items.
9664             // check e.getTarget().
9665
9666             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9667                 return true;
9668             }
9669
9670             Roo.log("keypress blocked");
9671
9672             e.preventDefault();
9673             return false;
9674         });
9675         
9676     },
9677     // private
9678     onSubmit : function(e){
9679         e.stopEvent();
9680     },
9681
9682      /**
9683      * Returns true if client-side validation on the form is successful.
9684      * @return Boolean
9685      */
9686     isValid : function(){
9687         var items = this.getItems();
9688         var valid = true;
9689         var target = false;
9690         
9691         items.each(function(f){
9692             
9693             if(f.validate()){
9694                 return;
9695             }
9696             
9697             Roo.log('invalid field: ' + f.name);
9698             
9699             valid = false;
9700
9701             if(!target && f.el.isVisible(true)){
9702                 target = f;
9703             }
9704            
9705         });
9706         
9707         if(this.errorMask && !valid){
9708             Roo.bootstrap.Form.popover.mask(this, target);
9709         }
9710         
9711         return valid;
9712     },
9713     
9714     /**
9715      * Returns true if any fields in this form have changed since their original load.
9716      * @return Boolean
9717      */
9718     isDirty : function(){
9719         var dirty = false;
9720         var items = this.getItems();
9721         items.each(function(f){
9722            if(f.isDirty()){
9723                dirty = true;
9724                return false;
9725            }
9726            return true;
9727         });
9728         return dirty;
9729     },
9730      /**
9731      * Performs a predefined action (submit or load) or custom actions you define on this form.
9732      * @param {String} actionName The name of the action type
9733      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9734      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9735      * accept other config options):
9736      * <pre>
9737 Property          Type             Description
9738 ----------------  ---------------  ----------------------------------------------------------------------------------
9739 url               String           The url for the action (defaults to the form's url)
9740 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9741 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9742 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9743                                    validate the form on the client (defaults to false)
9744      * </pre>
9745      * @return {BasicForm} this
9746      */
9747     doAction : function(action, options){
9748         if(typeof action == 'string'){
9749             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9750         }
9751         if(this.fireEvent('beforeaction', this, action) !== false){
9752             this.beforeAction(action);
9753             action.run.defer(100, action);
9754         }
9755         return this;
9756     },
9757
9758     // private
9759     beforeAction : function(action){
9760         var o = action.options;
9761         
9762         if(this.loadMask){
9763             
9764             if(this.maskBody){
9765                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9766             } else {
9767                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9768             }
9769         }
9770         // not really supported yet.. ??
9771
9772         //if(this.waitMsgTarget === true){
9773         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else if(this.waitMsgTarget){
9775         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9776         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9777         //}else {
9778         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9779        // }
9780
9781     },
9782
9783     // private
9784     afterAction : function(action, success){
9785         this.activeAction = null;
9786         var o = action.options;
9787
9788         if(this.loadMask){
9789             
9790             if(this.maskBody){
9791                 Roo.get(document.body).unmask();
9792             } else {
9793                 this.el.unmask();
9794             }
9795         }
9796         
9797         //if(this.waitMsgTarget === true){
9798 //            this.el.unmask();
9799         //}else if(this.waitMsgTarget){
9800         //    this.waitMsgTarget.unmask();
9801         //}else{
9802         //    Roo.MessageBox.updateProgress(1);
9803         //    Roo.MessageBox.hide();
9804        // }
9805         //
9806         if(success){
9807             if(o.reset){
9808                 this.reset();
9809             }
9810             Roo.callback(o.success, o.scope, [this, action]);
9811             this.fireEvent('actioncomplete', this, action);
9812
9813         }else{
9814
9815             // failure condition..
9816             // we have a scenario where updates need confirming.
9817             // eg. if a locking scenario exists..
9818             // we look for { errors : { needs_confirm : true }} in the response.
9819             if (
9820                 (typeof(action.result) != 'undefined')  &&
9821                 (typeof(action.result.errors) != 'undefined')  &&
9822                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9823            ){
9824                 var _t = this;
9825                 Roo.log("not supported yet");
9826                  /*
9827
9828                 Roo.MessageBox.confirm(
9829                     "Change requires confirmation",
9830                     action.result.errorMsg,
9831                     function(r) {
9832                         if (r != 'yes') {
9833                             return;
9834                         }
9835                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9836                     }
9837
9838                 );
9839                 */
9840
9841
9842                 return;
9843             }
9844
9845             Roo.callback(o.failure, o.scope, [this, action]);
9846             // show an error message if no failed handler is set..
9847             if (!this.hasListener('actionfailed')) {
9848                 Roo.log("need to add dialog support");
9849                 /*
9850                 Roo.MessageBox.alert("Error",
9851                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9852                         action.result.errorMsg :
9853                         "Saving Failed, please check your entries or try again"
9854                 );
9855                 */
9856             }
9857
9858             this.fireEvent('actionfailed', this, action);
9859         }
9860
9861     },
9862     /**
9863      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9864      * @param {String} id The value to search for
9865      * @return Field
9866      */
9867     findField : function(id){
9868         var items = this.getItems();
9869         var field = items.get(id);
9870         if(!field){
9871              items.each(function(f){
9872                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9873                     field = f;
9874                     return false;
9875                 }
9876                 return true;
9877             });
9878         }
9879         return field || null;
9880     },
9881      /**
9882      * Mark fields in this form invalid in bulk.
9883      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9884      * @return {BasicForm} this
9885      */
9886     markInvalid : function(errors){
9887         if(errors instanceof Array){
9888             for(var i = 0, len = errors.length; i < len; i++){
9889                 var fieldError = errors[i];
9890                 var f = this.findField(fieldError.id);
9891                 if(f){
9892                     f.markInvalid(fieldError.msg);
9893                 }
9894             }
9895         }else{
9896             var field, id;
9897             for(id in errors){
9898                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9899                     field.markInvalid(errors[id]);
9900                 }
9901             }
9902         }
9903         //Roo.each(this.childForms || [], function (f) {
9904         //    f.markInvalid(errors);
9905         //});
9906
9907         return this;
9908     },
9909
9910     /**
9911      * Set values for fields in this form in bulk.
9912      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9913      * @return {BasicForm} this
9914      */
9915     setValues : function(values){
9916         if(values instanceof Array){ // array of objects
9917             for(var i = 0, len = values.length; i < len; i++){
9918                 var v = values[i];
9919                 var f = this.findField(v.id);
9920                 if(f){
9921                     f.setValue(v.value);
9922                     if(this.trackResetOnLoad){
9923                         f.originalValue = f.getValue();
9924                     }
9925                 }
9926             }
9927         }else{ // object hash
9928             var field, id;
9929             for(id in values){
9930                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9931
9932                     if (field.setFromData &&
9933                         field.valueField &&
9934                         field.displayField &&
9935                         // combos' with local stores can
9936                         // be queried via setValue()
9937                         // to set their value..
9938                         (field.store && !field.store.isLocal)
9939                         ) {
9940                         // it's a combo
9941                         var sd = { };
9942                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9943                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9944                         field.setFromData(sd);
9945
9946                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9947                         
9948                         field.setFromData(values);
9949                         
9950                     } else {
9951                         field.setValue(values[id]);
9952                     }
9953
9954
9955                     if(this.trackResetOnLoad){
9956                         field.originalValue = field.getValue();
9957                     }
9958                 }
9959             }
9960         }
9961
9962         //Roo.each(this.childForms || [], function (f) {
9963         //    f.setValues(values);
9964         //});
9965
9966         return this;
9967     },
9968
9969     /**
9970      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9971      * they are returned as an array.
9972      * @param {Boolean} asString
9973      * @return {Object}
9974      */
9975     getValues : function(asString){
9976         //if (this.childForms) {
9977             // copy values from the child forms
9978         //    Roo.each(this.childForms, function (f) {
9979         //        this.setValues(f.getValues());
9980         //    }, this);
9981         //}
9982
9983
9984
9985         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9986         if(asString === true){
9987             return fs;
9988         }
9989         return Roo.urlDecode(fs);
9990     },
9991
9992     /**
9993      * Returns the fields in this form as an object with key/value pairs.
9994      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9995      * @return {Object}
9996      */
9997     getFieldValues : function(with_hidden)
9998     {
9999         var items = this.getItems();
10000         var ret = {};
10001         items.each(function(f){
10002             
10003             if (!f.getName()) {
10004                 return;
10005             }
10006             
10007             var v = f.getValue();
10008             
10009             if (f.inputType =='radio') {
10010                 if (typeof(ret[f.getName()]) == 'undefined') {
10011                     ret[f.getName()] = ''; // empty..
10012                 }
10013
10014                 if (!f.el.dom.checked) {
10015                     return;
10016
10017                 }
10018                 v = f.el.dom.value;
10019
10020             }
10021             
10022             if(f.xtype == 'MoneyField'){
10023                 ret[f.currencyName] = f.getCurrency();
10024             }
10025
10026             // not sure if this supported any more..
10027             if ((typeof(v) == 'object') && f.getRawValue) {
10028                 v = f.getRawValue() ; // dates..
10029             }
10030             // combo boxes where name != hiddenName...
10031             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10032                 ret[f.name] = f.getRawValue();
10033             }
10034             ret[f.getName()] = v;
10035         });
10036
10037         return ret;
10038     },
10039
10040     /**
10041      * Clears all invalid messages in this form.
10042      * @return {BasicForm} this
10043      */
10044     clearInvalid : function(){
10045         var items = this.getItems();
10046
10047         items.each(function(f){
10048            f.clearInvalid();
10049         });
10050
10051         return this;
10052     },
10053
10054     /**
10055      * Resets this form.
10056      * @return {BasicForm} this
10057      */
10058     reset : function(){
10059         var items = this.getItems();
10060         items.each(function(f){
10061             f.reset();
10062         });
10063
10064         Roo.each(this.childForms || [], function (f) {
10065             f.reset();
10066         });
10067
10068
10069         return this;
10070     },
10071     
10072     getItems : function()
10073     {
10074         var r=new Roo.util.MixedCollection(false, function(o){
10075             return o.id || (o.id = Roo.id());
10076         });
10077         var iter = function(el) {
10078             if (el.inputEl) {
10079                 r.add(el);
10080             }
10081             if (!el.items) {
10082                 return;
10083             }
10084             Roo.each(el.items,function(e) {
10085                 iter(e);
10086             });
10087         };
10088
10089         iter(this);
10090         return r;
10091     },
10092     
10093     hideFields : function(items)
10094     {
10095         Roo.each(items, function(i){
10096             
10097             var f = this.findField(i);
10098             
10099             if(!f){
10100                 return;
10101             }
10102             
10103             f.hide();
10104             
10105         }, this);
10106     },
10107     
10108     showFields : function(items)
10109     {
10110         Roo.each(items, function(i){
10111             
10112             var f = this.findField(i);
10113             
10114             if(!f){
10115                 return;
10116             }
10117             
10118             f.show();
10119             
10120         }, this);
10121     }
10122
10123 });
10124
10125 Roo.apply(Roo.bootstrap.Form, {
10126     
10127     popover : {
10128         
10129         padding : 5,
10130         
10131         isApplied : false,
10132         
10133         isMasked : false,
10134         
10135         form : false,
10136         
10137         target : false,
10138         
10139         toolTip : false,
10140         
10141         intervalID : false,
10142         
10143         maskEl : false,
10144         
10145         apply : function()
10146         {
10147             if(this.isApplied){
10148                 return;
10149             }
10150             
10151             this.maskEl = {
10152                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10153                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10154                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10155                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10156             };
10157             
10158             this.maskEl.top.enableDisplayMode("block");
10159             this.maskEl.left.enableDisplayMode("block");
10160             this.maskEl.bottom.enableDisplayMode("block");
10161             this.maskEl.right.enableDisplayMode("block");
10162             
10163             this.toolTip = new Roo.bootstrap.Tooltip({
10164                 cls : 'roo-form-error-popover',
10165                 alignment : {
10166                     'left' : ['r-l', [-2,0], 'right'],
10167                     'right' : ['l-r', [2,0], 'left'],
10168                     'bottom' : ['tl-bl', [0,2], 'top'],
10169                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10170                 }
10171             });
10172             
10173             this.toolTip.render(Roo.get(document.body));
10174
10175             this.toolTip.el.enableDisplayMode("block");
10176             
10177             Roo.get(document.body).on('click', function(){
10178                 this.unmask();
10179             }, this);
10180             
10181             Roo.get(document.body).on('touchstart', function(){
10182                 this.unmask();
10183             }, this);
10184             
10185             this.isApplied = true
10186         },
10187         
10188         mask : function(form, target)
10189         {
10190             this.form = form;
10191             
10192             this.target = target;
10193             
10194             if(!this.form.errorMask || !target.el){
10195                 return;
10196             }
10197             
10198             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10199             
10200             Roo.log(scrollable);
10201             
10202             var ot = this.target.el.calcOffsetsTo(scrollable);
10203             
10204             var scrollTo = ot[1] - this.form.maskOffset;
10205             
10206             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10207             
10208             scrollable.scrollTo('top', scrollTo);
10209             
10210             var box = this.target.el.getBox();
10211             Roo.log(box);
10212             var zIndex = Roo.bootstrap.Modal.zIndex++;
10213
10214             
10215             this.maskEl.top.setStyle('position', 'absolute');
10216             this.maskEl.top.setStyle('z-index', zIndex);
10217             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10218             this.maskEl.top.setLeft(0);
10219             this.maskEl.top.setTop(0);
10220             this.maskEl.top.show();
10221             
10222             this.maskEl.left.setStyle('position', 'absolute');
10223             this.maskEl.left.setStyle('z-index', zIndex);
10224             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10225             this.maskEl.left.setLeft(0);
10226             this.maskEl.left.setTop(box.y - this.padding);
10227             this.maskEl.left.show();
10228
10229             this.maskEl.bottom.setStyle('position', 'absolute');
10230             this.maskEl.bottom.setStyle('z-index', zIndex);
10231             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10232             this.maskEl.bottom.setLeft(0);
10233             this.maskEl.bottom.setTop(box.bottom + this.padding);
10234             this.maskEl.bottom.show();
10235
10236             this.maskEl.right.setStyle('position', 'absolute');
10237             this.maskEl.right.setStyle('z-index', zIndex);
10238             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10239             this.maskEl.right.setLeft(box.right + this.padding);
10240             this.maskEl.right.setTop(box.y - this.padding);
10241             this.maskEl.right.show();
10242
10243             this.toolTip.bindEl = this.target.el;
10244
10245             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10246
10247             var tip = this.target.blankText;
10248
10249             if(this.target.getValue() !== '' ) {
10250                 
10251                 if (this.target.invalidText.length) {
10252                     tip = this.target.invalidText;
10253                 } else if (this.target.regexText.length){
10254                     tip = this.target.regexText;
10255                 }
10256             }
10257
10258             this.toolTip.show(tip);
10259
10260             this.intervalID = window.setInterval(function() {
10261                 Roo.bootstrap.Form.popover.unmask();
10262             }, 10000);
10263
10264             window.onwheel = function(){ return false;};
10265             
10266             (function(){ this.isMasked = true; }).defer(500, this);
10267             
10268         },
10269         
10270         unmask : function()
10271         {
10272             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10273                 return;
10274             }
10275             
10276             this.maskEl.top.setStyle('position', 'absolute');
10277             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.top.hide();
10279
10280             this.maskEl.left.setStyle('position', 'absolute');
10281             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.left.hide();
10283
10284             this.maskEl.bottom.setStyle('position', 'absolute');
10285             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10286             this.maskEl.bottom.hide();
10287
10288             this.maskEl.right.setStyle('position', 'absolute');
10289             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10290             this.maskEl.right.hide();
10291             
10292             this.toolTip.hide();
10293             
10294             this.toolTip.el.hide();
10295             
10296             window.onwheel = function(){ return true;};
10297             
10298             if(this.intervalID){
10299                 window.clearInterval(this.intervalID);
10300                 this.intervalID = false;
10301             }
10302             
10303             this.isMasked = false;
10304             
10305         }
10306         
10307     }
10308     
10309 });
10310
10311 /*
10312  * Based on:
10313  * Ext JS Library 1.1.1
10314  * Copyright(c) 2006-2007, Ext JS, LLC.
10315  *
10316  * Originally Released Under LGPL - original licence link has changed is not relivant.
10317  *
10318  * Fork - LGPL
10319  * <script type="text/javascript">
10320  */
10321 /**
10322  * @class Roo.form.VTypes
10323  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10324  * @singleton
10325  */
10326 Roo.form.VTypes = function(){
10327     // closure these in so they are only created once.
10328     var alpha = /^[a-zA-Z_]+$/;
10329     var alphanum = /^[a-zA-Z0-9_]+$/;
10330     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10331     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10332
10333     // All these messages and functions are configurable
10334     return {
10335         /**
10336          * The function used to validate email addresses
10337          * @param {String} value The email address
10338          */
10339         'email' : function(v){
10340             return email.test(v);
10341         },
10342         /**
10343          * The error text to display when the email validation function returns false
10344          * @type String
10345          */
10346         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10347         /**
10348          * The keystroke filter mask to be applied on email input
10349          * @type RegExp
10350          */
10351         'emailMask' : /[a-z0-9_\.\-@]/i,
10352
10353         /**
10354          * The function used to validate URLs
10355          * @param {String} value The URL
10356          */
10357         'url' : function(v){
10358             return url.test(v);
10359         },
10360         /**
10361          * The error text to display when the url validation function returns false
10362          * @type String
10363          */
10364         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10365         
10366         /**
10367          * The function used to validate alpha values
10368          * @param {String} value The value
10369          */
10370         'alpha' : function(v){
10371             return alpha.test(v);
10372         },
10373         /**
10374          * The error text to display when the alpha validation function returns false
10375          * @type String
10376          */
10377         'alphaText' : 'This field should only contain letters and _',
10378         /**
10379          * The keystroke filter mask to be applied on alpha input
10380          * @type RegExp
10381          */
10382         'alphaMask' : /[a-z_]/i,
10383
10384         /**
10385          * The function used to validate alphanumeric values
10386          * @param {String} value The value
10387          */
10388         'alphanum' : function(v){
10389             return alphanum.test(v);
10390         },
10391         /**
10392          * The error text to display when the alphanumeric validation function returns false
10393          * @type String
10394          */
10395         'alphanumText' : 'This field should only contain letters, numbers and _',
10396         /**
10397          * The keystroke filter mask to be applied on alphanumeric input
10398          * @type RegExp
10399          */
10400         'alphanumMask' : /[a-z0-9_]/i
10401     };
10402 }();/*
10403  * - LGPL
10404  *
10405  * Input
10406  * 
10407  */
10408
10409 /**
10410  * @class Roo.bootstrap.Input
10411  * @extends Roo.bootstrap.Component
10412  * Bootstrap Input class
10413  * @cfg {Boolean} disabled is it disabled
10414  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10415  * @cfg {String} name name of the input
10416  * @cfg {string} fieldLabel - the label associated
10417  * @cfg {string} placeholder - placeholder to put in text.
10418  * @cfg {string}  before - input group add on before
10419  * @cfg {string} after - input group add on after
10420  * @cfg {string} size - (lg|sm) or leave empty..
10421  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10422  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10423  * @cfg {Number} md colspan out of 12 for computer-sized screens
10424  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10425  * @cfg {string} value default value of the input
10426  * @cfg {Number} labelWidth set the width of label 
10427  * @cfg {Number} labellg set the width of label (1-12)
10428  * @cfg {Number} labelmd set the width of label (1-12)
10429  * @cfg {Number} labelsm set the width of label (1-12)
10430  * @cfg {Number} labelxs set the width of label (1-12)
10431  * @cfg {String} labelAlign (top|left)
10432  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10433  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10434  * @cfg {String} indicatorpos (left|right) default left
10435  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10436  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10437  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10438
10439  * @cfg {String} align (left|center|right) Default left
10440  * @cfg {Boolean} forceFeedback (true|false) Default false
10441  * 
10442  * @constructor
10443  * Create a new Input
10444  * @param {Object} config The config object
10445  */
10446
10447 Roo.bootstrap.Input = function(config){
10448     
10449     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10450     
10451     this.addEvents({
10452         /**
10453          * @event focus
10454          * Fires when this field receives input focus.
10455          * @param {Roo.form.Field} this
10456          */
10457         focus : true,
10458         /**
10459          * @event blur
10460          * Fires when this field loses input focus.
10461          * @param {Roo.form.Field} this
10462          */
10463         blur : true,
10464         /**
10465          * @event specialkey
10466          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10467          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10468          * @param {Roo.form.Field} this
10469          * @param {Roo.EventObject} e The event object
10470          */
10471         specialkey : true,
10472         /**
10473          * @event change
10474          * Fires just before the field blurs if the field value has changed.
10475          * @param {Roo.form.Field} this
10476          * @param {Mixed} newValue The new value
10477          * @param {Mixed} oldValue The original value
10478          */
10479         change : true,
10480         /**
10481          * @event invalid
10482          * Fires after the field has been marked as invalid.
10483          * @param {Roo.form.Field} this
10484          * @param {String} msg The validation message
10485          */
10486         invalid : true,
10487         /**
10488          * @event valid
10489          * Fires after the field has been validated with no errors.
10490          * @param {Roo.form.Field} this
10491          */
10492         valid : true,
10493          /**
10494          * @event keyup
10495          * Fires after the key up
10496          * @param {Roo.form.Field} this
10497          * @param {Roo.EventObject}  e The event Object
10498          */
10499         keyup : true
10500     });
10501 };
10502
10503 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10504      /**
10505      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10506       automatic validation (defaults to "keyup").
10507      */
10508     validationEvent : "keyup",
10509      /**
10510      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10511      */
10512     validateOnBlur : true,
10513     /**
10514      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10515      */
10516     validationDelay : 250,
10517      /**
10518      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10519      */
10520     focusClass : "x-form-focus",  // not needed???
10521     
10522        
10523     /**
10524      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10525      */
10526     invalidClass : "has-warning",
10527     
10528     /**
10529      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10530      */
10531     validClass : "has-success",
10532     
10533     /**
10534      * @cfg {Boolean} hasFeedback (true|false) default true
10535      */
10536     hasFeedback : true,
10537     
10538     /**
10539      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10540      */
10541     invalidFeedbackClass : "glyphicon-warning-sign",
10542     
10543     /**
10544      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10545      */
10546     validFeedbackClass : "glyphicon-ok",
10547     
10548     /**
10549      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10550      */
10551     selectOnFocus : false,
10552     
10553      /**
10554      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10555      */
10556     maskRe : null,
10557        /**
10558      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10559      */
10560     vtype : null,
10561     
10562       /**
10563      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10564      */
10565     disableKeyFilter : false,
10566     
10567        /**
10568      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10569      */
10570     disabled : false,
10571      /**
10572      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10573      */
10574     allowBlank : true,
10575     /**
10576      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10577      */
10578     blankText : "Please complete this mandatory field",
10579     
10580      /**
10581      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10582      */
10583     minLength : 0,
10584     /**
10585      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10586      */
10587     maxLength : Number.MAX_VALUE,
10588     /**
10589      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10590      */
10591     minLengthText : "The minimum length for this field is {0}",
10592     /**
10593      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10594      */
10595     maxLengthText : "The maximum length for this field is {0}",
10596   
10597     
10598     /**
10599      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10600      * If available, this function will be called only after the basic validators all return true, and will be passed the
10601      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10602      */
10603     validator : null,
10604     /**
10605      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10606      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10607      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10608      */
10609     regex : null,
10610     /**
10611      * @cfg {String} regexText -- Depricated - use Invalid Text
10612      */
10613     regexText : "",
10614     
10615     /**
10616      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10617      */
10618     invalidText : "",
10619     
10620     
10621     
10622     autocomplete: false,
10623     
10624     
10625     fieldLabel : '',
10626     inputType : 'text',
10627     
10628     name : false,
10629     placeholder: false,
10630     before : false,
10631     after : false,
10632     size : false,
10633     hasFocus : false,
10634     preventMark: false,
10635     isFormField : true,
10636     value : '',
10637     labelWidth : 2,
10638     labelAlign : false,
10639     readOnly : false,
10640     align : false,
10641     formatedValue : false,
10642     forceFeedback : false,
10643     
10644     indicatorpos : 'left',
10645     
10646     labellg : 0,
10647     labelmd : 0,
10648     labelsm : 0,
10649     labelxs : 0,
10650     
10651     capture : '',
10652     accept : '',
10653     
10654     parentLabelAlign : function()
10655     {
10656         var parent = this;
10657         while (parent.parent()) {
10658             parent = parent.parent();
10659             if (typeof(parent.labelAlign) !='undefined') {
10660                 return parent.labelAlign;
10661             }
10662         }
10663         return 'left';
10664         
10665     },
10666     
10667     getAutoCreate : function()
10668     {
10669         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10670         
10671         var id = Roo.id();
10672         
10673         var cfg = {};
10674         
10675         if(this.inputType != 'hidden'){
10676             cfg.cls = 'form-group' //input-group
10677         }
10678         
10679         var input =  {
10680             tag: 'input',
10681             id : id,
10682             type : this.inputType,
10683             value : this.value,
10684             cls : 'form-control',
10685             placeholder : this.placeholder || '',
10686             autocomplete : this.autocomplete || 'new-password'
10687         };
10688         if (this.inputType == 'file') {
10689             input.style = 'overflow:hidden'; // why not in CSS?
10690         }
10691         
10692         if(this.capture.length){
10693             input.capture = this.capture;
10694         }
10695         
10696         if(this.accept.length){
10697             input.accept = this.accept + "/*";
10698         }
10699         
10700         if(this.align){
10701             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10702         }
10703         
10704         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10705             input.maxLength = this.maxLength;
10706         }
10707         
10708         if (this.disabled) {
10709             input.disabled=true;
10710         }
10711         
10712         if (this.readOnly) {
10713             input.readonly=true;
10714         }
10715         
10716         if (this.name) {
10717             input.name = this.name;
10718         }
10719         
10720         if (this.size) {
10721             input.cls += ' input-' + this.size;
10722         }
10723         
10724         var settings=this;
10725         ['xs','sm','md','lg'].map(function(size){
10726             if (settings[size]) {
10727                 cfg.cls += ' col-' + size + '-' + settings[size];
10728             }
10729         });
10730         
10731         var inputblock = input;
10732         
10733         var feedback = {
10734             tag: 'span',
10735             cls: 'glyphicon form-control-feedback'
10736         };
10737             
10738         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10739             
10740             inputblock = {
10741                 cls : 'has-feedback',
10742                 cn :  [
10743                     input,
10744                     feedback
10745                 ] 
10746             };  
10747         }
10748         
10749         if (this.before || this.after) {
10750             
10751             inputblock = {
10752                 cls : 'input-group',
10753                 cn :  [] 
10754             };
10755             
10756             if (this.before && typeof(this.before) == 'string') {
10757                 
10758                 inputblock.cn.push({
10759                     tag :'span',
10760                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10761                     html : this.before
10762                 });
10763             }
10764             if (this.before && typeof(this.before) == 'object') {
10765                 this.before = Roo.factory(this.before);
10766                 
10767                 inputblock.cn.push({
10768                     tag :'span',
10769                     cls : 'roo-input-before input-group-prepend   input-group-' +
10770                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10771                 });
10772             }
10773             
10774             inputblock.cn.push(input);
10775             
10776             if (this.after && typeof(this.after) == 'string') {
10777                 inputblock.cn.push({
10778                     tag :'span',
10779                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10780                     html : this.after
10781                 });
10782             }
10783             if (this.after && typeof(this.after) == 'object') {
10784                 this.after = Roo.factory(this.after);
10785                 
10786                 inputblock.cn.push({
10787                     tag :'span',
10788                     cls : 'roo-input-after input-group-append  input-group-' +
10789                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10790                 });
10791             }
10792             
10793             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10794                 inputblock.cls += ' has-feedback';
10795                 inputblock.cn.push(feedback);
10796             }
10797         };
10798         var indicator = {
10799             tag : 'i',
10800             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10801             tooltip : 'This field is required'
10802         };
10803         if (this.allowBlank ) {
10804             indicator.style = this.allowBlank ? ' display:none' : '';
10805         }
10806         if (align ==='left' && this.fieldLabel.length) {
10807             
10808             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10809             
10810             cfg.cn = [
10811                 indicator,
10812                 {
10813                     tag: 'label',
10814                     'for' :  id,
10815                     cls : 'control-label col-form-label',
10816                     html : this.fieldLabel
10817
10818                 },
10819                 {
10820                     cls : "", 
10821                     cn: [
10822                         inputblock
10823                     ]
10824                 }
10825             ];
10826             
10827             var labelCfg = cfg.cn[1];
10828             var contentCfg = cfg.cn[2];
10829             
10830             if(this.indicatorpos == 'right'){
10831                 cfg.cn = [
10832                     {
10833                         tag: 'label',
10834                         'for' :  id,
10835                         cls : 'control-label col-form-label',
10836                         cn : [
10837                             {
10838                                 tag : 'span',
10839                                 html : this.fieldLabel
10840                             },
10841                             indicator
10842                         ]
10843                     },
10844                     {
10845                         cls : "",
10846                         cn: [
10847                             inputblock
10848                         ]
10849                     }
10850
10851                 ];
10852                 
10853                 labelCfg = cfg.cn[0];
10854                 contentCfg = cfg.cn[1];
10855             
10856             }
10857             
10858             if(this.labelWidth > 12){
10859                 labelCfg.style = "width: " + this.labelWidth + 'px';
10860             }
10861             
10862             if(this.labelWidth < 13 && this.labelmd == 0){
10863                 this.labelmd = this.labelWidth;
10864             }
10865             
10866             if(this.labellg > 0){
10867                 labelCfg.cls += ' col-lg-' + this.labellg;
10868                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10869             }
10870             
10871             if(this.labelmd > 0){
10872                 labelCfg.cls += ' col-md-' + this.labelmd;
10873                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10874             }
10875             
10876             if(this.labelsm > 0){
10877                 labelCfg.cls += ' col-sm-' + this.labelsm;
10878                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10879             }
10880             
10881             if(this.labelxs > 0){
10882                 labelCfg.cls += ' col-xs-' + this.labelxs;
10883                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10884             }
10885             
10886             
10887         } else if ( this.fieldLabel.length) {
10888                 
10889             
10890             
10891             cfg.cn = [
10892                 {
10893                     tag : 'i',
10894                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10895                     tooltip : 'This field is required',
10896                     style : this.allowBlank ? ' display:none' : '' 
10897                 },
10898                 {
10899                     tag: 'label',
10900                    //cls : 'input-group-addon',
10901                     html : this.fieldLabel
10902
10903                 },
10904
10905                inputblock
10906
10907            ];
10908            
10909            if(this.indicatorpos == 'right'){
10910        
10911                 cfg.cn = [
10912                     {
10913                         tag: 'label',
10914                        //cls : 'input-group-addon',
10915                         html : this.fieldLabel
10916
10917                     },
10918                     {
10919                         tag : 'i',
10920                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10921                         tooltip : 'This field is required',
10922                         style : this.allowBlank ? ' display:none' : '' 
10923                     },
10924
10925                    inputblock
10926
10927                ];
10928
10929             }
10930
10931         } else {
10932             
10933             cfg.cn = [
10934
10935                     inputblock
10936
10937             ];
10938                 
10939                 
10940         };
10941         
10942         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10943            cfg.cls += ' navbar-form';
10944         }
10945         
10946         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10947             // on BS4 we do this only if not form 
10948             cfg.cls += ' navbar-form';
10949             cfg.tag = 'li';
10950         }
10951         
10952         return cfg;
10953         
10954     },
10955     /**
10956      * return the real input element.
10957      */
10958     inputEl: function ()
10959     {
10960         return this.el.select('input.form-control',true).first();
10961     },
10962     
10963     tooltipEl : function()
10964     {
10965         return this.inputEl();
10966     },
10967     
10968     indicatorEl : function()
10969     {
10970         if (Roo.bootstrap.version == 4) {
10971             return false; // not enabled in v4 yet.
10972         }
10973         
10974         var indicator = this.el.select('i.roo-required-indicator',true).first();
10975         
10976         if(!indicator){
10977             return false;
10978         }
10979         
10980         return indicator;
10981         
10982     },
10983     
10984     setDisabled : function(v)
10985     {
10986         var i  = this.inputEl().dom;
10987         if (!v) {
10988             i.removeAttribute('disabled');
10989             return;
10990             
10991         }
10992         i.setAttribute('disabled','true');
10993     },
10994     initEvents : function()
10995     {
10996           
10997         this.inputEl().on("keydown" , this.fireKey,  this);
10998         this.inputEl().on("focus", this.onFocus,  this);
10999         this.inputEl().on("blur", this.onBlur,  this);
11000         
11001         this.inputEl().relayEvent('keyup', this);
11002         
11003         this.indicator = this.indicatorEl();
11004         
11005         if(this.indicator){
11006             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11007         }
11008  
11009         // reference to original value for reset
11010         this.originalValue = this.getValue();
11011         //Roo.form.TextField.superclass.initEvents.call(this);
11012         if(this.validationEvent == 'keyup'){
11013             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11014             this.inputEl().on('keyup', this.filterValidation, this);
11015         }
11016         else if(this.validationEvent !== false){
11017             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11018         }
11019         
11020         if(this.selectOnFocus){
11021             this.on("focus", this.preFocus, this);
11022             
11023         }
11024         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11025             this.inputEl().on("keypress", this.filterKeys, this);
11026         } else {
11027             this.inputEl().relayEvent('keypress', this);
11028         }
11029        /* if(this.grow){
11030             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11031             this.el.on("click", this.autoSize,  this);
11032         }
11033         */
11034         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11035             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11036         }
11037         
11038         if (typeof(this.before) == 'object') {
11039             this.before.render(this.el.select('.roo-input-before',true).first());
11040         }
11041         if (typeof(this.after) == 'object') {
11042             this.after.render(this.el.select('.roo-input-after',true).first());
11043         }
11044         
11045         this.inputEl().on('change', this.onChange, this);
11046         
11047     },
11048     filterValidation : function(e){
11049         if(!e.isNavKeyPress()){
11050             this.validationTask.delay(this.validationDelay);
11051         }
11052     },
11053      /**
11054      * Validates the field value
11055      * @return {Boolean} True if the value is valid, else false
11056      */
11057     validate : function(){
11058         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11059         if(this.disabled || this.validateValue(this.getRawValue())){
11060             this.markValid();
11061             return true;
11062         }
11063         
11064         this.markInvalid();
11065         return false;
11066     },
11067     
11068     
11069     /**
11070      * Validates a value according to the field's validation rules and marks the field as invalid
11071      * if the validation fails
11072      * @param {Mixed} value The value to validate
11073      * @return {Boolean} True if the value is valid, else false
11074      */
11075     validateValue : function(value)
11076     {
11077         if(this.getVisibilityEl().hasClass('hidden')){
11078             return true;
11079         }
11080         
11081         if(value.length < 1)  { // if it's blank
11082             if(this.allowBlank){
11083                 return true;
11084             }
11085             return false;
11086         }
11087         
11088         if(value.length < this.minLength){
11089             return false;
11090         }
11091         if(value.length > this.maxLength){
11092             return false;
11093         }
11094         if(this.vtype){
11095             var vt = Roo.form.VTypes;
11096             if(!vt[this.vtype](value, this)){
11097                 return false;
11098             }
11099         }
11100         if(typeof this.validator == "function"){
11101             var msg = this.validator(value);
11102             if(msg !== true){
11103                 return false;
11104             }
11105             if (typeof(msg) == 'string') {
11106                 this.invalidText = msg;
11107             }
11108         }
11109         
11110         if(this.regex && !this.regex.test(value)){
11111             return false;
11112         }
11113         
11114         return true;
11115     },
11116     
11117      // private
11118     fireKey : function(e){
11119         //Roo.log('field ' + e.getKey());
11120         if(e.isNavKeyPress()){
11121             this.fireEvent("specialkey", this, e);
11122         }
11123     },
11124     focus : function (selectText){
11125         if(this.rendered){
11126             this.inputEl().focus();
11127             if(selectText === true){
11128                 this.inputEl().dom.select();
11129             }
11130         }
11131         return this;
11132     } ,
11133     
11134     onFocus : function(){
11135         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11136            // this.el.addClass(this.focusClass);
11137         }
11138         if(!this.hasFocus){
11139             this.hasFocus = true;
11140             this.startValue = this.getValue();
11141             this.fireEvent("focus", this);
11142         }
11143     },
11144     
11145     beforeBlur : Roo.emptyFn,
11146
11147     
11148     // private
11149     onBlur : function(){
11150         this.beforeBlur();
11151         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11152             //this.el.removeClass(this.focusClass);
11153         }
11154         this.hasFocus = false;
11155         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11156             this.validate();
11157         }
11158         var v = this.getValue();
11159         if(String(v) !== String(this.startValue)){
11160             this.fireEvent('change', this, v, this.startValue);
11161         }
11162         this.fireEvent("blur", this);
11163     },
11164     
11165     onChange : function(e)
11166     {
11167         var v = this.getValue();
11168         if(String(v) !== String(this.startValue)){
11169             this.fireEvent('change', this, v, this.startValue);
11170         }
11171         
11172     },
11173     
11174     /**
11175      * Resets the current field value to the originally loaded value and clears any validation messages
11176      */
11177     reset : function(){
11178         this.setValue(this.originalValue);
11179         this.validate();
11180     },
11181      /**
11182      * Returns the name of the field
11183      * @return {Mixed} name The name field
11184      */
11185     getName: function(){
11186         return this.name;
11187     },
11188      /**
11189      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11190      * @return {Mixed} value The field value
11191      */
11192     getValue : function(){
11193         
11194         var v = this.inputEl().getValue();
11195         
11196         return v;
11197     },
11198     /**
11199      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11200      * @return {Mixed} value The field value
11201      */
11202     getRawValue : function(){
11203         var v = this.inputEl().getValue();
11204         
11205         return v;
11206     },
11207     
11208     /**
11209      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11210      * @param {Mixed} value The value to set
11211      */
11212     setRawValue : function(v){
11213         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11214     },
11215     
11216     selectText : function(start, end){
11217         var v = this.getRawValue();
11218         if(v.length > 0){
11219             start = start === undefined ? 0 : start;
11220             end = end === undefined ? v.length : end;
11221             var d = this.inputEl().dom;
11222             if(d.setSelectionRange){
11223                 d.setSelectionRange(start, end);
11224             }else if(d.createTextRange){
11225                 var range = d.createTextRange();
11226                 range.moveStart("character", start);
11227                 range.moveEnd("character", v.length-end);
11228                 range.select();
11229             }
11230         }
11231     },
11232     
11233     /**
11234      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11235      * @param {Mixed} value The value to set
11236      */
11237     setValue : function(v){
11238         this.value = v;
11239         if(this.rendered){
11240             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11241             this.validate();
11242         }
11243     },
11244     
11245     /*
11246     processValue : function(value){
11247         if(this.stripCharsRe){
11248             var newValue = value.replace(this.stripCharsRe, '');
11249             if(newValue !== value){
11250                 this.setRawValue(newValue);
11251                 return newValue;
11252             }
11253         }
11254         return value;
11255     },
11256   */
11257     preFocus : function(){
11258         
11259         if(this.selectOnFocus){
11260             this.inputEl().dom.select();
11261         }
11262     },
11263     filterKeys : function(e){
11264         var k = e.getKey();
11265         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11266             return;
11267         }
11268         var c = e.getCharCode(), cc = String.fromCharCode(c);
11269         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11270             return;
11271         }
11272         if(!this.maskRe.test(cc)){
11273             e.stopEvent();
11274         }
11275     },
11276      /**
11277      * Clear any invalid styles/messages for this field
11278      */
11279     clearInvalid : function(){
11280         
11281         if(!this.el || this.preventMark){ // not rendered
11282             return;
11283         }
11284         
11285         
11286         this.el.removeClass([this.invalidClass, 'is-invalid']);
11287         
11288         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11289             
11290             var feedback = this.el.select('.form-control-feedback', true).first();
11291             
11292             if(feedback){
11293                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11294             }
11295             
11296         }
11297         
11298         if(this.indicator){
11299             this.indicator.removeClass('visible');
11300             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11301         }
11302         
11303         this.fireEvent('valid', this);
11304     },
11305     
11306      /**
11307      * Mark this field as valid
11308      */
11309     markValid : function()
11310     {
11311         if(!this.el  || this.preventMark){ // not rendered...
11312             return;
11313         }
11314         
11315         this.el.removeClass([this.invalidClass, this.validClass]);
11316         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11317
11318         var feedback = this.el.select('.form-control-feedback', true).first();
11319             
11320         if(feedback){
11321             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11322         }
11323         
11324         if(this.indicator){
11325             this.indicator.removeClass('visible');
11326             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11327         }
11328         
11329         if(this.disabled){
11330             return;
11331         }
11332         
11333            
11334         if(this.allowBlank && !this.getRawValue().length){
11335             return;
11336         }
11337         if (Roo.bootstrap.version == 3) {
11338             this.el.addClass(this.validClass);
11339         } else {
11340             this.inputEl().addClass('is-valid');
11341         }
11342
11343         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11344             
11345             var feedback = this.el.select('.form-control-feedback', true).first();
11346             
11347             if(feedback){
11348                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11349                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11350             }
11351             
11352         }
11353         
11354         this.fireEvent('valid', this);
11355     },
11356     
11357      /**
11358      * Mark this field as invalid
11359      * @param {String} msg The validation message
11360      */
11361     markInvalid : function(msg)
11362     {
11363         if(!this.el  || this.preventMark){ // not rendered
11364             return;
11365         }
11366         
11367         this.el.removeClass([this.invalidClass, this.validClass]);
11368         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11369         
11370         var feedback = this.el.select('.form-control-feedback', true).first();
11371             
11372         if(feedback){
11373             this.el.select('.form-control-feedback', true).first().removeClass(
11374                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11375         }
11376
11377         if(this.disabled){
11378             return;
11379         }
11380         
11381         if(this.allowBlank && !this.getRawValue().length){
11382             return;
11383         }
11384         
11385         if(this.indicator){
11386             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11387             this.indicator.addClass('visible');
11388         }
11389         if (Roo.bootstrap.version == 3) {
11390             this.el.addClass(this.invalidClass);
11391         } else {
11392             this.inputEl().addClass('is-invalid');
11393         }
11394         
11395         
11396         
11397         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11398             
11399             var feedback = this.el.select('.form-control-feedback', true).first();
11400             
11401             if(feedback){
11402                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11403                 
11404                 if(this.getValue().length || this.forceFeedback){
11405                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11406                 }
11407                 
11408             }
11409             
11410         }
11411         
11412         this.fireEvent('invalid', this, msg);
11413     },
11414     // private
11415     SafariOnKeyDown : function(event)
11416     {
11417         // this is a workaround for a password hang bug on chrome/ webkit.
11418         if (this.inputEl().dom.type != 'password') {
11419             return;
11420         }
11421         
11422         var isSelectAll = false;
11423         
11424         if(this.inputEl().dom.selectionEnd > 0){
11425             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11426         }
11427         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11428             event.preventDefault();
11429             this.setValue('');
11430             return;
11431         }
11432         
11433         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11434             
11435             event.preventDefault();
11436             // this is very hacky as keydown always get's upper case.
11437             //
11438             var cc = String.fromCharCode(event.getCharCode());
11439             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11440             
11441         }
11442     },
11443     adjustWidth : function(tag, w){
11444         tag = tag.toLowerCase();
11445         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11446             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11447                 if(tag == 'input'){
11448                     return w + 2;
11449                 }
11450                 if(tag == 'textarea'){
11451                     return w-2;
11452                 }
11453             }else if(Roo.isOpera){
11454                 if(tag == 'input'){
11455                     return w + 2;
11456                 }
11457                 if(tag == 'textarea'){
11458                     return w-2;
11459                 }
11460             }
11461         }
11462         return w;
11463     },
11464     
11465     setFieldLabel : function(v)
11466     {
11467         if(!this.rendered){
11468             return;
11469         }
11470         
11471         if(this.indicatorEl()){
11472             var ar = this.el.select('label > span',true);
11473             
11474             if (ar.elements.length) {
11475                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             var br = this.el.select('label',true);
11481             
11482             if(br.elements.length) {
11483                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11484                 this.fieldLabel = v;
11485                 return;
11486             }
11487             
11488             Roo.log('Cannot Found any of label > span || label in input');
11489             return;
11490         }
11491         
11492         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11493         this.fieldLabel = v;
11494         
11495         
11496     }
11497 });
11498
11499  
11500 /*
11501  * - LGPL
11502  *
11503  * Input
11504  * 
11505  */
11506
11507 /**
11508  * @class Roo.bootstrap.TextArea
11509  * @extends Roo.bootstrap.Input
11510  * Bootstrap TextArea class
11511  * @cfg {Number} cols Specifies the visible width of a text area
11512  * @cfg {Number} rows Specifies the visible number of lines in a text area
11513  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11514  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11515  * @cfg {string} html text
11516  * 
11517  * @constructor
11518  * Create a new TextArea
11519  * @param {Object} config The config object
11520  */
11521
11522 Roo.bootstrap.TextArea = function(config){
11523     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11524    
11525 };
11526
11527 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11528      
11529     cols : false,
11530     rows : 5,
11531     readOnly : false,
11532     warp : 'soft',
11533     resize : false,
11534     value: false,
11535     html: false,
11536     
11537     getAutoCreate : function(){
11538         
11539         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11540         
11541         var id = Roo.id();
11542         
11543         var cfg = {};
11544         
11545         if(this.inputType != 'hidden'){
11546             cfg.cls = 'form-group' //input-group
11547         }
11548         
11549         var input =  {
11550             tag: 'textarea',
11551             id : id,
11552             warp : this.warp,
11553             rows : this.rows,
11554             value : this.value || '',
11555             html: this.html || '',
11556             cls : 'form-control',
11557             placeholder : this.placeholder || '' 
11558             
11559         };
11560         
11561         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11562             input.maxLength = this.maxLength;
11563         }
11564         
11565         if(this.resize){
11566             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11567         }
11568         
11569         if(this.cols){
11570             input.cols = this.cols;
11571         }
11572         
11573         if (this.readOnly) {
11574             input.readonly = true;
11575         }
11576         
11577         if (this.name) {
11578             input.name = this.name;
11579         }
11580         
11581         if (this.size) {
11582             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11583         }
11584         
11585         var settings=this;
11586         ['xs','sm','md','lg'].map(function(size){
11587             if (settings[size]) {
11588                 cfg.cls += ' col-' + size + '-' + settings[size];
11589             }
11590         });
11591         
11592         var inputblock = input;
11593         
11594         if(this.hasFeedback && !this.allowBlank){
11595             
11596             var feedback = {
11597                 tag: 'span',
11598                 cls: 'glyphicon form-control-feedback'
11599             };
11600
11601             inputblock = {
11602                 cls : 'has-feedback',
11603                 cn :  [
11604                     input,
11605                     feedback
11606                 ] 
11607             };  
11608         }
11609         
11610         
11611         if (this.before || this.after) {
11612             
11613             inputblock = {
11614                 cls : 'input-group',
11615                 cn :  [] 
11616             };
11617             if (this.before) {
11618                 inputblock.cn.push({
11619                     tag :'span',
11620                     cls : 'input-group-addon',
11621                     html : this.before
11622                 });
11623             }
11624             
11625             inputblock.cn.push(input);
11626             
11627             if(this.hasFeedback && !this.allowBlank){
11628                 inputblock.cls += ' has-feedback';
11629                 inputblock.cn.push(feedback);
11630             }
11631             
11632             if (this.after) {
11633                 inputblock.cn.push({
11634                     tag :'span',
11635                     cls : 'input-group-addon',
11636                     html : this.after
11637                 });
11638             }
11639             
11640         }
11641         
11642         if (align ==='left' && this.fieldLabel.length) {
11643             cfg.cn = [
11644                 {
11645                     tag: 'label',
11646                     'for' :  id,
11647                     cls : 'control-label',
11648                     html : this.fieldLabel
11649                 },
11650                 {
11651                     cls : "",
11652                     cn: [
11653                         inputblock
11654                     ]
11655                 }
11656
11657             ];
11658             
11659             if(this.labelWidth > 12){
11660                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11661             }
11662
11663             if(this.labelWidth < 13 && this.labelmd == 0){
11664                 this.labelmd = this.labelWidth;
11665             }
11666
11667             if(this.labellg > 0){
11668                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11669                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11670             }
11671
11672             if(this.labelmd > 0){
11673                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11674                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11675             }
11676
11677             if(this.labelsm > 0){
11678                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11679                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11680             }
11681
11682             if(this.labelxs > 0){
11683                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11684                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11685             }
11686             
11687         } else if ( this.fieldLabel.length) {
11688             cfg.cn = [
11689
11690                {
11691                    tag: 'label',
11692                    //cls : 'input-group-addon',
11693                    html : this.fieldLabel
11694
11695                },
11696
11697                inputblock
11698
11699            ];
11700
11701         } else {
11702
11703             cfg.cn = [
11704
11705                 inputblock
11706
11707             ];
11708                 
11709         }
11710         
11711         if (this.disabled) {
11712             input.disabled=true;
11713         }
11714         
11715         return cfg;
11716         
11717     },
11718     /**
11719      * return the real textarea element.
11720      */
11721     inputEl: function ()
11722     {
11723         return this.el.select('textarea.form-control',true).first();
11724     },
11725     
11726     /**
11727      * Clear any invalid styles/messages for this field
11728      */
11729     clearInvalid : function()
11730     {
11731         
11732         if(!this.el || this.preventMark){ // not rendered
11733             return;
11734         }
11735         
11736         var label = this.el.select('label', true).first();
11737         var icon = this.el.select('i.fa-star', true).first();
11738         
11739         if(label && icon){
11740             icon.remove();
11741         }
11742         this.el.removeClass( this.validClass);
11743         this.inputEl().removeClass('is-invalid');
11744          
11745         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11746             
11747             var feedback = this.el.select('.form-control-feedback', true).first();
11748             
11749             if(feedback){
11750                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11751             }
11752             
11753         }
11754         
11755         this.fireEvent('valid', this);
11756     },
11757     
11758      /**
11759      * Mark this field as valid
11760      */
11761     markValid : function()
11762     {
11763         if(!this.el  || this.preventMark){ // not rendered
11764             return;
11765         }
11766         
11767         this.el.removeClass([this.invalidClass, this.validClass]);
11768         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11769         
11770         var feedback = this.el.select('.form-control-feedback', true).first();
11771             
11772         if(feedback){
11773             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11774         }
11775
11776         if(this.disabled || this.allowBlank){
11777             return;
11778         }
11779         
11780         var label = this.el.select('label', true).first();
11781         var icon = this.el.select('i.fa-star', true).first();
11782         
11783         if(label && icon){
11784             icon.remove();
11785         }
11786         if (Roo.bootstrap.version == 3) {
11787             this.el.addClass(this.validClass);
11788         } else {
11789             this.inputEl().addClass('is-valid');
11790         }
11791         
11792         
11793         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11794             
11795             var feedback = this.el.select('.form-control-feedback', true).first();
11796             
11797             if(feedback){
11798                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11799                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11800             }
11801             
11802         }
11803         
11804         this.fireEvent('valid', this);
11805     },
11806     
11807      /**
11808      * Mark this field as invalid
11809      * @param {String} msg The validation message
11810      */
11811     markInvalid : function(msg)
11812     {
11813         if(!this.el  || this.preventMark){ // not rendered
11814             return;
11815         }
11816         
11817         this.el.removeClass([this.invalidClass, this.validClass]);
11818         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11819         
11820         var feedback = this.el.select('.form-control-feedback', true).first();
11821             
11822         if(feedback){
11823             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11824         }
11825
11826         if(this.disabled || this.allowBlank){
11827             return;
11828         }
11829         
11830         var label = this.el.select('label', true).first();
11831         var icon = this.el.select('i.fa-star', true).first();
11832         
11833         if(!this.getValue().length && label && !icon){
11834             this.el.createChild({
11835                 tag : 'i',
11836                 cls : 'text-danger fa fa-lg fa-star',
11837                 tooltip : 'This field is required',
11838                 style : 'margin-right:5px;'
11839             }, label, true);
11840         }
11841         
11842         if (Roo.bootstrap.version == 3) {
11843             this.el.addClass(this.invalidClass);
11844         } else {
11845             this.inputEl().addClass('is-invalid');
11846         }
11847         
11848         // fixme ... this may be depricated need to test..
11849         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11850             
11851             var feedback = this.el.select('.form-control-feedback', true).first();
11852             
11853             if(feedback){
11854                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11855                 
11856                 if(this.getValue().length || this.forceFeedback){
11857                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11858                 }
11859                 
11860             }
11861             
11862         }
11863         
11864         this.fireEvent('invalid', this, msg);
11865     }
11866 });
11867
11868  
11869 /*
11870  * - LGPL
11871  *
11872  * trigger field - base class for combo..
11873  * 
11874  */
11875  
11876 /**
11877  * @class Roo.bootstrap.TriggerField
11878  * @extends Roo.bootstrap.Input
11879  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11880  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11881  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11882  * for which you can provide a custom implementation.  For example:
11883  * <pre><code>
11884 var trigger = new Roo.bootstrap.TriggerField();
11885 trigger.onTriggerClick = myTriggerFn;
11886 trigger.applyTo('my-field');
11887 </code></pre>
11888  *
11889  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11890  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11891  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11892  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11893  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11894
11895  * @constructor
11896  * Create a new TriggerField.
11897  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11898  * to the base TextField)
11899  */
11900 Roo.bootstrap.TriggerField = function(config){
11901     this.mimicing = false;
11902     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11903 };
11904
11905 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11906     /**
11907      * @cfg {String} triggerClass A CSS class to apply to the trigger
11908      */
11909      /**
11910      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11911      */
11912     hideTrigger:false,
11913
11914     /**
11915      * @cfg {Boolean} removable (true|false) special filter default false
11916      */
11917     removable : false,
11918     
11919     /** @cfg {Boolean} grow @hide */
11920     /** @cfg {Number} growMin @hide */
11921     /** @cfg {Number} growMax @hide */
11922
11923     /**
11924      * @hide 
11925      * @method
11926      */
11927     autoSize: Roo.emptyFn,
11928     // private
11929     monitorTab : true,
11930     // private
11931     deferHeight : true,
11932
11933     
11934     actionMode : 'wrap',
11935     
11936     caret : false,
11937     
11938     
11939     getAutoCreate : function(){
11940        
11941         var align = this.labelAlign || this.parentLabelAlign();
11942         
11943         var id = Roo.id();
11944         
11945         var cfg = {
11946             cls: 'form-group' //input-group
11947         };
11948         
11949         
11950         var input =  {
11951             tag: 'input',
11952             id : id,
11953             type : this.inputType,
11954             cls : 'form-control',
11955             autocomplete: 'new-password',
11956             placeholder : this.placeholder || '' 
11957             
11958         };
11959         if (this.name) {
11960             input.name = this.name;
11961         }
11962         if (this.size) {
11963             input.cls += ' input-' + this.size;
11964         }
11965         
11966         if (this.disabled) {
11967             input.disabled=true;
11968         }
11969         
11970         var inputblock = input;
11971         
11972         if(this.hasFeedback && !this.allowBlank){
11973             
11974             var feedback = {
11975                 tag: 'span',
11976                 cls: 'glyphicon form-control-feedback'
11977             };
11978             
11979             if(this.removable && !this.editable  ){
11980                 inputblock = {
11981                     cls : 'has-feedback',
11982                     cn :  [
11983                         inputblock,
11984                         {
11985                             tag: 'button',
11986                             html : 'x',
11987                             cls : 'roo-combo-removable-btn close'
11988                         },
11989                         feedback
11990                     ] 
11991                 };
11992             } else {
11993                 inputblock = {
11994                     cls : 'has-feedback',
11995                     cn :  [
11996                         inputblock,
11997                         feedback
11998                     ] 
11999                 };
12000             }
12001
12002         } else {
12003             if(this.removable && !this.editable ){
12004                 inputblock = {
12005                     cls : 'roo-removable',
12006                     cn :  [
12007                         inputblock,
12008                         {
12009                             tag: 'button',
12010                             html : 'x',
12011                             cls : 'roo-combo-removable-btn close'
12012                         }
12013                     ] 
12014                 };
12015             }
12016         }
12017         
12018         if (this.before || this.after) {
12019             
12020             inputblock = {
12021                 cls : 'input-group',
12022                 cn :  [] 
12023             };
12024             if (this.before) {
12025                 inputblock.cn.push({
12026                     tag :'span',
12027                     cls : 'input-group-addon input-group-prepend input-group-text',
12028                     html : this.before
12029                 });
12030             }
12031             
12032             inputblock.cn.push(input);
12033             
12034             if(this.hasFeedback && !this.allowBlank){
12035                 inputblock.cls += ' has-feedback';
12036                 inputblock.cn.push(feedback);
12037             }
12038             
12039             if (this.after) {
12040                 inputblock.cn.push({
12041                     tag :'span',
12042                     cls : 'input-group-addon input-group-append input-group-text',
12043                     html : this.after
12044                 });
12045             }
12046             
12047         };
12048         
12049       
12050         
12051         var ibwrap = inputblock;
12052         
12053         if(this.multiple){
12054             ibwrap = {
12055                 tag: 'ul',
12056                 cls: 'roo-select2-choices',
12057                 cn:[
12058                     {
12059                         tag: 'li',
12060                         cls: 'roo-select2-search-field',
12061                         cn: [
12062
12063                             inputblock
12064                         ]
12065                     }
12066                 ]
12067             };
12068                 
12069         }
12070         
12071         var combobox = {
12072             cls: 'roo-select2-container input-group',
12073             cn: [
12074                  {
12075                     tag: 'input',
12076                     type : 'hidden',
12077                     cls: 'form-hidden-field'
12078                 },
12079                 ibwrap
12080             ]
12081         };
12082         
12083         if(!this.multiple && this.showToggleBtn){
12084             
12085             var caret = {
12086                         tag: 'span',
12087                         cls: 'caret'
12088              };
12089             if (this.caret != false) {
12090                 caret = {
12091                      tag: 'i',
12092                      cls: 'fa fa-' + this.caret
12093                 };
12094                 
12095             }
12096             
12097             combobox.cn.push({
12098                 tag :'span',
12099                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12100                 cn : [
12101                     Roo.bootstrap.version == 3 ? caret : '',
12102                     {
12103                         tag: 'span',
12104                         cls: 'combobox-clear',
12105                         cn  : [
12106                             {
12107                                 tag : 'i',
12108                                 cls: 'icon-remove'
12109                             }
12110                         ]
12111                     }
12112                 ]
12113
12114             })
12115         }
12116         
12117         if(this.multiple){
12118             combobox.cls += ' roo-select2-container-multi';
12119         }
12120          var indicator = {
12121             tag : 'i',
12122             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12123             tooltip : 'This field is required'
12124         };
12125         if (Roo.bootstrap.version == 4) {
12126             indicator = {
12127                 tag : 'i',
12128                 style : 'display:none'
12129             };
12130         }
12131         
12132         
12133         if (align ==='left' && this.fieldLabel.length) {
12134             
12135             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12136
12137             cfg.cn = [
12138                 indicator,
12139                 {
12140                     tag: 'label',
12141                     'for' :  id,
12142                     cls : 'control-label',
12143                     html : this.fieldLabel
12144
12145                 },
12146                 {
12147                     cls : "", 
12148                     cn: [
12149                         combobox
12150                     ]
12151                 }
12152
12153             ];
12154             
12155             var labelCfg = cfg.cn[1];
12156             var contentCfg = cfg.cn[2];
12157             
12158             if(this.indicatorpos == 'right'){
12159                 cfg.cn = [
12160                     {
12161                         tag: 'label',
12162                         'for' :  id,
12163                         cls : 'control-label',
12164                         cn : [
12165                             {
12166                                 tag : 'span',
12167                                 html : this.fieldLabel
12168                             },
12169                             indicator
12170                         ]
12171                     },
12172                     {
12173                         cls : "", 
12174                         cn: [
12175                             combobox
12176                         ]
12177                     }
12178
12179                 ];
12180                 
12181                 labelCfg = cfg.cn[0];
12182                 contentCfg = cfg.cn[1];
12183             }
12184             
12185             if(this.labelWidth > 12){
12186                 labelCfg.style = "width: " + this.labelWidth + 'px';
12187             }
12188             
12189             if(this.labelWidth < 13 && this.labelmd == 0){
12190                 this.labelmd = this.labelWidth;
12191             }
12192             
12193             if(this.labellg > 0){
12194                 labelCfg.cls += ' col-lg-' + this.labellg;
12195                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12196             }
12197             
12198             if(this.labelmd > 0){
12199                 labelCfg.cls += ' col-md-' + this.labelmd;
12200                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12201             }
12202             
12203             if(this.labelsm > 0){
12204                 labelCfg.cls += ' col-sm-' + this.labelsm;
12205                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12206             }
12207             
12208             if(this.labelxs > 0){
12209                 labelCfg.cls += ' col-xs-' + this.labelxs;
12210                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12211             }
12212             
12213         } else if ( this.fieldLabel.length) {
12214 //                Roo.log(" label");
12215             cfg.cn = [
12216                 indicator,
12217                {
12218                    tag: 'label',
12219                    //cls : 'input-group-addon',
12220                    html : this.fieldLabel
12221
12222                },
12223
12224                combobox
12225
12226             ];
12227             
12228             if(this.indicatorpos == 'right'){
12229                 
12230                 cfg.cn = [
12231                     {
12232                        tag: 'label',
12233                        cn : [
12234                            {
12235                                tag : 'span',
12236                                html : this.fieldLabel
12237                            },
12238                            indicator
12239                        ]
12240
12241                     },
12242                     combobox
12243
12244                 ];
12245
12246             }
12247
12248         } else {
12249             
12250 //                Roo.log(" no label && no align");
12251                 cfg = combobox
12252                      
12253                 
12254         }
12255         
12256         var settings=this;
12257         ['xs','sm','md','lg'].map(function(size){
12258             if (settings[size]) {
12259                 cfg.cls += ' col-' + size + '-' + settings[size];
12260             }
12261         });
12262         
12263         return cfg;
12264         
12265     },
12266     
12267     
12268     
12269     // private
12270     onResize : function(w, h){
12271 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12272 //        if(typeof w == 'number'){
12273 //            var x = w - this.trigger.getWidth();
12274 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12275 //            this.trigger.setStyle('left', x+'px');
12276 //        }
12277     },
12278
12279     // private
12280     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12281
12282     // private
12283     getResizeEl : function(){
12284         return this.inputEl();
12285     },
12286
12287     // private
12288     getPositionEl : function(){
12289         return this.inputEl();
12290     },
12291
12292     // private
12293     alignErrorIcon : function(){
12294         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12295     },
12296
12297     // private
12298     initEvents : function(){
12299         
12300         this.createList();
12301         
12302         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12303         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12304         if(!this.multiple && this.showToggleBtn){
12305             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12306             if(this.hideTrigger){
12307                 this.trigger.setDisplayed(false);
12308             }
12309             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12310         }
12311         
12312         if(this.multiple){
12313             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12314         }
12315         
12316         if(this.removable && !this.editable && !this.tickable){
12317             var close = this.closeTriggerEl();
12318             
12319             if(close){
12320                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12321                 close.on('click', this.removeBtnClick, this, close);
12322             }
12323         }
12324         
12325         //this.trigger.addClassOnOver('x-form-trigger-over');
12326         //this.trigger.addClassOnClick('x-form-trigger-click');
12327         
12328         //if(!this.width){
12329         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12330         //}
12331     },
12332     
12333     closeTriggerEl : function()
12334     {
12335         var close = this.el.select('.roo-combo-removable-btn', true).first();
12336         return close ? close : false;
12337     },
12338     
12339     removeBtnClick : function(e, h, el)
12340     {
12341         e.preventDefault();
12342         
12343         if(this.fireEvent("remove", this) !== false){
12344             this.reset();
12345             this.fireEvent("afterremove", this)
12346         }
12347     },
12348     
12349     createList : function()
12350     {
12351         this.list = Roo.get(document.body).createChild({
12352             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12353             cls: 'typeahead typeahead-long dropdown-menu shadow',
12354             style: 'display:none'
12355         });
12356         
12357         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12358         
12359     },
12360
12361     // private
12362     initTrigger : function(){
12363        
12364     },
12365
12366     // private
12367     onDestroy : function(){
12368         if(this.trigger){
12369             this.trigger.removeAllListeners();
12370           //  this.trigger.remove();
12371         }
12372         //if(this.wrap){
12373         //    this.wrap.remove();
12374         //}
12375         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12376     },
12377
12378     // private
12379     onFocus : function(){
12380         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12381         /*
12382         if(!this.mimicing){
12383             this.wrap.addClass('x-trigger-wrap-focus');
12384             this.mimicing = true;
12385             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12386             if(this.monitorTab){
12387                 this.el.on("keydown", this.checkTab, this);
12388             }
12389         }
12390         */
12391     },
12392
12393     // private
12394     checkTab : function(e){
12395         if(e.getKey() == e.TAB){
12396             this.triggerBlur();
12397         }
12398     },
12399
12400     // private
12401     onBlur : function(){
12402         // do nothing
12403     },
12404
12405     // private
12406     mimicBlur : function(e, t){
12407         /*
12408         if(!this.wrap.contains(t) && this.validateBlur()){
12409             this.triggerBlur();
12410         }
12411         */
12412     },
12413
12414     // private
12415     triggerBlur : function(){
12416         this.mimicing = false;
12417         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12418         if(this.monitorTab){
12419             this.el.un("keydown", this.checkTab, this);
12420         }
12421         //this.wrap.removeClass('x-trigger-wrap-focus');
12422         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12423     },
12424
12425     // private
12426     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12427     validateBlur : function(e, t){
12428         return true;
12429     },
12430
12431     // private
12432     onDisable : function(){
12433         this.inputEl().dom.disabled = true;
12434         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12435         //if(this.wrap){
12436         //    this.wrap.addClass('x-item-disabled');
12437         //}
12438     },
12439
12440     // private
12441     onEnable : function(){
12442         this.inputEl().dom.disabled = false;
12443         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12444         //if(this.wrap){
12445         //    this.el.removeClass('x-item-disabled');
12446         //}
12447     },
12448
12449     // private
12450     onShow : function(){
12451         var ae = this.getActionEl();
12452         
12453         if(ae){
12454             ae.dom.style.display = '';
12455             ae.dom.style.visibility = 'visible';
12456         }
12457     },
12458
12459     // private
12460     
12461     onHide : function(){
12462         var ae = this.getActionEl();
12463         ae.dom.style.display = 'none';
12464     },
12465
12466     /**
12467      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12468      * by an implementing function.
12469      * @method
12470      * @param {EventObject} e
12471      */
12472     onTriggerClick : Roo.emptyFn
12473 });
12474  
12475 /*
12476 * Licence: LGPL
12477 */
12478
12479 /**
12480  * @class Roo.bootstrap.CardUploader
12481  * @extends Roo.bootstrap.Button
12482  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12483  * @cfg {Number} errorTimeout default 3000
12484  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12485  * @cfg {Array}  html The button text.
12486
12487  *
12488  * @constructor
12489  * Create a new CardUploader
12490  * @param {Object} config The config object
12491  */
12492
12493 Roo.bootstrap.CardUploader = function(config){
12494     
12495  
12496     
12497     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12498     
12499     
12500     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12501         return r.data.id
12502      });
12503     
12504      this.addEvents({
12505          // raw events
12506         /**
12507          * @event preview
12508          * When a image is clicked on - and needs to display a slideshow or similar..
12509          * @param {Roo.bootstrap.Card} this
12510          * @param {Object} The image information data 
12511          *
12512          */
12513         'preview' : true,
12514          /**
12515          * @event download
12516          * When a the download link is clicked
12517          * @param {Roo.bootstrap.Card} this
12518          * @param {Object} The image information data  contains 
12519          */
12520         'download' : true
12521         
12522     });
12523 };
12524  
12525 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12526     
12527      
12528     errorTimeout : 3000,
12529      
12530     images : false,
12531    
12532     fileCollection : false,
12533     allowBlank : true,
12534     
12535     getAutoCreate : function()
12536     {
12537         
12538         var cfg =  {
12539             cls :'form-group' ,
12540             cn : [
12541                
12542                 {
12543                     tag: 'label',
12544                    //cls : 'input-group-addon',
12545                     html : this.fieldLabel
12546
12547                 },
12548
12549                 {
12550                     tag: 'input',
12551                     type : 'hidden',
12552                     name : this.name,
12553                     value : this.value,
12554                     cls : 'd-none  form-control'
12555                 },
12556                 
12557                 {
12558                     tag: 'input',
12559                     multiple : 'multiple',
12560                     type : 'file',
12561                     cls : 'd-none  roo-card-upload-selector'
12562                 },
12563                 
12564                 {
12565                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12566                 },
12567                 {
12568                     cls : 'card-columns roo-card-uploader-container'
12569                 }
12570
12571             ]
12572         };
12573            
12574          
12575         return cfg;
12576     },
12577     
12578     getChildContainer : function() /// what children are added to.
12579     {
12580         return this.containerEl;
12581     },
12582    
12583     getButtonContainer : function() /// what children are added to.
12584     {
12585         return this.el.select(".roo-card-uploader-button-container").first();
12586     },
12587    
12588     initEvents : function()
12589     {
12590         
12591         Roo.bootstrap.Input.prototype.initEvents.call(this);
12592         
12593         var t = this;
12594         this.addxtype({
12595             xns: Roo.bootstrap,
12596
12597             xtype : 'Button',
12598             container_method : 'getButtonContainer' ,            
12599             html :  this.html, // fix changable?
12600             cls : 'w-100 ',
12601             listeners : {
12602                 'click' : function(btn, e) {
12603                     t.onClick(e);
12604                 }
12605             }
12606         });
12607         
12608         
12609         
12610         
12611         this.urlAPI = (window.createObjectURL && window) || 
12612                                 (window.URL && URL.revokeObjectURL && URL) || 
12613                                 (window.webkitURL && webkitURL);
12614                         
12615          
12616          
12617          
12618         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12619         
12620         this.selectorEl.on('change', this.onFileSelected, this);
12621         if (this.images) {
12622             var t = this;
12623             this.images.forEach(function(img) {
12624                 t.addCard(img)
12625             });
12626             this.images = false;
12627         }
12628         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12629          
12630        
12631     },
12632     
12633    
12634     onClick : function(e)
12635     {
12636         e.preventDefault();
12637          
12638         this.selectorEl.dom.click();
12639          
12640     },
12641     
12642     onFileSelected : function(e)
12643     {
12644         e.preventDefault();
12645         
12646         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12647             return;
12648         }
12649         
12650         Roo.each(this.selectorEl.dom.files, function(file){    
12651             this.addFile(file);
12652         }, this);
12653          
12654     },
12655     
12656       
12657     
12658       
12659     
12660     addFile : function(file)
12661     {
12662            
12663         if(typeof(file) === 'string'){
12664             throw "Add file by name?"; // should not happen
12665             return;
12666         }
12667         
12668         if(!file || !this.urlAPI){
12669             return;
12670         }
12671         
12672         // file;
12673         // file.type;
12674         
12675         var _this = this;
12676         
12677         
12678         var url = _this.urlAPI.createObjectURL( file);
12679            
12680         this.addCard({
12681             id : Roo.bootstrap.CardUploader.ID--,
12682             is_uploaded : false,
12683             src : url,
12684             srcfile : file,
12685             title : file.name,
12686             mimetype : file.type,
12687             preview : false,
12688             is_deleted : 0
12689         });
12690         
12691     },
12692     
12693     /**
12694      * addCard - add an Attachment to the uploader
12695      * @param data - the data about the image to upload
12696      *
12697      * {
12698           id : 123
12699           title : "Title of file",
12700           is_uploaded : false,
12701           src : "http://.....",
12702           srcfile : { the File upload object },
12703           mimetype : file.type,
12704           preview : false,
12705           is_deleted : 0
12706           .. any other data...
12707         }
12708      *
12709      * 
12710     */
12711     
12712     addCard : function (data)
12713     {
12714         // hidden input element?
12715         // if the file is not an image...
12716         //then we need to use something other that and header_image
12717         var t = this;
12718         //   remove.....
12719         var footer = [
12720             {
12721                 xns : Roo.bootstrap,
12722                 xtype : 'CardFooter',
12723                  items: [
12724                     {
12725                         xns : Roo.bootstrap,
12726                         xtype : 'Element',
12727                         cls : 'd-flex',
12728                         items : [
12729                             
12730                             {
12731                                 xns : Roo.bootstrap,
12732                                 xtype : 'Button',
12733                                 html : String.format("<small>{0}</small>", data.title),
12734                                 cls : 'col-10 text-left',
12735                                 size: 'sm',
12736                                 weight: 'link',
12737                                 fa : 'download',
12738                                 listeners : {
12739                                     click : function() {
12740                                      
12741                                         t.fireEvent( "download", t, data );
12742                                     }
12743                                 }
12744                             },
12745                           
12746                             {
12747                                 xns : Roo.bootstrap,
12748                                 xtype : 'Button',
12749                                 style: 'max-height: 28px; ',
12750                                 size : 'sm',
12751                                 weight: 'danger',
12752                                 cls : 'col-2',
12753                                 fa : 'times',
12754                                 listeners : {
12755                                     click : function() {
12756                                         t.removeCard(data.id)
12757                                     }
12758                                 }
12759                             }
12760                         ]
12761                     }
12762                     
12763                 ] 
12764             }
12765             
12766         ];
12767         
12768         var cn = this.addxtype(
12769             {
12770                  
12771                 xns : Roo.bootstrap,
12772                 xtype : 'Card',
12773                 closeable : true,
12774                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12775                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12776                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12777                 data : data,
12778                 html : false,
12779                  
12780                 items : footer,
12781                 initEvents : function() {
12782                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12783                     var card = this;
12784                     this.imgEl = this.el.select('.card-img-top').first();
12785                     if (this.imgEl) {
12786                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12787                         this.imgEl.set({ 'pointer' : 'cursor' });
12788                                   
12789                     }
12790                     this.getCardFooter().addClass('p-1');
12791                     
12792                   
12793                 }
12794                 
12795             }
12796         );
12797         // dont' really need ot update items.
12798         // this.items.push(cn);
12799         this.fileCollection.add(cn);
12800         
12801         if (!data.srcfile) {
12802             this.updateInput();
12803             return;
12804         }
12805             
12806         var _t = this;
12807         var reader = new FileReader();
12808         reader.addEventListener("load", function() {  
12809             data.srcdata =  reader.result;
12810             _t.updateInput();
12811         });
12812         reader.readAsDataURL(data.srcfile);
12813         
12814         
12815         
12816     },
12817     removeCard : function(id)
12818     {
12819         
12820         var card  = this.fileCollection.get(id);
12821         card.data.is_deleted = 1;
12822         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12823         //this.fileCollection.remove(card);
12824         //this.items = this.items.filter(function(e) { return e != card });
12825         // dont' really need ot update items.
12826         card.el.dom.parentNode.removeChild(card.el.dom);
12827         this.updateInput();
12828
12829         
12830     },
12831     reset: function()
12832     {
12833         this.fileCollection.each(function(card) {
12834             if (card.el.dom && card.el.dom.parentNode) {
12835                 card.el.dom.parentNode.removeChild(card.el.dom);
12836             }
12837         });
12838         this.fileCollection.clear();
12839         this.updateInput();
12840     },
12841     
12842     updateInput : function()
12843     {
12844          var data = [];
12845         this.fileCollection.each(function(e) {
12846             data.push(e.data);
12847             
12848         });
12849         this.inputEl().dom.value = JSON.stringify(data);
12850         
12851         
12852         
12853     }
12854     
12855     
12856 });
12857
12858
12859 Roo.bootstrap.CardUploader.ID = -1;/*
12860  * Based on:
12861  * Ext JS Library 1.1.1
12862  * Copyright(c) 2006-2007, Ext JS, LLC.
12863  *
12864  * Originally Released Under LGPL - original licence link has changed is not relivant.
12865  *
12866  * Fork - LGPL
12867  * <script type="text/javascript">
12868  */
12869
12870
12871 /**
12872  * @class Roo.data.SortTypes
12873  * @singleton
12874  * Defines the default sorting (casting?) comparison functions used when sorting data.
12875  */
12876 Roo.data.SortTypes = {
12877     /**
12878      * Default sort that does nothing
12879      * @param {Mixed} s The value being converted
12880      * @return {Mixed} The comparison value
12881      */
12882     none : function(s){
12883         return s;
12884     },
12885     
12886     /**
12887      * The regular expression used to strip tags
12888      * @type {RegExp}
12889      * @property
12890      */
12891     stripTagsRE : /<\/?[^>]+>/gi,
12892     
12893     /**
12894      * Strips all HTML tags to sort on text only
12895      * @param {Mixed} s The value being converted
12896      * @return {String} The comparison value
12897      */
12898     asText : function(s){
12899         return String(s).replace(this.stripTagsRE, "");
12900     },
12901     
12902     /**
12903      * Strips all HTML tags to sort on text only - Case insensitive
12904      * @param {Mixed} s The value being converted
12905      * @return {String} The comparison value
12906      */
12907     asUCText : function(s){
12908         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12909     },
12910     
12911     /**
12912      * Case insensitive string
12913      * @param {Mixed} s The value being converted
12914      * @return {String} The comparison value
12915      */
12916     asUCString : function(s) {
12917         return String(s).toUpperCase();
12918     },
12919     
12920     /**
12921      * Date sorting
12922      * @param {Mixed} s The value being converted
12923      * @return {Number} The comparison value
12924      */
12925     asDate : function(s) {
12926         if(!s){
12927             return 0;
12928         }
12929         if(s instanceof Date){
12930             return s.getTime();
12931         }
12932         return Date.parse(String(s));
12933     },
12934     
12935     /**
12936      * Float sorting
12937      * @param {Mixed} s The value being converted
12938      * @return {Float} The comparison value
12939      */
12940     asFloat : function(s) {
12941         var val = parseFloat(String(s).replace(/,/g, ""));
12942         if(isNaN(val)) {
12943             val = 0;
12944         }
12945         return val;
12946     },
12947     
12948     /**
12949      * Integer sorting
12950      * @param {Mixed} s The value being converted
12951      * @return {Number} The comparison value
12952      */
12953     asInt : function(s) {
12954         var val = parseInt(String(s).replace(/,/g, ""));
12955         if(isNaN(val)) {
12956             val = 0;
12957         }
12958         return val;
12959     }
12960 };/*
12961  * Based on:
12962  * Ext JS Library 1.1.1
12963  * Copyright(c) 2006-2007, Ext JS, LLC.
12964  *
12965  * Originally Released Under LGPL - original licence link has changed is not relivant.
12966  *
12967  * Fork - LGPL
12968  * <script type="text/javascript">
12969  */
12970
12971 /**
12972 * @class Roo.data.Record
12973  * Instances of this class encapsulate both record <em>definition</em> information, and record
12974  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12975  * to access Records cached in an {@link Roo.data.Store} object.<br>
12976  * <p>
12977  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12978  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12979  * objects.<br>
12980  * <p>
12981  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12982  * @constructor
12983  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12984  * {@link #create}. The parameters are the same.
12985  * @param {Array} data An associative Array of data values keyed by the field name.
12986  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12987  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12988  * not specified an integer id is generated.
12989  */
12990 Roo.data.Record = function(data, id){
12991     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12992     this.data = data;
12993 };
12994
12995 /**
12996  * Generate a constructor for a specific record layout.
12997  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12998  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12999  * Each field definition object may contain the following properties: <ul>
13000  * <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,
13001  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13002  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13003  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13004  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13005  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13006  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13007  * this may be omitted.</p></li>
13008  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13009  * <ul><li>auto (Default, implies no conversion)</li>
13010  * <li>string</li>
13011  * <li>int</li>
13012  * <li>float</li>
13013  * <li>boolean</li>
13014  * <li>date</li></ul></p></li>
13015  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13016  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13017  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13018  * by the Reader into an object that will be stored in the Record. It is passed the
13019  * following parameters:<ul>
13020  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13021  * </ul></p></li>
13022  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13023  * </ul>
13024  * <br>usage:<br><pre><code>
13025 var TopicRecord = Roo.data.Record.create(
13026     {name: 'title', mapping: 'topic_title'},
13027     {name: 'author', mapping: 'username'},
13028     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13029     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13030     {name: 'lastPoster', mapping: 'user2'},
13031     {name: 'excerpt', mapping: 'post_text'}
13032 );
13033
13034 var myNewRecord = new TopicRecord({
13035     title: 'Do my job please',
13036     author: 'noobie',
13037     totalPosts: 1,
13038     lastPost: new Date(),
13039     lastPoster: 'Animal',
13040     excerpt: 'No way dude!'
13041 });
13042 myStore.add(myNewRecord);
13043 </code></pre>
13044  * @method create
13045  * @static
13046  */
13047 Roo.data.Record.create = function(o){
13048     var f = function(){
13049         f.superclass.constructor.apply(this, arguments);
13050     };
13051     Roo.extend(f, Roo.data.Record);
13052     var p = f.prototype;
13053     p.fields = new Roo.util.MixedCollection(false, function(field){
13054         return field.name;
13055     });
13056     for(var i = 0, len = o.length; i < len; i++){
13057         p.fields.add(new Roo.data.Field(o[i]));
13058     }
13059     f.getField = function(name){
13060         return p.fields.get(name);  
13061     };
13062     return f;
13063 };
13064
13065 Roo.data.Record.AUTO_ID = 1000;
13066 Roo.data.Record.EDIT = 'edit';
13067 Roo.data.Record.REJECT = 'reject';
13068 Roo.data.Record.COMMIT = 'commit';
13069
13070 Roo.data.Record.prototype = {
13071     /**
13072      * Readonly flag - true if this record has been modified.
13073      * @type Boolean
13074      */
13075     dirty : false,
13076     editing : false,
13077     error: null,
13078     modified: null,
13079
13080     // private
13081     join : function(store){
13082         this.store = store;
13083     },
13084
13085     /**
13086      * Set the named field to the specified value.
13087      * @param {String} name The name of the field to set.
13088      * @param {Object} value The value to set the field to.
13089      */
13090     set : function(name, value){
13091         if(this.data[name] == value){
13092             return;
13093         }
13094         this.dirty = true;
13095         if(!this.modified){
13096             this.modified = {};
13097         }
13098         if(typeof this.modified[name] == 'undefined'){
13099             this.modified[name] = this.data[name];
13100         }
13101         this.data[name] = value;
13102         if(!this.editing && this.store){
13103             this.store.afterEdit(this);
13104         }       
13105     },
13106
13107     /**
13108      * Get the value of the named field.
13109      * @param {String} name The name of the field to get the value of.
13110      * @return {Object} The value of the field.
13111      */
13112     get : function(name){
13113         return this.data[name]; 
13114     },
13115
13116     // private
13117     beginEdit : function(){
13118         this.editing = true;
13119         this.modified = {}; 
13120     },
13121
13122     // private
13123     cancelEdit : function(){
13124         this.editing = false;
13125         delete this.modified;
13126     },
13127
13128     // private
13129     endEdit : function(){
13130         this.editing = false;
13131         if(this.dirty && this.store){
13132             this.store.afterEdit(this);
13133         }
13134     },
13135
13136     /**
13137      * Usually called by the {@link Roo.data.Store} which owns the Record.
13138      * Rejects all changes made to the Record since either creation, or the last commit operation.
13139      * Modified fields are reverted to their original values.
13140      * <p>
13141      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13142      * of reject operations.
13143      */
13144     reject : function(){
13145         var m = this.modified;
13146         for(var n in m){
13147             if(typeof m[n] != "function"){
13148                 this.data[n] = m[n];
13149             }
13150         }
13151         this.dirty = false;
13152         delete this.modified;
13153         this.editing = false;
13154         if(this.store){
13155             this.store.afterReject(this);
13156         }
13157     },
13158
13159     /**
13160      * Usually called by the {@link Roo.data.Store} which owns the Record.
13161      * Commits all changes made to the Record since either creation, or the last commit operation.
13162      * <p>
13163      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13164      * of commit operations.
13165      */
13166     commit : function(){
13167         this.dirty = false;
13168         delete this.modified;
13169         this.editing = false;
13170         if(this.store){
13171             this.store.afterCommit(this);
13172         }
13173     },
13174
13175     // private
13176     hasError : function(){
13177         return this.error != null;
13178     },
13179
13180     // private
13181     clearError : function(){
13182         this.error = null;
13183     },
13184
13185     /**
13186      * Creates a copy of this record.
13187      * @param {String} id (optional) A new record id if you don't want to use this record's id
13188      * @return {Record}
13189      */
13190     copy : function(newId) {
13191         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13192     }
13193 };/*
13194  * Based on:
13195  * Ext JS Library 1.1.1
13196  * Copyright(c) 2006-2007, Ext JS, LLC.
13197  *
13198  * Originally Released Under LGPL - original licence link has changed is not relivant.
13199  *
13200  * Fork - LGPL
13201  * <script type="text/javascript">
13202  */
13203
13204
13205
13206 /**
13207  * @class Roo.data.Store
13208  * @extends Roo.util.Observable
13209  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13210  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13211  * <p>
13212  * 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
13213  * has no knowledge of the format of the data returned by the Proxy.<br>
13214  * <p>
13215  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13216  * instances from the data object. These records are cached and made available through accessor functions.
13217  * @constructor
13218  * Creates a new Store.
13219  * @param {Object} config A config object containing the objects needed for the Store to access data,
13220  * and read the data into Records.
13221  */
13222 Roo.data.Store = function(config){
13223     this.data = new Roo.util.MixedCollection(false);
13224     this.data.getKey = function(o){
13225         return o.id;
13226     };
13227     this.baseParams = {};
13228     // private
13229     this.paramNames = {
13230         "start" : "start",
13231         "limit" : "limit",
13232         "sort" : "sort",
13233         "dir" : "dir",
13234         "multisort" : "_multisort"
13235     };
13236
13237     if(config && config.data){
13238         this.inlineData = config.data;
13239         delete config.data;
13240     }
13241
13242     Roo.apply(this, config);
13243     
13244     if(this.reader){ // reader passed
13245         this.reader = Roo.factory(this.reader, Roo.data);
13246         this.reader.xmodule = this.xmodule || false;
13247         if(!this.recordType){
13248             this.recordType = this.reader.recordType;
13249         }
13250         if(this.reader.onMetaChange){
13251             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13252         }
13253     }
13254
13255     if(this.recordType){
13256         this.fields = this.recordType.prototype.fields;
13257     }
13258     this.modified = [];
13259
13260     this.addEvents({
13261         /**
13262          * @event datachanged
13263          * Fires when the data cache has changed, and a widget which is using this Store
13264          * as a Record cache should refresh its view.
13265          * @param {Store} this
13266          */
13267         datachanged : true,
13268         /**
13269          * @event metachange
13270          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13271          * @param {Store} this
13272          * @param {Object} meta The JSON metadata
13273          */
13274         metachange : true,
13275         /**
13276          * @event add
13277          * Fires when Records have been added to the Store
13278          * @param {Store} this
13279          * @param {Roo.data.Record[]} records The array of Records added
13280          * @param {Number} index The index at which the record(s) were added
13281          */
13282         add : true,
13283         /**
13284          * @event remove
13285          * Fires when a Record has been removed from the Store
13286          * @param {Store} this
13287          * @param {Roo.data.Record} record The Record that was removed
13288          * @param {Number} index The index at which the record was removed
13289          */
13290         remove : true,
13291         /**
13292          * @event update
13293          * Fires when a Record has been updated
13294          * @param {Store} this
13295          * @param {Roo.data.Record} record The Record that was updated
13296          * @param {String} operation The update operation being performed.  Value may be one of:
13297          * <pre><code>
13298  Roo.data.Record.EDIT
13299  Roo.data.Record.REJECT
13300  Roo.data.Record.COMMIT
13301          * </code></pre>
13302          */
13303         update : true,
13304         /**
13305          * @event clear
13306          * Fires when the data cache has been cleared.
13307          * @param {Store} this
13308          */
13309         clear : true,
13310         /**
13311          * @event beforeload
13312          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13313          * the load action will be canceled.
13314          * @param {Store} this
13315          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13316          */
13317         beforeload : true,
13318         /**
13319          * @event beforeloadadd
13320          * Fires after a new set of Records has been loaded.
13321          * @param {Store} this
13322          * @param {Roo.data.Record[]} records The Records that were loaded
13323          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13324          */
13325         beforeloadadd : true,
13326         /**
13327          * @event load
13328          * Fires after a new set of Records has been loaded, before they are added to the store.
13329          * @param {Store} this
13330          * @param {Roo.data.Record[]} records The Records that were loaded
13331          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13332          * @params {Object} return from reader
13333          */
13334         load : true,
13335         /**
13336          * @event loadexception
13337          * Fires if an exception occurs in the Proxy during loading.
13338          * Called with the signature of the Proxy's "loadexception" event.
13339          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13340          * 
13341          * @param {Proxy} 
13342          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13343          * @param {Object} load options 
13344          * @param {Object} jsonData from your request (normally this contains the Exception)
13345          */
13346         loadexception : true
13347     });
13348     
13349     if(this.proxy){
13350         this.proxy = Roo.factory(this.proxy, Roo.data);
13351         this.proxy.xmodule = this.xmodule || false;
13352         this.relayEvents(this.proxy,  ["loadexception"]);
13353     }
13354     this.sortToggle = {};
13355     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13356
13357     Roo.data.Store.superclass.constructor.call(this);
13358
13359     if(this.inlineData){
13360         this.loadData(this.inlineData);
13361         delete this.inlineData;
13362     }
13363 };
13364
13365 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13366      /**
13367     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13368     * without a remote query - used by combo/forms at present.
13369     */
13370     
13371     /**
13372     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13373     */
13374     /**
13375     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13376     */
13377     /**
13378     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13379     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13380     */
13381     /**
13382     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13383     * on any HTTP request
13384     */
13385     /**
13386     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13387     */
13388     /**
13389     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13390     */
13391     multiSort: false,
13392     /**
13393     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13394     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13395     */
13396     remoteSort : false,
13397
13398     /**
13399     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13400      * loaded or when a record is removed. (defaults to false).
13401     */
13402     pruneModifiedRecords : false,
13403
13404     // private
13405     lastOptions : null,
13406
13407     /**
13408      * Add Records to the Store and fires the add event.
13409      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13410      */
13411     add : function(records){
13412         records = [].concat(records);
13413         for(var i = 0, len = records.length; i < len; i++){
13414             records[i].join(this);
13415         }
13416         var index = this.data.length;
13417         this.data.addAll(records);
13418         this.fireEvent("add", this, records, index);
13419     },
13420
13421     /**
13422      * Remove a Record from the Store and fires the remove event.
13423      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13424      */
13425     remove : function(record){
13426         var index = this.data.indexOf(record);
13427         this.data.removeAt(index);
13428  
13429         if(this.pruneModifiedRecords){
13430             this.modified.remove(record);
13431         }
13432         this.fireEvent("remove", this, record, index);
13433     },
13434
13435     /**
13436      * Remove all Records from the Store and fires the clear event.
13437      */
13438     removeAll : function(){
13439         this.data.clear();
13440         if(this.pruneModifiedRecords){
13441             this.modified = [];
13442         }
13443         this.fireEvent("clear", this);
13444     },
13445
13446     /**
13447      * Inserts Records to the Store at the given index and fires the add event.
13448      * @param {Number} index The start index at which to insert the passed Records.
13449      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13450      */
13451     insert : function(index, records){
13452         records = [].concat(records);
13453         for(var i = 0, len = records.length; i < len; i++){
13454             this.data.insert(index, records[i]);
13455             records[i].join(this);
13456         }
13457         this.fireEvent("add", this, records, index);
13458     },
13459
13460     /**
13461      * Get the index within the cache of the passed Record.
13462      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13463      * @return {Number} The index of the passed Record. Returns -1 if not found.
13464      */
13465     indexOf : function(record){
13466         return this.data.indexOf(record);
13467     },
13468
13469     /**
13470      * Get the index within the cache of the Record with the passed id.
13471      * @param {String} id The id of the Record to find.
13472      * @return {Number} The index of the Record. Returns -1 if not found.
13473      */
13474     indexOfId : function(id){
13475         return this.data.indexOfKey(id);
13476     },
13477
13478     /**
13479      * Get the Record with the specified id.
13480      * @param {String} id The id of the Record to find.
13481      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13482      */
13483     getById : function(id){
13484         return this.data.key(id);
13485     },
13486
13487     /**
13488      * Get the Record at the specified index.
13489      * @param {Number} index The index of the Record to find.
13490      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13491      */
13492     getAt : function(index){
13493         return this.data.itemAt(index);
13494     },
13495
13496     /**
13497      * Returns a range of Records between specified indices.
13498      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13499      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13500      * @return {Roo.data.Record[]} An array of Records
13501      */
13502     getRange : function(start, end){
13503         return this.data.getRange(start, end);
13504     },
13505
13506     // private
13507     storeOptions : function(o){
13508         o = Roo.apply({}, o);
13509         delete o.callback;
13510         delete o.scope;
13511         this.lastOptions = o;
13512     },
13513
13514     /**
13515      * Loads the Record cache from the configured Proxy using the configured Reader.
13516      * <p>
13517      * If using remote paging, then the first load call must specify the <em>start</em>
13518      * and <em>limit</em> properties in the options.params property to establish the initial
13519      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13520      * <p>
13521      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13522      * and this call will return before the new data has been loaded. Perform any post-processing
13523      * in a callback function, or in a "load" event handler.</strong>
13524      * <p>
13525      * @param {Object} options An object containing properties which control loading options:<ul>
13526      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13527      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13528      * passed the following arguments:<ul>
13529      * <li>r : Roo.data.Record[]</li>
13530      * <li>options: Options object from the load call</li>
13531      * <li>success: Boolean success indicator</li></ul></li>
13532      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13533      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13534      * </ul>
13535      */
13536     load : function(options){
13537         options = options || {};
13538         if(this.fireEvent("beforeload", this, options) !== false){
13539             this.storeOptions(options);
13540             var p = Roo.apply(options.params || {}, this.baseParams);
13541             // if meta was not loaded from remote source.. try requesting it.
13542             if (!this.reader.metaFromRemote) {
13543                 p._requestMeta = 1;
13544             }
13545             if(this.sortInfo && this.remoteSort){
13546                 var pn = this.paramNames;
13547                 p[pn["sort"]] = this.sortInfo.field;
13548                 p[pn["dir"]] = this.sortInfo.direction;
13549             }
13550             if (this.multiSort) {
13551                 var pn = this.paramNames;
13552                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13553             }
13554             
13555             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13556         }
13557     },
13558
13559     /**
13560      * Reloads the Record cache from the configured Proxy using the configured Reader and
13561      * the options from the last load operation performed.
13562      * @param {Object} options (optional) An object containing properties which may override the options
13563      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13564      * the most recently used options are reused).
13565      */
13566     reload : function(options){
13567         this.load(Roo.applyIf(options||{}, this.lastOptions));
13568     },
13569
13570     // private
13571     // Called as a callback by the Reader during a load operation.
13572     loadRecords : function(o, options, success){
13573         if(!o || success === false){
13574             if(success !== false){
13575                 this.fireEvent("load", this, [], options, o);
13576             }
13577             if(options.callback){
13578                 options.callback.call(options.scope || this, [], options, false);
13579             }
13580             return;
13581         }
13582         // if data returned failure - throw an exception.
13583         if (o.success === false) {
13584             // show a message if no listener is registered.
13585             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13586                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13587             }
13588             // loadmask wil be hooked into this..
13589             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13590             return;
13591         }
13592         var r = o.records, t = o.totalRecords || r.length;
13593         
13594         this.fireEvent("beforeloadadd", this, r, options, o);
13595         
13596         if(!options || options.add !== true){
13597             if(this.pruneModifiedRecords){
13598                 this.modified = [];
13599             }
13600             for(var i = 0, len = r.length; i < len; i++){
13601                 r[i].join(this);
13602             }
13603             if(this.snapshot){
13604                 this.data = this.snapshot;
13605                 delete this.snapshot;
13606             }
13607             this.data.clear();
13608             this.data.addAll(r);
13609             this.totalLength = t;
13610             this.applySort();
13611             this.fireEvent("datachanged", this);
13612         }else{
13613             this.totalLength = Math.max(t, this.data.length+r.length);
13614             this.add(r);
13615         }
13616         
13617         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13618                 
13619             var e = new Roo.data.Record({});
13620
13621             e.set(this.parent.displayField, this.parent.emptyTitle);
13622             e.set(this.parent.valueField, '');
13623
13624             this.insert(0, e);
13625         }
13626             
13627         this.fireEvent("load", this, r, options, o);
13628         if(options.callback){
13629             options.callback.call(options.scope || this, r, options, true);
13630         }
13631     },
13632
13633
13634     /**
13635      * Loads data from a passed data block. A Reader which understands the format of the data
13636      * must have been configured in the constructor.
13637      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13638      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13639      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13640      */
13641     loadData : function(o, append){
13642         var r = this.reader.readRecords(o);
13643         this.loadRecords(r, {add: append}, true);
13644     },
13645     
13646      /**
13647      * using 'cn' the nested child reader read the child array into it's child stores.
13648      * @param {Object} rec The record with a 'children array
13649      */
13650     loadDataFromChildren : function(rec)
13651     {
13652         this.loadData(this.reader.toLoadData(rec));
13653     },
13654     
13655
13656     /**
13657      * Gets the number of cached records.
13658      * <p>
13659      * <em>If using paging, this may not be the total size of the dataset. If the data object
13660      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13661      * the data set size</em>
13662      */
13663     getCount : function(){
13664         return this.data.length || 0;
13665     },
13666
13667     /**
13668      * Gets the total number of records in the dataset as returned by the server.
13669      * <p>
13670      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13671      * the dataset size</em>
13672      */
13673     getTotalCount : function(){
13674         return this.totalLength || 0;
13675     },
13676
13677     /**
13678      * Returns the sort state of the Store as an object with two properties:
13679      * <pre><code>
13680  field {String} The name of the field by which the Records are sorted
13681  direction {String} The sort order, "ASC" or "DESC"
13682      * </code></pre>
13683      */
13684     getSortState : function(){
13685         return this.sortInfo;
13686     },
13687
13688     // private
13689     applySort : function(){
13690         if(this.sortInfo && !this.remoteSort){
13691             var s = this.sortInfo, f = s.field;
13692             var st = this.fields.get(f).sortType;
13693             var fn = function(r1, r2){
13694                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13695                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13696             };
13697             this.data.sort(s.direction, fn);
13698             if(this.snapshot && this.snapshot != this.data){
13699                 this.snapshot.sort(s.direction, fn);
13700             }
13701         }
13702     },
13703
13704     /**
13705      * Sets the default sort column and order to be used by the next load operation.
13706      * @param {String} fieldName The name of the field to sort by.
13707      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13708      */
13709     setDefaultSort : function(field, dir){
13710         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13711     },
13712
13713     /**
13714      * Sort the Records.
13715      * If remote sorting is used, the sort is performed on the server, and the cache is
13716      * reloaded. If local sorting is used, the cache is sorted internally.
13717      * @param {String} fieldName The name of the field to sort by.
13718      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13719      */
13720     sort : function(fieldName, dir){
13721         var f = this.fields.get(fieldName);
13722         if(!dir){
13723             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13724             
13725             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13726                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13727             }else{
13728                 dir = f.sortDir;
13729             }
13730         }
13731         this.sortToggle[f.name] = dir;
13732         this.sortInfo = {field: f.name, direction: dir};
13733         if(!this.remoteSort){
13734             this.applySort();
13735             this.fireEvent("datachanged", this);
13736         }else{
13737             this.load(this.lastOptions);
13738         }
13739     },
13740
13741     /**
13742      * Calls the specified function for each of the Records in the cache.
13743      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13744      * Returning <em>false</em> aborts and exits the iteration.
13745      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13746      */
13747     each : function(fn, scope){
13748         this.data.each(fn, scope);
13749     },
13750
13751     /**
13752      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13753      * (e.g., during paging).
13754      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13755      */
13756     getModifiedRecords : function(){
13757         return this.modified;
13758     },
13759
13760     // private
13761     createFilterFn : function(property, value, anyMatch){
13762         if(!value.exec){ // not a regex
13763             value = String(value);
13764             if(value.length == 0){
13765                 return false;
13766             }
13767             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13768         }
13769         return function(r){
13770             return value.test(r.data[property]);
13771         };
13772     },
13773
13774     /**
13775      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13776      * @param {String} property A field on your records
13777      * @param {Number} start The record index to start at (defaults to 0)
13778      * @param {Number} end The last record index to include (defaults to length - 1)
13779      * @return {Number} The sum
13780      */
13781     sum : function(property, start, end){
13782         var rs = this.data.items, v = 0;
13783         start = start || 0;
13784         end = (end || end === 0) ? end : rs.length-1;
13785
13786         for(var i = start; i <= end; i++){
13787             v += (rs[i].data[property] || 0);
13788         }
13789         return v;
13790     },
13791
13792     /**
13793      * Filter the records by a specified property.
13794      * @param {String} field A field on your records
13795      * @param {String/RegExp} value Either a string that the field
13796      * should start with or a RegExp to test against the field
13797      * @param {Boolean} anyMatch True to match any part not just the beginning
13798      */
13799     filter : function(property, value, anyMatch){
13800         var fn = this.createFilterFn(property, value, anyMatch);
13801         return fn ? this.filterBy(fn) : this.clearFilter();
13802     },
13803
13804     /**
13805      * Filter by a function. The specified function will be called with each
13806      * record in this data source. If the function returns true the record is included,
13807      * otherwise it is filtered.
13808      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13809      * @param {Object} scope (optional) The scope of the function (defaults to this)
13810      */
13811     filterBy : function(fn, scope){
13812         this.snapshot = this.snapshot || this.data;
13813         this.data = this.queryBy(fn, scope||this);
13814         this.fireEvent("datachanged", this);
13815     },
13816
13817     /**
13818      * Query the records by a specified property.
13819      * @param {String} field A field on your records
13820      * @param {String/RegExp} value Either a string that the field
13821      * should start with or a RegExp to test against the field
13822      * @param {Boolean} anyMatch True to match any part not just the beginning
13823      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13824      */
13825     query : function(property, value, anyMatch){
13826         var fn = this.createFilterFn(property, value, anyMatch);
13827         return fn ? this.queryBy(fn) : this.data.clone();
13828     },
13829
13830     /**
13831      * Query by a function. The specified function will be called with each
13832      * record in this data source. If the function returns true the record is included
13833      * in the results.
13834      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13835      * @param {Object} scope (optional) The scope of the function (defaults to this)
13836       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13837      **/
13838     queryBy : function(fn, scope){
13839         var data = this.snapshot || this.data;
13840         return data.filterBy(fn, scope||this);
13841     },
13842
13843     /**
13844      * Collects unique values for a particular dataIndex from this store.
13845      * @param {String} dataIndex The property to collect
13846      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13847      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13848      * @return {Array} An array of the unique values
13849      **/
13850     collect : function(dataIndex, allowNull, bypassFilter){
13851         var d = (bypassFilter === true && this.snapshot) ?
13852                 this.snapshot.items : this.data.items;
13853         var v, sv, r = [], l = {};
13854         for(var i = 0, len = d.length; i < len; i++){
13855             v = d[i].data[dataIndex];
13856             sv = String(v);
13857             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13858                 l[sv] = true;
13859                 r[r.length] = v;
13860             }
13861         }
13862         return r;
13863     },
13864
13865     /**
13866      * Revert to a view of the Record cache with no filtering applied.
13867      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13868      */
13869     clearFilter : function(suppressEvent){
13870         if(this.snapshot && this.snapshot != this.data){
13871             this.data = this.snapshot;
13872             delete this.snapshot;
13873             if(suppressEvent !== true){
13874                 this.fireEvent("datachanged", this);
13875             }
13876         }
13877     },
13878
13879     // private
13880     afterEdit : function(record){
13881         if(this.modified.indexOf(record) == -1){
13882             this.modified.push(record);
13883         }
13884         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13885     },
13886     
13887     // private
13888     afterReject : function(record){
13889         this.modified.remove(record);
13890         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13891     },
13892
13893     // private
13894     afterCommit : function(record){
13895         this.modified.remove(record);
13896         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13897     },
13898
13899     /**
13900      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13901      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13902      */
13903     commitChanges : function(){
13904         var m = this.modified.slice(0);
13905         this.modified = [];
13906         for(var i = 0, len = m.length; i < len; i++){
13907             m[i].commit();
13908         }
13909     },
13910
13911     /**
13912      * Cancel outstanding changes on all changed records.
13913      */
13914     rejectChanges : function(){
13915         var m = this.modified.slice(0);
13916         this.modified = [];
13917         for(var i = 0, len = m.length; i < len; i++){
13918             m[i].reject();
13919         }
13920     },
13921
13922     onMetaChange : function(meta, rtype, o){
13923         this.recordType = rtype;
13924         this.fields = rtype.prototype.fields;
13925         delete this.snapshot;
13926         this.sortInfo = meta.sortInfo || this.sortInfo;
13927         this.modified = [];
13928         this.fireEvent('metachange', this, this.reader.meta);
13929     },
13930     
13931     moveIndex : function(data, type)
13932     {
13933         var index = this.indexOf(data);
13934         
13935         var newIndex = index + type;
13936         
13937         this.remove(data);
13938         
13939         this.insert(newIndex, data);
13940         
13941     }
13942 });/*
13943  * Based on:
13944  * Ext JS Library 1.1.1
13945  * Copyright(c) 2006-2007, Ext JS, LLC.
13946  *
13947  * Originally Released Under LGPL - original licence link has changed is not relivant.
13948  *
13949  * Fork - LGPL
13950  * <script type="text/javascript">
13951  */
13952
13953 /**
13954  * @class Roo.data.SimpleStore
13955  * @extends Roo.data.Store
13956  * Small helper class to make creating Stores from Array data easier.
13957  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13958  * @cfg {Array} fields An array of field definition objects, or field name strings.
13959  * @cfg {Object} an existing reader (eg. copied from another store)
13960  * @cfg {Array} data The multi-dimensional array of data
13961  * @constructor
13962  * @param {Object} config
13963  */
13964 Roo.data.SimpleStore = function(config)
13965 {
13966     Roo.data.SimpleStore.superclass.constructor.call(this, {
13967         isLocal : true,
13968         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13969                 id: config.id
13970             },
13971             Roo.data.Record.create(config.fields)
13972         ),
13973         proxy : new Roo.data.MemoryProxy(config.data)
13974     });
13975     this.load();
13976 };
13977 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13978  * Based on:
13979  * Ext JS Library 1.1.1
13980  * Copyright(c) 2006-2007, Ext JS, LLC.
13981  *
13982  * Originally Released Under LGPL - original licence link has changed is not relivant.
13983  *
13984  * Fork - LGPL
13985  * <script type="text/javascript">
13986  */
13987
13988 /**
13989 /**
13990  * @extends Roo.data.Store
13991  * @class Roo.data.JsonStore
13992  * Small helper class to make creating Stores for JSON data easier. <br/>
13993 <pre><code>
13994 var store = new Roo.data.JsonStore({
13995     url: 'get-images.php',
13996     root: 'images',
13997     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13998 });
13999 </code></pre>
14000  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14001  * JsonReader and HttpProxy (unless inline data is provided).</b>
14002  * @cfg {Array} fields An array of field definition objects, or field name strings.
14003  * @constructor
14004  * @param {Object} config
14005  */
14006 Roo.data.JsonStore = function(c){
14007     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14008         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14009         reader: new Roo.data.JsonReader(c, c.fields)
14010     }));
14011 };
14012 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14013  * Based on:
14014  * Ext JS Library 1.1.1
14015  * Copyright(c) 2006-2007, Ext JS, LLC.
14016  *
14017  * Originally Released Under LGPL - original licence link has changed is not relivant.
14018  *
14019  * Fork - LGPL
14020  * <script type="text/javascript">
14021  */
14022
14023  
14024 Roo.data.Field = function(config){
14025     if(typeof config == "string"){
14026         config = {name: config};
14027     }
14028     Roo.apply(this, config);
14029     
14030     if(!this.type){
14031         this.type = "auto";
14032     }
14033     
14034     var st = Roo.data.SortTypes;
14035     // named sortTypes are supported, here we look them up
14036     if(typeof this.sortType == "string"){
14037         this.sortType = st[this.sortType];
14038     }
14039     
14040     // set default sortType for strings and dates
14041     if(!this.sortType){
14042         switch(this.type){
14043             case "string":
14044                 this.sortType = st.asUCString;
14045                 break;
14046             case "date":
14047                 this.sortType = st.asDate;
14048                 break;
14049             default:
14050                 this.sortType = st.none;
14051         }
14052     }
14053
14054     // define once
14055     var stripRe = /[\$,%]/g;
14056
14057     // prebuilt conversion function for this field, instead of
14058     // switching every time we're reading a value
14059     if(!this.convert){
14060         var cv, dateFormat = this.dateFormat;
14061         switch(this.type){
14062             case "":
14063             case "auto":
14064             case undefined:
14065                 cv = function(v){ return v; };
14066                 break;
14067             case "string":
14068                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14069                 break;
14070             case "int":
14071                 cv = function(v){
14072                     return v !== undefined && v !== null && v !== '' ?
14073                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14074                     };
14075                 break;
14076             case "float":
14077                 cv = function(v){
14078                     return v !== undefined && v !== null && v !== '' ?
14079                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14080                     };
14081                 break;
14082             case "bool":
14083             case "boolean":
14084                 cv = function(v){ return v === true || v === "true" || v == 1; };
14085                 break;
14086             case "date":
14087                 cv = function(v){
14088                     if(!v){
14089                         return '';
14090                     }
14091                     if(v instanceof Date){
14092                         return v;
14093                     }
14094                     if(dateFormat){
14095                         if(dateFormat == "timestamp"){
14096                             return new Date(v*1000);
14097                         }
14098                         return Date.parseDate(v, dateFormat);
14099                     }
14100                     var parsed = Date.parse(v);
14101                     return parsed ? new Date(parsed) : null;
14102                 };
14103              break;
14104             
14105         }
14106         this.convert = cv;
14107     }
14108 };
14109
14110 Roo.data.Field.prototype = {
14111     dateFormat: null,
14112     defaultValue: "",
14113     mapping: null,
14114     sortType : null,
14115     sortDir : "ASC"
14116 };/*
14117  * Based on:
14118  * Ext JS Library 1.1.1
14119  * Copyright(c) 2006-2007, Ext JS, LLC.
14120  *
14121  * Originally Released Under LGPL - original licence link has changed is not relivant.
14122  *
14123  * Fork - LGPL
14124  * <script type="text/javascript">
14125  */
14126  
14127 // Base class for reading structured data from a data source.  This class is intended to be
14128 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14129
14130 /**
14131  * @class Roo.data.DataReader
14132  * Base class for reading structured data from a data source.  This class is intended to be
14133  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14134  */
14135
14136 Roo.data.DataReader = function(meta, recordType){
14137     
14138     this.meta = meta;
14139     
14140     this.recordType = recordType instanceof Array ? 
14141         Roo.data.Record.create(recordType) : recordType;
14142 };
14143
14144 Roo.data.DataReader.prototype = {
14145     
14146     
14147     readerType : 'Data',
14148      /**
14149      * Create an empty record
14150      * @param {Object} data (optional) - overlay some values
14151      * @return {Roo.data.Record} record created.
14152      */
14153     newRow :  function(d) {
14154         var da =  {};
14155         this.recordType.prototype.fields.each(function(c) {
14156             switch( c.type) {
14157                 case 'int' : da[c.name] = 0; break;
14158                 case 'date' : da[c.name] = new Date(); break;
14159                 case 'float' : da[c.name] = 0.0; break;
14160                 case 'boolean' : da[c.name] = false; break;
14161                 default : da[c.name] = ""; break;
14162             }
14163             
14164         });
14165         return new this.recordType(Roo.apply(da, d));
14166     }
14167     
14168     
14169 };/*
14170  * Based on:
14171  * Ext JS Library 1.1.1
14172  * Copyright(c) 2006-2007, Ext JS, LLC.
14173  *
14174  * Originally Released Under LGPL - original licence link has changed is not relivant.
14175  *
14176  * Fork - LGPL
14177  * <script type="text/javascript">
14178  */
14179
14180 /**
14181  * @class Roo.data.DataProxy
14182  * @extends Roo.data.Observable
14183  * This class is an abstract base class for implementations which provide retrieval of
14184  * unformatted data objects.<br>
14185  * <p>
14186  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14187  * (of the appropriate type which knows how to parse the data object) to provide a block of
14188  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14189  * <p>
14190  * Custom implementations must implement the load method as described in
14191  * {@link Roo.data.HttpProxy#load}.
14192  */
14193 Roo.data.DataProxy = function(){
14194     this.addEvents({
14195         /**
14196          * @event beforeload
14197          * Fires before a network request is made to retrieve a data object.
14198          * @param {Object} This DataProxy object.
14199          * @param {Object} params The params parameter to the load function.
14200          */
14201         beforeload : true,
14202         /**
14203          * @event load
14204          * Fires before the load method's callback is called.
14205          * @param {Object} This DataProxy object.
14206          * @param {Object} o The data object.
14207          * @param {Object} arg The callback argument object passed to the load function.
14208          */
14209         load : true,
14210         /**
14211          * @event loadexception
14212          * Fires if an Exception occurs during data retrieval.
14213          * @param {Object} This DataProxy object.
14214          * @param {Object} o The data object.
14215          * @param {Object} arg The callback argument object passed to the load function.
14216          * @param {Object} e The Exception.
14217          */
14218         loadexception : true
14219     });
14220     Roo.data.DataProxy.superclass.constructor.call(this);
14221 };
14222
14223 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14224
14225     /**
14226      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14227      */
14228 /*
14229  * Based on:
14230  * Ext JS Library 1.1.1
14231  * Copyright(c) 2006-2007, Ext JS, LLC.
14232  *
14233  * Originally Released Under LGPL - original licence link has changed is not relivant.
14234  *
14235  * Fork - LGPL
14236  * <script type="text/javascript">
14237  */
14238 /**
14239  * @class Roo.data.MemoryProxy
14240  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14241  * to the Reader when its load method is called.
14242  * @constructor
14243  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14244  */
14245 Roo.data.MemoryProxy = function(data){
14246     if (data.data) {
14247         data = data.data;
14248     }
14249     Roo.data.MemoryProxy.superclass.constructor.call(this);
14250     this.data = data;
14251 };
14252
14253 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14254     
14255     /**
14256      * Load data from the requested source (in this case an in-memory
14257      * data object passed to the constructor), read the data object into
14258      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14259      * process that block using the passed callback.
14260      * @param {Object} params This parameter is not used by the MemoryProxy class.
14261      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14262      * object into a block of Roo.data.Records.
14263      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14264      * The function must be passed <ul>
14265      * <li>The Record block object</li>
14266      * <li>The "arg" argument from the load function</li>
14267      * <li>A boolean success indicator</li>
14268      * </ul>
14269      * @param {Object} scope The scope in which to call the callback
14270      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14271      */
14272     load : function(params, reader, callback, scope, arg){
14273         params = params || {};
14274         var result;
14275         try {
14276             result = reader.readRecords(params.data ? params.data :this.data);
14277         }catch(e){
14278             this.fireEvent("loadexception", this, arg, null, e);
14279             callback.call(scope, null, arg, false);
14280             return;
14281         }
14282         callback.call(scope, result, arg, true);
14283     },
14284     
14285     // private
14286     update : function(params, records){
14287         
14288     }
14289 });/*
14290  * Based on:
14291  * Ext JS Library 1.1.1
14292  * Copyright(c) 2006-2007, Ext JS, LLC.
14293  *
14294  * Originally Released Under LGPL - original licence link has changed is not relivant.
14295  *
14296  * Fork - LGPL
14297  * <script type="text/javascript">
14298  */
14299 /**
14300  * @class Roo.data.HttpProxy
14301  * @extends Roo.data.DataProxy
14302  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14303  * configured to reference a certain URL.<br><br>
14304  * <p>
14305  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14306  * from which the running page was served.<br><br>
14307  * <p>
14308  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14309  * <p>
14310  * Be aware that to enable the browser to parse an XML document, the server must set
14311  * the Content-Type header in the HTTP response to "text/xml".
14312  * @constructor
14313  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14314  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14315  * will be used to make the request.
14316  */
14317 Roo.data.HttpProxy = function(conn){
14318     Roo.data.HttpProxy.superclass.constructor.call(this);
14319     // is conn a conn config or a real conn?
14320     this.conn = conn;
14321     this.useAjax = !conn || !conn.events;
14322   
14323 };
14324
14325 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14326     // thse are take from connection...
14327     
14328     /**
14329      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14330      */
14331     /**
14332      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14333      * extra parameters to each request made by this object. (defaults to undefined)
14334      */
14335     /**
14336      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14337      *  to each request made by this object. (defaults to undefined)
14338      */
14339     /**
14340      * @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)
14341      */
14342     /**
14343      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14344      */
14345      /**
14346      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14347      * @type Boolean
14348      */
14349   
14350
14351     /**
14352      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14353      * @type Boolean
14354      */
14355     /**
14356      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14357      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14358      * a finer-grained basis than the DataProxy events.
14359      */
14360     getConnection : function(){
14361         return this.useAjax ? Roo.Ajax : this.conn;
14362     },
14363
14364     /**
14365      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14366      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14367      * process that block using the passed callback.
14368      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14369      * for the request to the remote server.
14370      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14371      * object into a block of Roo.data.Records.
14372      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14373      * The function must be passed <ul>
14374      * <li>The Record block object</li>
14375      * <li>The "arg" argument from the load function</li>
14376      * <li>A boolean success indicator</li>
14377      * </ul>
14378      * @param {Object} scope The scope in which to call the callback
14379      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14380      */
14381     load : function(params, reader, callback, scope, arg){
14382         if(this.fireEvent("beforeload", this, params) !== false){
14383             var  o = {
14384                 params : params || {},
14385                 request: {
14386                     callback : callback,
14387                     scope : scope,
14388                     arg : arg
14389                 },
14390                 reader: reader,
14391                 callback : this.loadResponse,
14392                 scope: this
14393             };
14394             if(this.useAjax){
14395                 Roo.applyIf(o, this.conn);
14396                 if(this.activeRequest){
14397                     Roo.Ajax.abort(this.activeRequest);
14398                 }
14399                 this.activeRequest = Roo.Ajax.request(o);
14400             }else{
14401                 this.conn.request(o);
14402             }
14403         }else{
14404             callback.call(scope||this, null, arg, false);
14405         }
14406     },
14407
14408     // private
14409     loadResponse : function(o, success, response){
14410         delete this.activeRequest;
14411         if(!success){
14412             this.fireEvent("loadexception", this, o, response);
14413             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14414             return;
14415         }
14416         var result;
14417         try {
14418             result = o.reader.read(response);
14419         }catch(e){
14420             this.fireEvent("loadexception", this, o, response, e);
14421             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14422             return;
14423         }
14424         
14425         this.fireEvent("load", this, o, o.request.arg);
14426         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14427     },
14428
14429     // private
14430     update : function(dataSet){
14431
14432     },
14433
14434     // private
14435     updateResponse : function(dataSet){
14436
14437     }
14438 });/*
14439  * Based on:
14440  * Ext JS Library 1.1.1
14441  * Copyright(c) 2006-2007, Ext JS, LLC.
14442  *
14443  * Originally Released Under LGPL - original licence link has changed is not relivant.
14444  *
14445  * Fork - LGPL
14446  * <script type="text/javascript">
14447  */
14448
14449 /**
14450  * @class Roo.data.ScriptTagProxy
14451  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14452  * other than the originating domain of the running page.<br><br>
14453  * <p>
14454  * <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
14455  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14456  * <p>
14457  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14458  * source code that is used as the source inside a &lt;script> tag.<br><br>
14459  * <p>
14460  * In order for the browser to process the returned data, the server must wrap the data object
14461  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14462  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14463  * depending on whether the callback name was passed:
14464  * <p>
14465  * <pre><code>
14466 boolean scriptTag = false;
14467 String cb = request.getParameter("callback");
14468 if (cb != null) {
14469     scriptTag = true;
14470     response.setContentType("text/javascript");
14471 } else {
14472     response.setContentType("application/x-json");
14473 }
14474 Writer out = response.getWriter();
14475 if (scriptTag) {
14476     out.write(cb + "(");
14477 }
14478 out.print(dataBlock.toJsonString());
14479 if (scriptTag) {
14480     out.write(");");
14481 }
14482 </pre></code>
14483  *
14484  * @constructor
14485  * @param {Object} config A configuration object.
14486  */
14487 Roo.data.ScriptTagProxy = function(config){
14488     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14489     Roo.apply(this, config);
14490     this.head = document.getElementsByTagName("head")[0];
14491 };
14492
14493 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14494
14495 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14496     /**
14497      * @cfg {String} url The URL from which to request the data object.
14498      */
14499     /**
14500      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14501      */
14502     timeout : 30000,
14503     /**
14504      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14505      * the server the name of the callback function set up by the load call to process the returned data object.
14506      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14507      * javascript output which calls this named function passing the data object as its only parameter.
14508      */
14509     callbackParam : "callback",
14510     /**
14511      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14512      * name to the request.
14513      */
14514     nocache : true,
14515
14516     /**
14517      * Load data from the configured URL, read the data object into
14518      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14519      * process that block using the passed callback.
14520      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14521      * for the request to the remote server.
14522      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14523      * object into a block of Roo.data.Records.
14524      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14525      * The function must be passed <ul>
14526      * <li>The Record block object</li>
14527      * <li>The "arg" argument from the load function</li>
14528      * <li>A boolean success indicator</li>
14529      * </ul>
14530      * @param {Object} scope The scope in which to call the callback
14531      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14532      */
14533     load : function(params, reader, callback, scope, arg){
14534         if(this.fireEvent("beforeload", this, params) !== false){
14535
14536             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14537
14538             var url = this.url;
14539             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14540             if(this.nocache){
14541                 url += "&_dc=" + (new Date().getTime());
14542             }
14543             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14544             var trans = {
14545                 id : transId,
14546                 cb : "stcCallback"+transId,
14547                 scriptId : "stcScript"+transId,
14548                 params : params,
14549                 arg : arg,
14550                 url : url,
14551                 callback : callback,
14552                 scope : scope,
14553                 reader : reader
14554             };
14555             var conn = this;
14556
14557             window[trans.cb] = function(o){
14558                 conn.handleResponse(o, trans);
14559             };
14560
14561             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14562
14563             if(this.autoAbort !== false){
14564                 this.abort();
14565             }
14566
14567             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14568
14569             var script = document.createElement("script");
14570             script.setAttribute("src", url);
14571             script.setAttribute("type", "text/javascript");
14572             script.setAttribute("id", trans.scriptId);
14573             this.head.appendChild(script);
14574
14575             this.trans = trans;
14576         }else{
14577             callback.call(scope||this, null, arg, false);
14578         }
14579     },
14580
14581     // private
14582     isLoading : function(){
14583         return this.trans ? true : false;
14584     },
14585
14586     /**
14587      * Abort the current server request.
14588      */
14589     abort : function(){
14590         if(this.isLoading()){
14591             this.destroyTrans(this.trans);
14592         }
14593     },
14594
14595     // private
14596     destroyTrans : function(trans, isLoaded){
14597         this.head.removeChild(document.getElementById(trans.scriptId));
14598         clearTimeout(trans.timeoutId);
14599         if(isLoaded){
14600             window[trans.cb] = undefined;
14601             try{
14602                 delete window[trans.cb];
14603             }catch(e){}
14604         }else{
14605             // if hasn't been loaded, wait for load to remove it to prevent script error
14606             window[trans.cb] = function(){
14607                 window[trans.cb] = undefined;
14608                 try{
14609                     delete window[trans.cb];
14610                 }catch(e){}
14611             };
14612         }
14613     },
14614
14615     // private
14616     handleResponse : function(o, trans){
14617         this.trans = false;
14618         this.destroyTrans(trans, true);
14619         var result;
14620         try {
14621             result = trans.reader.readRecords(o);
14622         }catch(e){
14623             this.fireEvent("loadexception", this, o, trans.arg, e);
14624             trans.callback.call(trans.scope||window, null, trans.arg, false);
14625             return;
14626         }
14627         this.fireEvent("load", this, o, trans.arg);
14628         trans.callback.call(trans.scope||window, result, trans.arg, true);
14629     },
14630
14631     // private
14632     handleFailure : function(trans){
14633         this.trans = false;
14634         this.destroyTrans(trans, false);
14635         this.fireEvent("loadexception", this, null, trans.arg);
14636         trans.callback.call(trans.scope||window, null, trans.arg, false);
14637     }
14638 });/*
14639  * Based on:
14640  * Ext JS Library 1.1.1
14641  * Copyright(c) 2006-2007, Ext JS, LLC.
14642  *
14643  * Originally Released Under LGPL - original licence link has changed is not relivant.
14644  *
14645  * Fork - LGPL
14646  * <script type="text/javascript">
14647  */
14648
14649 /**
14650  * @class Roo.data.JsonReader
14651  * @extends Roo.data.DataReader
14652  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14653  * based on mappings in a provided Roo.data.Record constructor.
14654  * 
14655  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14656  * in the reply previously. 
14657  * 
14658  * <p>
14659  * Example code:
14660  * <pre><code>
14661 var RecordDef = Roo.data.Record.create([
14662     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14663     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14664 ]);
14665 var myReader = new Roo.data.JsonReader({
14666     totalProperty: "results",    // The property which contains the total dataset size (optional)
14667     root: "rows",                // The property which contains an Array of row objects
14668     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14669 }, RecordDef);
14670 </code></pre>
14671  * <p>
14672  * This would consume a JSON file like this:
14673  * <pre><code>
14674 { 'results': 2, 'rows': [
14675     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14676     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14677 }
14678 </code></pre>
14679  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14680  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14681  * paged from the remote server.
14682  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14683  * @cfg {String} root name of the property which contains the Array of row objects.
14684  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14685  * @cfg {Array} fields Array of field definition objects
14686  * @constructor
14687  * Create a new JsonReader
14688  * @param {Object} meta Metadata configuration options
14689  * @param {Object} recordType Either an Array of field definition objects,
14690  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14691  */
14692 Roo.data.JsonReader = function(meta, recordType){
14693     
14694     meta = meta || {};
14695     // set some defaults:
14696     Roo.applyIf(meta, {
14697         totalProperty: 'total',
14698         successProperty : 'success',
14699         root : 'data',
14700         id : 'id'
14701     });
14702     
14703     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14704 };
14705 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14706     
14707     readerType : 'Json',
14708     
14709     /**
14710      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14711      * Used by Store query builder to append _requestMeta to params.
14712      * 
14713      */
14714     metaFromRemote : false,
14715     /**
14716      * This method is only used by a DataProxy which has retrieved data from a remote server.
14717      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14718      * @return {Object} data A data block which is used by an Roo.data.Store object as
14719      * a cache of Roo.data.Records.
14720      */
14721     read : function(response){
14722         var json = response.responseText;
14723        
14724         var o = /* eval:var:o */ eval("("+json+")");
14725         if(!o) {
14726             throw {message: "JsonReader.read: Json object not found"};
14727         }
14728         
14729         if(o.metaData){
14730             
14731             delete this.ef;
14732             this.metaFromRemote = true;
14733             this.meta = o.metaData;
14734             this.recordType = Roo.data.Record.create(o.metaData.fields);
14735             this.onMetaChange(this.meta, this.recordType, o);
14736         }
14737         return this.readRecords(o);
14738     },
14739
14740     // private function a store will implement
14741     onMetaChange : function(meta, recordType, o){
14742
14743     },
14744
14745     /**
14746          * @ignore
14747          */
14748     simpleAccess: function(obj, subsc) {
14749         return obj[subsc];
14750     },
14751
14752         /**
14753          * @ignore
14754          */
14755     getJsonAccessor: function(){
14756         var re = /[\[\.]/;
14757         return function(expr) {
14758             try {
14759                 return(re.test(expr))
14760                     ? new Function("obj", "return obj." + expr)
14761                     : function(obj){
14762                         return obj[expr];
14763                     };
14764             } catch(e){}
14765             return Roo.emptyFn;
14766         };
14767     }(),
14768
14769     /**
14770      * Create a data block containing Roo.data.Records from an XML document.
14771      * @param {Object} o An object which contains an Array of row objects in the property specified
14772      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14773      * which contains the total size of the dataset.
14774      * @return {Object} data A data block which is used by an Roo.data.Store object as
14775      * a cache of Roo.data.Records.
14776      */
14777     readRecords : function(o){
14778         /**
14779          * After any data loads, the raw JSON data is available for further custom processing.
14780          * @type Object
14781          */
14782         this.o = o;
14783         var s = this.meta, Record = this.recordType,
14784             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14785
14786 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14787         if (!this.ef) {
14788             if(s.totalProperty) {
14789                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14790                 }
14791                 if(s.successProperty) {
14792                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14793                 }
14794                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14795                 if (s.id) {
14796                         var g = this.getJsonAccessor(s.id);
14797                         this.getId = function(rec) {
14798                                 var r = g(rec);  
14799                                 return (r === undefined || r === "") ? null : r;
14800                         };
14801                 } else {
14802                         this.getId = function(){return null;};
14803                 }
14804             this.ef = [];
14805             for(var jj = 0; jj < fl; jj++){
14806                 f = fi[jj];
14807                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14808                 this.ef[jj] = this.getJsonAccessor(map);
14809             }
14810         }
14811
14812         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14813         if(s.totalProperty){
14814             var vt = parseInt(this.getTotal(o), 10);
14815             if(!isNaN(vt)){
14816                 totalRecords = vt;
14817             }
14818         }
14819         if(s.successProperty){
14820             var vs = this.getSuccess(o);
14821             if(vs === false || vs === 'false'){
14822                 success = false;
14823             }
14824         }
14825         var records = [];
14826         for(var i = 0; i < c; i++){
14827                 var n = root[i];
14828             var values = {};
14829             var id = this.getId(n);
14830             for(var j = 0; j < fl; j++){
14831                 f = fi[j];
14832             var v = this.ef[j](n);
14833             if (!f.convert) {
14834                 Roo.log('missing convert for ' + f.name);
14835                 Roo.log(f);
14836                 continue;
14837             }
14838             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14839             }
14840             var record = new Record(values, id);
14841             record.json = n;
14842             records[i] = record;
14843         }
14844         return {
14845             raw : o,
14846             success : success,
14847             records : records,
14848             totalRecords : totalRecords
14849         };
14850     },
14851     // used when loading children.. @see loadDataFromChildren
14852     toLoadData: function(rec)
14853     {
14854         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14855         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14856         return { data : data, total : data.length };
14857         
14858     }
14859 });/*
14860  * Based on:
14861  * Ext JS Library 1.1.1
14862  * Copyright(c) 2006-2007, Ext JS, LLC.
14863  *
14864  * Originally Released Under LGPL - original licence link has changed is not relivant.
14865  *
14866  * Fork - LGPL
14867  * <script type="text/javascript">
14868  */
14869
14870 /**
14871  * @class Roo.data.ArrayReader
14872  * @extends Roo.data.DataReader
14873  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14874  * Each element of that Array represents a row of data fields. The
14875  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14876  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14877  * <p>
14878  * Example code:.
14879  * <pre><code>
14880 var RecordDef = Roo.data.Record.create([
14881     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14882     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14883 ]);
14884 var myReader = new Roo.data.ArrayReader({
14885     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14886 }, RecordDef);
14887 </code></pre>
14888  * <p>
14889  * This would consume an Array like this:
14890  * <pre><code>
14891 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14892   </code></pre>
14893  
14894  * @constructor
14895  * Create a new JsonReader
14896  * @param {Object} meta Metadata configuration options.
14897  * @param {Object|Array} recordType Either an Array of field definition objects
14898  * 
14899  * @cfg {Array} fields Array of field definition objects
14900  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14901  * as specified to {@link Roo.data.Record#create},
14902  * or an {@link Roo.data.Record} object
14903  *
14904  * 
14905  * created using {@link Roo.data.Record#create}.
14906  */
14907 Roo.data.ArrayReader = function(meta, recordType)
14908 {    
14909     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14910 };
14911
14912 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14913     
14914       /**
14915      * Create a data block containing Roo.data.Records from an XML document.
14916      * @param {Object} o An Array of row objects which represents the dataset.
14917      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14918      * a cache of Roo.data.Records.
14919      */
14920     readRecords : function(o)
14921     {
14922         var sid = this.meta ? this.meta.id : null;
14923         var recordType = this.recordType, fields = recordType.prototype.fields;
14924         var records = [];
14925         var root = o;
14926         for(var i = 0; i < root.length; i++){
14927                 var n = root[i];
14928             var values = {};
14929             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14930             for(var j = 0, jlen = fields.length; j < jlen; j++){
14931                 var f = fields.items[j];
14932                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14933                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14934                 v = f.convert(v);
14935                 values[f.name] = v;
14936             }
14937             var record = new recordType(values, id);
14938             record.json = n;
14939             records[records.length] = record;
14940         }
14941         return {
14942             records : records,
14943             totalRecords : records.length
14944         };
14945     },
14946     // used when loading children.. @see loadDataFromChildren
14947     toLoadData: function(rec)
14948     {
14949         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14950         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14951         
14952     }
14953     
14954     
14955 });/*
14956  * - LGPL
14957  * * 
14958  */
14959
14960 /**
14961  * @class Roo.bootstrap.ComboBox
14962  * @extends Roo.bootstrap.TriggerField
14963  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14964  * @cfg {Boolean} append (true|false) default false
14965  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14966  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14967  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14968  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14969  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14970  * @cfg {Boolean} animate default true
14971  * @cfg {Boolean} emptyResultText only for touch device
14972  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14973  * @cfg {String} emptyTitle default ''
14974  * @cfg {Number} width fixed with? experimental
14975  * @constructor
14976  * Create a new ComboBox.
14977  * @param {Object} config Configuration options
14978  */
14979 Roo.bootstrap.ComboBox = function(config){
14980     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14981     this.addEvents({
14982         /**
14983          * @event expand
14984          * Fires when the dropdown list is expanded
14985         * @param {Roo.bootstrap.ComboBox} combo This combo box
14986         */
14987         'expand' : true,
14988         /**
14989          * @event collapse
14990          * Fires when the dropdown list is collapsed
14991         * @param {Roo.bootstrap.ComboBox} combo This combo box
14992         */
14993         'collapse' : true,
14994         /**
14995          * @event beforeselect
14996          * Fires before a list item is selected. Return false to cancel the selection.
14997         * @param {Roo.bootstrap.ComboBox} combo This combo box
14998         * @param {Roo.data.Record} record The data record returned from the underlying store
14999         * @param {Number} index The index of the selected item in the dropdown list
15000         */
15001         'beforeselect' : true,
15002         /**
15003          * @event select
15004          * Fires when a list item is selected
15005         * @param {Roo.bootstrap.ComboBox} combo This combo box
15006         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15007         * @param {Number} index The index of the selected item in the dropdown list
15008         */
15009         'select' : true,
15010         /**
15011          * @event beforequery
15012          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15013          * The event object passed has these properties:
15014         * @param {Roo.bootstrap.ComboBox} combo This combo box
15015         * @param {String} query The query
15016         * @param {Boolean} forceAll true to force "all" query
15017         * @param {Boolean} cancel true to cancel the query
15018         * @param {Object} e The query event object
15019         */
15020         'beforequery': true,
15021          /**
15022          * @event add
15023          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15024         * @param {Roo.bootstrap.ComboBox} combo This combo box
15025         */
15026         'add' : true,
15027         /**
15028          * @event edit
15029          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15030         * @param {Roo.bootstrap.ComboBox} combo This combo box
15031         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15032         */
15033         'edit' : true,
15034         /**
15035          * @event remove
15036          * Fires when the remove value from the combobox array
15037         * @param {Roo.bootstrap.ComboBox} combo This combo box
15038         */
15039         'remove' : true,
15040         /**
15041          * @event afterremove
15042          * Fires when the remove value from the combobox array
15043         * @param {Roo.bootstrap.ComboBox} combo This combo box
15044         */
15045         'afterremove' : true,
15046         /**
15047          * @event specialfilter
15048          * Fires when specialfilter
15049             * @param {Roo.bootstrap.ComboBox} combo This combo box
15050             */
15051         'specialfilter' : true,
15052         /**
15053          * @event tick
15054          * Fires when tick the element
15055             * @param {Roo.bootstrap.ComboBox} combo This combo box
15056             */
15057         'tick' : true,
15058         /**
15059          * @event touchviewdisplay
15060          * Fires when touch view require special display (default is using displayField)
15061             * @param {Roo.bootstrap.ComboBox} combo This combo box
15062             * @param {Object} cfg set html .
15063             */
15064         'touchviewdisplay' : true
15065         
15066     });
15067     
15068     this.item = [];
15069     this.tickItems = [];
15070     
15071     this.selectedIndex = -1;
15072     if(this.mode == 'local'){
15073         if(config.queryDelay === undefined){
15074             this.queryDelay = 10;
15075         }
15076         if(config.minChars === undefined){
15077             this.minChars = 0;
15078         }
15079     }
15080 };
15081
15082 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15083      
15084     /**
15085      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15086      * rendering into an Roo.Editor, defaults to false)
15087      */
15088     /**
15089      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15090      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15091      */
15092     /**
15093      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15094      */
15095     /**
15096      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15097      * the dropdown list (defaults to undefined, with no header element)
15098      */
15099
15100      /**
15101      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15102      */
15103      
15104      /**
15105      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15106      */
15107     listWidth: undefined,
15108     /**
15109      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15110      * mode = 'remote' or 'text' if mode = 'local')
15111      */
15112     displayField: undefined,
15113     
15114     /**
15115      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15116      * mode = 'remote' or 'value' if mode = 'local'). 
15117      * Note: use of a valueField requires the user make a selection
15118      * in order for a value to be mapped.
15119      */
15120     valueField: undefined,
15121     /**
15122      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15123      */
15124     modalTitle : '',
15125     
15126     /**
15127      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15128      * field's data value (defaults to the underlying DOM element's name)
15129      */
15130     hiddenName: undefined,
15131     /**
15132      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15133      */
15134     listClass: '',
15135     /**
15136      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15137      */
15138     selectedClass: 'active',
15139     
15140     /**
15141      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15142      */
15143     shadow:'sides',
15144     /**
15145      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15146      * anchor positions (defaults to 'tl-bl')
15147      */
15148     listAlign: 'tl-bl?',
15149     /**
15150      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15151      */
15152     maxHeight: 300,
15153     /**
15154      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15155      * query specified by the allQuery config option (defaults to 'query')
15156      */
15157     triggerAction: 'query',
15158     /**
15159      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15160      * (defaults to 4, does not apply if editable = false)
15161      */
15162     minChars : 4,
15163     /**
15164      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15165      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15166      */
15167     typeAhead: false,
15168     /**
15169      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15170      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15171      */
15172     queryDelay: 500,
15173     /**
15174      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15175      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15176      */
15177     pageSize: 0,
15178     /**
15179      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15180      * when editable = true (defaults to false)
15181      */
15182     selectOnFocus:false,
15183     /**
15184      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15185      */
15186     queryParam: 'query',
15187     /**
15188      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15189      * when mode = 'remote' (defaults to 'Loading...')
15190      */
15191     loadingText: 'Loading...',
15192     /**
15193      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15194      */
15195     resizable: false,
15196     /**
15197      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15198      */
15199     handleHeight : 8,
15200     /**
15201      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15202      * traditional select (defaults to true)
15203      */
15204     editable: true,
15205     /**
15206      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15207      */
15208     allQuery: '',
15209     /**
15210      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15211      */
15212     mode: 'remote',
15213     /**
15214      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15215      * listWidth has a higher value)
15216      */
15217     minListWidth : 70,
15218     /**
15219      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15220      * allow the user to set arbitrary text into the field (defaults to false)
15221      */
15222     forceSelection:false,
15223     /**
15224      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15225      * if typeAhead = true (defaults to 250)
15226      */
15227     typeAheadDelay : 250,
15228     /**
15229      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15230      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15231      */
15232     valueNotFoundText : undefined,
15233     /**
15234      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15235      */
15236     blockFocus : false,
15237     
15238     /**
15239      * @cfg {Boolean} disableClear Disable showing of clear button.
15240      */
15241     disableClear : false,
15242     /**
15243      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15244      */
15245     alwaysQuery : false,
15246     
15247     /**
15248      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15249      */
15250     multiple : false,
15251     
15252     /**
15253      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15254      */
15255     invalidClass : "has-warning",
15256     
15257     /**
15258      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15259      */
15260     validClass : "has-success",
15261     
15262     /**
15263      * @cfg {Boolean} specialFilter (true|false) special filter default false
15264      */
15265     specialFilter : false,
15266     
15267     /**
15268      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15269      */
15270     mobileTouchView : true,
15271     
15272     /**
15273      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15274      */
15275     useNativeIOS : false,
15276     
15277     /**
15278      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15279      */
15280     mobile_restrict_height : false,
15281     
15282     ios_options : false,
15283     
15284     //private
15285     addicon : false,
15286     editicon: false,
15287     
15288     page: 0,
15289     hasQuery: false,
15290     append: false,
15291     loadNext: false,
15292     autoFocus : true,
15293     tickable : false,
15294     btnPosition : 'right',
15295     triggerList : true,
15296     showToggleBtn : true,
15297     animate : true,
15298     emptyResultText: 'Empty',
15299     triggerText : 'Select',
15300     emptyTitle : '',
15301     width : false,
15302     
15303     // element that contains real text value.. (when hidden is used..)
15304     
15305     getAutoCreate : function()
15306     {   
15307         var cfg = false;
15308         //render
15309         /*
15310          * Render classic select for iso
15311          */
15312         
15313         if(Roo.isIOS && this.useNativeIOS){
15314             cfg = this.getAutoCreateNativeIOS();
15315             return cfg;
15316         }
15317         
15318         /*
15319          * Touch Devices
15320          */
15321         
15322         if(Roo.isTouch && this.mobileTouchView){
15323             cfg = this.getAutoCreateTouchView();
15324             return cfg;;
15325         }
15326         
15327         /*
15328          *  Normal ComboBox
15329          */
15330         if(!this.tickable){
15331             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15332             return cfg;
15333         }
15334         
15335         /*
15336          *  ComboBox with tickable selections
15337          */
15338              
15339         var align = this.labelAlign || this.parentLabelAlign();
15340         
15341         cfg = {
15342             cls : 'form-group roo-combobox-tickable' //input-group
15343         };
15344         
15345         var btn_text_select = '';
15346         var btn_text_done = '';
15347         var btn_text_cancel = '';
15348         
15349         if (this.btn_text_show) {
15350             btn_text_select = 'Select';
15351             btn_text_done = 'Done';
15352             btn_text_cancel = 'Cancel'; 
15353         }
15354         
15355         var buttons = {
15356             tag : 'div',
15357             cls : 'tickable-buttons',
15358             cn : [
15359                 {
15360                     tag : 'button',
15361                     type : 'button',
15362                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15363                     //html : this.triggerText
15364                     html: btn_text_select
15365                 },
15366                 {
15367                     tag : 'button',
15368                     type : 'button',
15369                     name : 'ok',
15370                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15371                     //html : 'Done'
15372                     html: btn_text_done
15373                 },
15374                 {
15375                     tag : 'button',
15376                     type : 'button',
15377                     name : 'cancel',
15378                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15379                     //html : 'Cancel'
15380                     html: btn_text_cancel
15381                 }
15382             ]
15383         };
15384         
15385         if(this.editable){
15386             buttons.cn.unshift({
15387                 tag: 'input',
15388                 cls: 'roo-select2-search-field-input'
15389             });
15390         }
15391         
15392         var _this = this;
15393         
15394         Roo.each(buttons.cn, function(c){
15395             if (_this.size) {
15396                 c.cls += ' btn-' + _this.size;
15397             }
15398
15399             if (_this.disabled) {
15400                 c.disabled = true;
15401             }
15402         });
15403         
15404         var box = {
15405             tag: 'div',
15406             style : 'display: contents',
15407             cn: [
15408                 {
15409                     tag: 'input',
15410                     type : 'hidden',
15411                     cls: 'form-hidden-field'
15412                 },
15413                 {
15414                     tag: 'ul',
15415                     cls: 'roo-select2-choices',
15416                     cn:[
15417                         {
15418                             tag: 'li',
15419                             cls: 'roo-select2-search-field',
15420                             cn: [
15421                                 buttons
15422                             ]
15423                         }
15424                     ]
15425                 }
15426             ]
15427         };
15428         
15429         var combobox = {
15430             cls: 'roo-select2-container input-group roo-select2-container-multi',
15431             cn: [
15432                 
15433                 box
15434 //                {
15435 //                    tag: 'ul',
15436 //                    cls: 'typeahead typeahead-long dropdown-menu',
15437 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15438 //                }
15439             ]
15440         };
15441         
15442         if(this.hasFeedback && !this.allowBlank){
15443             
15444             var feedback = {
15445                 tag: 'span',
15446                 cls: 'glyphicon form-control-feedback'
15447             };
15448
15449             combobox.cn.push(feedback);
15450         }
15451         
15452         
15453         
15454         var indicator = {
15455             tag : 'i',
15456             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15457             tooltip : 'This field is required'
15458         };
15459         if (Roo.bootstrap.version == 4) {
15460             indicator = {
15461                 tag : 'i',
15462                 style : 'display:none'
15463             };
15464         }
15465         if (align ==='left' && this.fieldLabel.length) {
15466             
15467             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15468             
15469             cfg.cn = [
15470                 indicator,
15471                 {
15472                     tag: 'label',
15473                     'for' :  id,
15474                     cls : 'control-label col-form-label',
15475                     html : this.fieldLabel
15476
15477                 },
15478                 {
15479                     cls : "", 
15480                     cn: [
15481                         combobox
15482                     ]
15483                 }
15484
15485             ];
15486             
15487             var labelCfg = cfg.cn[1];
15488             var contentCfg = cfg.cn[2];
15489             
15490
15491             if(this.indicatorpos == 'right'){
15492                 
15493                 cfg.cn = [
15494                     {
15495                         tag: 'label',
15496                         'for' :  id,
15497                         cls : 'control-label col-form-label',
15498                         cn : [
15499                             {
15500                                 tag : 'span',
15501                                 html : this.fieldLabel
15502                             },
15503                             indicator
15504                         ]
15505                     },
15506                     {
15507                         cls : "",
15508                         cn: [
15509                             combobox
15510                         ]
15511                     }
15512
15513                 ];
15514                 
15515                 
15516                 
15517                 labelCfg = cfg.cn[0];
15518                 contentCfg = cfg.cn[1];
15519             
15520             }
15521             
15522             if(this.labelWidth > 12){
15523                 labelCfg.style = "width: " + this.labelWidth + 'px';
15524             }
15525             if(this.width * 1 > 0){
15526                 contentCfg.style = "width: " + this.width + 'px';
15527             }
15528             if(this.labelWidth < 13 && this.labelmd == 0){
15529                 this.labelmd = this.labelWidth;
15530             }
15531             
15532             if(this.labellg > 0){
15533                 labelCfg.cls += ' col-lg-' + this.labellg;
15534                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15535             }
15536             
15537             if(this.labelmd > 0){
15538                 labelCfg.cls += ' col-md-' + this.labelmd;
15539                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15540             }
15541             
15542             if(this.labelsm > 0){
15543                 labelCfg.cls += ' col-sm-' + this.labelsm;
15544                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15545             }
15546             
15547             if(this.labelxs > 0){
15548                 labelCfg.cls += ' col-xs-' + this.labelxs;
15549                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15550             }
15551                 
15552                 
15553         } else if ( this.fieldLabel.length) {
15554 //                Roo.log(" label");
15555                  cfg.cn = [
15556                    indicator,
15557                     {
15558                         tag: 'label',
15559                         //cls : 'input-group-addon',
15560                         html : this.fieldLabel
15561                     },
15562                     combobox
15563                 ];
15564                 
15565                 if(this.indicatorpos == 'right'){
15566                     cfg.cn = [
15567                         {
15568                             tag: 'label',
15569                             //cls : 'input-group-addon',
15570                             html : this.fieldLabel
15571                         },
15572                         indicator,
15573                         combobox
15574                     ];
15575                     
15576                 }
15577
15578         } else {
15579             
15580 //                Roo.log(" no label && no align");
15581                 cfg = combobox
15582                      
15583                 
15584         }
15585          
15586         var settings=this;
15587         ['xs','sm','md','lg'].map(function(size){
15588             if (settings[size]) {
15589                 cfg.cls += ' col-' + size + '-' + settings[size];
15590             }
15591         });
15592         
15593         return cfg;
15594         
15595     },
15596     
15597     _initEventsCalled : false,
15598     
15599     // private
15600     initEvents: function()
15601     {   
15602         if (this._initEventsCalled) { // as we call render... prevent looping...
15603             return;
15604         }
15605         this._initEventsCalled = true;
15606         
15607         if (!this.store) {
15608             throw "can not find store for combo";
15609         }
15610         
15611         this.indicator = this.indicatorEl();
15612         
15613         this.store = Roo.factory(this.store, Roo.data);
15614         this.store.parent = this;
15615         
15616         // if we are building from html. then this element is so complex, that we can not really
15617         // use the rendered HTML.
15618         // so we have to trash and replace the previous code.
15619         if (Roo.XComponent.build_from_html) {
15620             // remove this element....
15621             var e = this.el.dom, k=0;
15622             while (e ) { e = e.previousSibling;  ++k;}
15623
15624             this.el.remove();
15625             
15626             this.el=false;
15627             this.rendered = false;
15628             
15629             this.render(this.parent().getChildContainer(true), k);
15630         }
15631         
15632         if(Roo.isIOS && this.useNativeIOS){
15633             this.initIOSView();
15634             return;
15635         }
15636         
15637         /*
15638          * Touch Devices
15639          */
15640         
15641         if(Roo.isTouch && this.mobileTouchView){
15642             this.initTouchView();
15643             return;
15644         }
15645         
15646         if(this.tickable){
15647             this.initTickableEvents();
15648             return;
15649         }
15650         
15651         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15652         
15653         if(this.hiddenName){
15654             
15655             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15656             
15657             this.hiddenField.dom.value =
15658                 this.hiddenValue !== undefined ? this.hiddenValue :
15659                 this.value !== undefined ? this.value : '';
15660
15661             // prevent input submission
15662             this.el.dom.removeAttribute('name');
15663             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15664              
15665              
15666         }
15667         //if(Roo.isGecko){
15668         //    this.el.dom.setAttribute('autocomplete', 'off');
15669         //}
15670         
15671         var cls = 'x-combo-list';
15672         
15673         //this.list = new Roo.Layer({
15674         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15675         //});
15676         
15677         var _this = this;
15678         
15679         (function(){
15680             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15681             _this.list.setWidth(lw);
15682         }).defer(100);
15683         
15684         this.list.on('mouseover', this.onViewOver, this);
15685         this.list.on('mousemove', this.onViewMove, this);
15686         this.list.on('scroll', this.onViewScroll, this);
15687         
15688         /*
15689         this.list.swallowEvent('mousewheel');
15690         this.assetHeight = 0;
15691
15692         if(this.title){
15693             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15694             this.assetHeight += this.header.getHeight();
15695         }
15696
15697         this.innerList = this.list.createChild({cls:cls+'-inner'});
15698         this.innerList.on('mouseover', this.onViewOver, this);
15699         this.innerList.on('mousemove', this.onViewMove, this);
15700         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15701         
15702         if(this.allowBlank && !this.pageSize && !this.disableClear){
15703             this.footer = this.list.createChild({cls:cls+'-ft'});
15704             this.pageTb = new Roo.Toolbar(this.footer);
15705            
15706         }
15707         if(this.pageSize){
15708             this.footer = this.list.createChild({cls:cls+'-ft'});
15709             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15710                     {pageSize: this.pageSize});
15711             
15712         }
15713         
15714         if (this.pageTb && this.allowBlank && !this.disableClear) {
15715             var _this = this;
15716             this.pageTb.add(new Roo.Toolbar.Fill(), {
15717                 cls: 'x-btn-icon x-btn-clear',
15718                 text: '&#160;',
15719                 handler: function()
15720                 {
15721                     _this.collapse();
15722                     _this.clearValue();
15723                     _this.onSelect(false, -1);
15724                 }
15725             });
15726         }
15727         if (this.footer) {
15728             this.assetHeight += this.footer.getHeight();
15729         }
15730         */
15731             
15732         if(!this.tpl){
15733             this.tpl = Roo.bootstrap.version == 4 ?
15734                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15735                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15736         }
15737
15738         this.view = new Roo.View(this.list, this.tpl, {
15739             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15740         });
15741         //this.view.wrapEl.setDisplayed(false);
15742         this.view.on('click', this.onViewClick, this);
15743         
15744         
15745         this.store.on('beforeload', this.onBeforeLoad, this);
15746         this.store.on('load', this.onLoad, this);
15747         this.store.on('loadexception', this.onLoadException, this);
15748         /*
15749         if(this.resizable){
15750             this.resizer = new Roo.Resizable(this.list,  {
15751                pinned:true, handles:'se'
15752             });
15753             this.resizer.on('resize', function(r, w, h){
15754                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15755                 this.listWidth = w;
15756                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15757                 this.restrictHeight();
15758             }, this);
15759             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15760         }
15761         */
15762         if(!this.editable){
15763             this.editable = true;
15764             this.setEditable(false);
15765         }
15766         
15767         /*
15768         
15769         if (typeof(this.events.add.listeners) != 'undefined') {
15770             
15771             this.addicon = this.wrap.createChild(
15772                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15773        
15774             this.addicon.on('click', function(e) {
15775                 this.fireEvent('add', this);
15776             }, this);
15777         }
15778         if (typeof(this.events.edit.listeners) != 'undefined') {
15779             
15780             this.editicon = this.wrap.createChild(
15781                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15782             if (this.addicon) {
15783                 this.editicon.setStyle('margin-left', '40px');
15784             }
15785             this.editicon.on('click', function(e) {
15786                 
15787                 // we fire even  if inothing is selected..
15788                 this.fireEvent('edit', this, this.lastData );
15789                 
15790             }, this);
15791         }
15792         */
15793         
15794         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15795             "up" : function(e){
15796                 this.inKeyMode = true;
15797                 this.selectPrev();
15798             },
15799
15800             "down" : function(e){
15801                 if(!this.isExpanded()){
15802                     this.onTriggerClick();
15803                 }else{
15804                     this.inKeyMode = true;
15805                     this.selectNext();
15806                 }
15807             },
15808
15809             "enter" : function(e){
15810 //                this.onViewClick();
15811                 //return true;
15812                 this.collapse();
15813                 
15814                 if(this.fireEvent("specialkey", this, e)){
15815                     this.onViewClick(false);
15816                 }
15817                 
15818                 return true;
15819             },
15820
15821             "esc" : function(e){
15822                 this.collapse();
15823             },
15824
15825             "tab" : function(e){
15826                 this.collapse();
15827                 
15828                 if(this.fireEvent("specialkey", this, e)){
15829                     this.onViewClick(false);
15830                 }
15831                 
15832                 return true;
15833             },
15834
15835             scope : this,
15836
15837             doRelay : function(foo, bar, hname){
15838                 if(hname == 'down' || this.scope.isExpanded()){
15839                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15840                 }
15841                 return true;
15842             },
15843
15844             forceKeyDown: true
15845         });
15846         
15847         
15848         this.queryDelay = Math.max(this.queryDelay || 10,
15849                 this.mode == 'local' ? 10 : 250);
15850         
15851         
15852         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15853         
15854         if(this.typeAhead){
15855             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15856         }
15857         if(this.editable !== false){
15858             this.inputEl().on("keyup", this.onKeyUp, this);
15859         }
15860         if(this.forceSelection){
15861             this.inputEl().on('blur', this.doForce, this);
15862         }
15863         
15864         if(this.multiple){
15865             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15866             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15867         }
15868     },
15869     
15870     initTickableEvents: function()
15871     {   
15872         this.createList();
15873         
15874         if(this.hiddenName){
15875             
15876             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15877             
15878             this.hiddenField.dom.value =
15879                 this.hiddenValue !== undefined ? this.hiddenValue :
15880                 this.value !== undefined ? this.value : '';
15881
15882             // prevent input submission
15883             this.el.dom.removeAttribute('name');
15884             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15885              
15886              
15887         }
15888         
15889 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15890         
15891         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15892         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15893         if(this.triggerList){
15894             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15895         }
15896          
15897         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15898         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15899         
15900         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15901         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15902         
15903         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15904         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15905         
15906         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15907         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15908         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15909         
15910         this.okBtn.hide();
15911         this.cancelBtn.hide();
15912         
15913         var _this = this;
15914         
15915         (function(){
15916             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15917             _this.list.setWidth(lw);
15918         }).defer(100);
15919         
15920         this.list.on('mouseover', this.onViewOver, this);
15921         this.list.on('mousemove', this.onViewMove, this);
15922         
15923         this.list.on('scroll', this.onViewScroll, this);
15924         
15925         if(!this.tpl){
15926             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15927                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15928         }
15929
15930         this.view = new Roo.View(this.list, this.tpl, {
15931             singleSelect:true,
15932             tickable:true,
15933             parent:this,
15934             store: this.store,
15935             selectedClass: this.selectedClass
15936         });
15937         
15938         //this.view.wrapEl.setDisplayed(false);
15939         this.view.on('click', this.onViewClick, this);
15940         
15941         
15942         
15943         this.store.on('beforeload', this.onBeforeLoad, this);
15944         this.store.on('load', this.onLoad, this);
15945         this.store.on('loadexception', this.onLoadException, this);
15946         
15947         if(this.editable){
15948             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15949                 "up" : function(e){
15950                     this.inKeyMode = true;
15951                     this.selectPrev();
15952                 },
15953
15954                 "down" : function(e){
15955                     this.inKeyMode = true;
15956                     this.selectNext();
15957                 },
15958
15959                 "enter" : function(e){
15960                     if(this.fireEvent("specialkey", this, e)){
15961                         this.onViewClick(false);
15962                     }
15963                     
15964                     return true;
15965                 },
15966
15967                 "esc" : function(e){
15968                     this.onTickableFooterButtonClick(e, false, false);
15969                 },
15970
15971                 "tab" : function(e){
15972                     this.fireEvent("specialkey", this, e);
15973                     
15974                     this.onTickableFooterButtonClick(e, false, false);
15975                     
15976                     return true;
15977                 },
15978
15979                 scope : this,
15980
15981                 doRelay : function(e, fn, key){
15982                     if(this.scope.isExpanded()){
15983                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15984                     }
15985                     return true;
15986                 },
15987
15988                 forceKeyDown: true
15989             });
15990         }
15991         
15992         this.queryDelay = Math.max(this.queryDelay || 10,
15993                 this.mode == 'local' ? 10 : 250);
15994         
15995         
15996         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15997         
15998         if(this.typeAhead){
15999             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16000         }
16001         
16002         if(this.editable !== false){
16003             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16004         }
16005         
16006         this.indicator = this.indicatorEl();
16007         
16008         if(this.indicator){
16009             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16010             this.indicator.hide();
16011         }
16012         
16013     },
16014
16015     onDestroy : function(){
16016         if(this.view){
16017             this.view.setStore(null);
16018             this.view.el.removeAllListeners();
16019             this.view.el.remove();
16020             this.view.purgeListeners();
16021         }
16022         if(this.list){
16023             this.list.dom.innerHTML  = '';
16024         }
16025         
16026         if(this.store){
16027             this.store.un('beforeload', this.onBeforeLoad, this);
16028             this.store.un('load', this.onLoad, this);
16029             this.store.un('loadexception', this.onLoadException, this);
16030         }
16031         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16032     },
16033
16034     // private
16035     fireKey : function(e){
16036         if(e.isNavKeyPress() && !this.list.isVisible()){
16037             this.fireEvent("specialkey", this, e);
16038         }
16039     },
16040
16041     // private
16042     onResize: function(w, h)
16043     {
16044         
16045         
16046 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16047 //        
16048 //        if(typeof w != 'number'){
16049 //            // we do not handle it!?!?
16050 //            return;
16051 //        }
16052 //        var tw = this.trigger.getWidth();
16053 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16054 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16055 //        var x = w - tw;
16056 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16057 //            
16058 //        //this.trigger.setStyle('left', x+'px');
16059 //        
16060 //        if(this.list && this.listWidth === undefined){
16061 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16062 //            this.list.setWidth(lw);
16063 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16064 //        }
16065         
16066     
16067         
16068     },
16069
16070     /**
16071      * Allow or prevent the user from directly editing the field text.  If false is passed,
16072      * the user will only be able to select from the items defined in the dropdown list.  This method
16073      * is the runtime equivalent of setting the 'editable' config option at config time.
16074      * @param {Boolean} value True to allow the user to directly edit the field text
16075      */
16076     setEditable : function(value){
16077         if(value == this.editable){
16078             return;
16079         }
16080         this.editable = value;
16081         if(!value){
16082             this.inputEl().dom.setAttribute('readOnly', true);
16083             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16084             this.inputEl().addClass('x-combo-noedit');
16085         }else{
16086             this.inputEl().dom.setAttribute('readOnly', false);
16087             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16088             this.inputEl().removeClass('x-combo-noedit');
16089         }
16090     },
16091
16092     // private
16093     
16094     onBeforeLoad : function(combo,opts){
16095         if(!this.hasFocus){
16096             return;
16097         }
16098          if (!opts.add) {
16099             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16100          }
16101         this.restrictHeight();
16102         this.selectedIndex = -1;
16103     },
16104
16105     // private
16106     onLoad : function(){
16107         
16108         this.hasQuery = false;
16109         
16110         if(!this.hasFocus){
16111             return;
16112         }
16113         
16114         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16115             this.loading.hide();
16116         }
16117         
16118         if(this.store.getCount() > 0){
16119             
16120             this.expand();
16121             this.restrictHeight();
16122             if(this.lastQuery == this.allQuery){
16123                 if(this.editable && !this.tickable){
16124                     this.inputEl().dom.select();
16125                 }
16126                 
16127                 if(
16128                     !this.selectByValue(this.value, true) &&
16129                     this.autoFocus && 
16130                     (
16131                         !this.store.lastOptions ||
16132                         typeof(this.store.lastOptions.add) == 'undefined' || 
16133                         this.store.lastOptions.add != true
16134                     )
16135                 ){
16136                     this.select(0, true);
16137                 }
16138             }else{
16139                 if(this.autoFocus){
16140                     this.selectNext();
16141                 }
16142                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16143                     this.taTask.delay(this.typeAheadDelay);
16144                 }
16145             }
16146         }else{
16147             this.onEmptyResults();
16148         }
16149         
16150         //this.el.focus();
16151     },
16152     // private
16153     onLoadException : function()
16154     {
16155         this.hasQuery = false;
16156         
16157         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16158             this.loading.hide();
16159         }
16160         
16161         if(this.tickable && this.editable){
16162             return;
16163         }
16164         
16165         this.collapse();
16166         // only causes errors at present
16167         //Roo.log(this.store.reader.jsonData);
16168         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16169             // fixme
16170             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16171         //}
16172         
16173         
16174     },
16175     // private
16176     onTypeAhead : function(){
16177         if(this.store.getCount() > 0){
16178             var r = this.store.getAt(0);
16179             var newValue = r.data[this.displayField];
16180             var len = newValue.length;
16181             var selStart = this.getRawValue().length;
16182             
16183             if(selStart != len){
16184                 this.setRawValue(newValue);
16185                 this.selectText(selStart, newValue.length);
16186             }
16187         }
16188     },
16189
16190     // private
16191     onSelect : function(record, index){
16192         
16193         if(this.fireEvent('beforeselect', this, record, index) !== false){
16194         
16195             this.setFromData(index > -1 ? record.data : false);
16196             
16197             this.collapse();
16198             this.fireEvent('select', this, record, index);
16199         }
16200     },
16201
16202     /**
16203      * Returns the currently selected field value or empty string if no value is set.
16204      * @return {String} value The selected value
16205      */
16206     getValue : function()
16207     {
16208         if(Roo.isIOS && this.useNativeIOS){
16209             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16210         }
16211         
16212         if(this.multiple){
16213             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16214         }
16215         
16216         if(this.valueField){
16217             return typeof this.value != 'undefined' ? this.value : '';
16218         }else{
16219             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16220         }
16221     },
16222     
16223     getRawValue : function()
16224     {
16225         if(Roo.isIOS && this.useNativeIOS){
16226             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16227         }
16228         
16229         var v = this.inputEl().getValue();
16230         
16231         return v;
16232     },
16233
16234     /**
16235      * Clears any text/value currently set in the field
16236      */
16237     clearValue : function(){
16238         
16239         if(this.hiddenField){
16240             this.hiddenField.dom.value = '';
16241         }
16242         this.value = '';
16243         this.setRawValue('');
16244         this.lastSelectionText = '';
16245         this.lastData = false;
16246         
16247         var close = this.closeTriggerEl();
16248         
16249         if(close){
16250             close.hide();
16251         }
16252         
16253         this.validate();
16254         
16255     },
16256
16257     /**
16258      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16259      * will be displayed in the field.  If the value does not match the data value of an existing item,
16260      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16261      * Otherwise the field will be blank (although the value will still be set).
16262      * @param {String} value The value to match
16263      */
16264     setValue : function(v)
16265     {
16266         if(Roo.isIOS && this.useNativeIOS){
16267             this.setIOSValue(v);
16268             return;
16269         }
16270         
16271         if(this.multiple){
16272             this.syncValue();
16273             return;
16274         }
16275         
16276         var text = v;
16277         if(this.valueField){
16278             var r = this.findRecord(this.valueField, v);
16279             if(r){
16280                 text = r.data[this.displayField];
16281             }else if(this.valueNotFoundText !== undefined){
16282                 text = this.valueNotFoundText;
16283             }
16284         }
16285         this.lastSelectionText = text;
16286         if(this.hiddenField){
16287             this.hiddenField.dom.value = v;
16288         }
16289         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16290         this.value = v;
16291         
16292         var close = this.closeTriggerEl();
16293         
16294         if(close){
16295             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16296         }
16297         
16298         this.validate();
16299     },
16300     /**
16301      * @property {Object} the last set data for the element
16302      */
16303     
16304     lastData : false,
16305     /**
16306      * Sets the value of the field based on a object which is related to the record format for the store.
16307      * @param {Object} value the value to set as. or false on reset?
16308      */
16309     setFromData : function(o){
16310         
16311         if(this.multiple){
16312             this.addItem(o);
16313             return;
16314         }
16315             
16316         var dv = ''; // display value
16317         var vv = ''; // value value..
16318         this.lastData = o;
16319         if (this.displayField) {
16320             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16321         } else {
16322             // this is an error condition!!!
16323             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16324         }
16325         
16326         if(this.valueField){
16327             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16328         }
16329         
16330         var close = this.closeTriggerEl();
16331         
16332         if(close){
16333             if(dv.length || vv * 1 > 0){
16334                 close.show() ;
16335                 this.blockFocus=true;
16336             } else {
16337                 close.hide();
16338             }             
16339         }
16340         
16341         if(this.hiddenField){
16342             this.hiddenField.dom.value = vv;
16343             
16344             this.lastSelectionText = dv;
16345             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16346             this.value = vv;
16347             return;
16348         }
16349         // no hidden field.. - we store the value in 'value', but still display
16350         // display field!!!!
16351         this.lastSelectionText = dv;
16352         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16353         this.value = vv;
16354         
16355         
16356         
16357     },
16358     // private
16359     reset : function(){
16360         // overridden so that last data is reset..
16361         
16362         if(this.multiple){
16363             this.clearItem();
16364             return;
16365         }
16366         
16367         this.setValue(this.originalValue);
16368         //this.clearInvalid();
16369         this.lastData = false;
16370         if (this.view) {
16371             this.view.clearSelections();
16372         }
16373         
16374         this.validate();
16375     },
16376     // private
16377     findRecord : function(prop, value){
16378         var record;
16379         if(this.store.getCount() > 0){
16380             this.store.each(function(r){
16381                 if(r.data[prop] == value){
16382                     record = r;
16383                     return false;
16384                 }
16385                 return true;
16386             });
16387         }
16388         return record;
16389     },
16390     
16391     getName: function()
16392     {
16393         // returns hidden if it's set..
16394         if (!this.rendered) {return ''};
16395         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16396         
16397     },
16398     // private
16399     onViewMove : function(e, t){
16400         this.inKeyMode = false;
16401     },
16402
16403     // private
16404     onViewOver : function(e, t){
16405         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16406             return;
16407         }
16408         var item = this.view.findItemFromChild(t);
16409         
16410         if(item){
16411             var index = this.view.indexOf(item);
16412             this.select(index, false);
16413         }
16414     },
16415
16416     // private
16417     onViewClick : function(view, doFocus, el, e)
16418     {
16419         var index = this.view.getSelectedIndexes()[0];
16420         
16421         var r = this.store.getAt(index);
16422         
16423         if(this.tickable){
16424             
16425             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16426                 return;
16427             }
16428             
16429             var rm = false;
16430             var _this = this;
16431             
16432             Roo.each(this.tickItems, function(v,k){
16433                 
16434                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16435                     Roo.log(v);
16436                     _this.tickItems.splice(k, 1);
16437                     
16438                     if(typeof(e) == 'undefined' && view == false){
16439                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16440                     }
16441                     
16442                     rm = true;
16443                     return;
16444                 }
16445             });
16446             
16447             if(rm){
16448                 return;
16449             }
16450             
16451             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16452                 this.tickItems.push(r.data);
16453             }
16454             
16455             if(typeof(e) == 'undefined' && view == false){
16456                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16457             }
16458                     
16459             return;
16460         }
16461         
16462         if(r){
16463             this.onSelect(r, index);
16464         }
16465         if(doFocus !== false && !this.blockFocus){
16466             this.inputEl().focus();
16467         }
16468     },
16469
16470     // private
16471     restrictHeight : function(){
16472         //this.innerList.dom.style.height = '';
16473         //var inner = this.innerList.dom;
16474         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16475         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16476         //this.list.beginUpdate();
16477         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16478         this.list.alignTo(this.inputEl(), this.listAlign);
16479         this.list.alignTo(this.inputEl(), this.listAlign);
16480         //this.list.endUpdate();
16481     },
16482
16483     // private
16484     onEmptyResults : function(){
16485         
16486         if(this.tickable && this.editable){
16487             this.hasFocus = false;
16488             this.restrictHeight();
16489             return;
16490         }
16491         
16492         this.collapse();
16493     },
16494
16495     /**
16496      * Returns true if the dropdown list is expanded, else false.
16497      */
16498     isExpanded : function(){
16499         return this.list.isVisible();
16500     },
16501
16502     /**
16503      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16504      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16505      * @param {String} value The data value of the item to select
16506      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16507      * selected item if it is not currently in view (defaults to true)
16508      * @return {Boolean} True if the value matched an item in the list, else false
16509      */
16510     selectByValue : function(v, scrollIntoView){
16511         if(v !== undefined && v !== null){
16512             var r = this.findRecord(this.valueField || this.displayField, v);
16513             if(r){
16514                 this.select(this.store.indexOf(r), scrollIntoView);
16515                 return true;
16516             }
16517         }
16518         return false;
16519     },
16520
16521     /**
16522      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16523      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16524      * @param {Number} index The zero-based index of the list item to select
16525      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16526      * selected item if it is not currently in view (defaults to true)
16527      */
16528     select : function(index, scrollIntoView){
16529         this.selectedIndex = index;
16530         this.view.select(index);
16531         if(scrollIntoView !== false){
16532             var el = this.view.getNode(index);
16533             /*
16534              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16535              */
16536             if(el){
16537                 this.list.scrollChildIntoView(el, false);
16538             }
16539         }
16540     },
16541
16542     // private
16543     selectNext : function(){
16544         var ct = this.store.getCount();
16545         if(ct > 0){
16546             if(this.selectedIndex == -1){
16547                 this.select(0);
16548             }else if(this.selectedIndex < ct-1){
16549                 this.select(this.selectedIndex+1);
16550             }
16551         }
16552     },
16553
16554     // private
16555     selectPrev : function(){
16556         var ct = this.store.getCount();
16557         if(ct > 0){
16558             if(this.selectedIndex == -1){
16559                 this.select(0);
16560             }else if(this.selectedIndex != 0){
16561                 this.select(this.selectedIndex-1);
16562             }
16563         }
16564     },
16565
16566     // private
16567     onKeyUp : function(e){
16568         if(this.editable !== false && !e.isSpecialKey()){
16569             this.lastKey = e.getKey();
16570             this.dqTask.delay(this.queryDelay);
16571         }
16572     },
16573
16574     // private
16575     validateBlur : function(){
16576         return !this.list || !this.list.isVisible();   
16577     },
16578
16579     // private
16580     initQuery : function(){
16581         
16582         var v = this.getRawValue();
16583         
16584         if(this.tickable && this.editable){
16585             v = this.tickableInputEl().getValue();
16586         }
16587         
16588         this.doQuery(v);
16589     },
16590
16591     // private
16592     doForce : function(){
16593         if(this.inputEl().dom.value.length > 0){
16594             this.inputEl().dom.value =
16595                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16596              
16597         }
16598     },
16599
16600     /**
16601      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16602      * query allowing the query action to be canceled if needed.
16603      * @param {String} query The SQL query to execute
16604      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16605      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16606      * saved in the current store (defaults to false)
16607      */
16608     doQuery : function(q, forceAll){
16609         
16610         if(q === undefined || q === null){
16611             q = '';
16612         }
16613         var qe = {
16614             query: q,
16615             forceAll: forceAll,
16616             combo: this,
16617             cancel:false
16618         };
16619         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16620             return false;
16621         }
16622         q = qe.query;
16623         
16624         forceAll = qe.forceAll;
16625         if(forceAll === true || (q.length >= this.minChars)){
16626             
16627             this.hasQuery = true;
16628             
16629             if(this.lastQuery != q || this.alwaysQuery){
16630                 this.lastQuery = q;
16631                 if(this.mode == 'local'){
16632                     this.selectedIndex = -1;
16633                     if(forceAll){
16634                         this.store.clearFilter();
16635                     }else{
16636                         
16637                         if(this.specialFilter){
16638                             this.fireEvent('specialfilter', this);
16639                             this.onLoad();
16640                             return;
16641                         }
16642                         
16643                         this.store.filter(this.displayField, q);
16644                     }
16645                     
16646                     this.store.fireEvent("datachanged", this.store);
16647                     
16648                     this.onLoad();
16649                     
16650                     
16651                 }else{
16652                     
16653                     this.store.baseParams[this.queryParam] = q;
16654                     
16655                     var options = {params : this.getParams(q)};
16656                     
16657                     if(this.loadNext){
16658                         options.add = true;
16659                         options.params.start = this.page * this.pageSize;
16660                     }
16661                     
16662                     this.store.load(options);
16663                     
16664                     /*
16665                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16666                      *  we should expand the list on onLoad
16667                      *  so command out it
16668                      */
16669 //                    this.expand();
16670                 }
16671             }else{
16672                 this.selectedIndex = -1;
16673                 this.onLoad();   
16674             }
16675         }
16676         
16677         this.loadNext = false;
16678     },
16679     
16680     // private
16681     getParams : function(q){
16682         var p = {};
16683         //p[this.queryParam] = q;
16684         
16685         if(this.pageSize){
16686             p.start = 0;
16687             p.limit = this.pageSize;
16688         }
16689         return p;
16690     },
16691
16692     /**
16693      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16694      */
16695     collapse : function(){
16696         if(!this.isExpanded()){
16697             return;
16698         }
16699         
16700         this.list.hide();
16701         
16702         this.hasFocus = false;
16703         
16704         if(this.tickable){
16705             this.okBtn.hide();
16706             this.cancelBtn.hide();
16707             this.trigger.show();
16708             
16709             if(this.editable){
16710                 this.tickableInputEl().dom.value = '';
16711                 this.tickableInputEl().blur();
16712             }
16713             
16714         }
16715         
16716         Roo.get(document).un('mousedown', this.collapseIf, this);
16717         Roo.get(document).un('mousewheel', this.collapseIf, this);
16718         if (!this.editable) {
16719             Roo.get(document).un('keydown', this.listKeyPress, this);
16720         }
16721         this.fireEvent('collapse', this);
16722         
16723         this.validate();
16724     },
16725
16726     // private
16727     collapseIf : function(e){
16728         var in_combo  = e.within(this.el);
16729         var in_list =  e.within(this.list);
16730         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16731         
16732         if (in_combo || in_list || is_list) {
16733             //e.stopPropagation();
16734             return;
16735         }
16736         
16737         if(this.tickable){
16738             this.onTickableFooterButtonClick(e, false, false);
16739         }
16740
16741         this.collapse();
16742         
16743     },
16744
16745     /**
16746      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16747      */
16748     expand : function(){
16749        
16750         if(this.isExpanded() || !this.hasFocus){
16751             return;
16752         }
16753         
16754         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16755         this.list.setWidth(lw);
16756         
16757         Roo.log('expand');
16758         
16759         this.list.show();
16760         
16761         this.restrictHeight();
16762         
16763         if(this.tickable){
16764             
16765             this.tickItems = Roo.apply([], this.item);
16766             
16767             this.okBtn.show();
16768             this.cancelBtn.show();
16769             this.trigger.hide();
16770             
16771             if(this.editable){
16772                 this.tickableInputEl().focus();
16773             }
16774             
16775         }
16776         
16777         Roo.get(document).on('mousedown', this.collapseIf, this);
16778         Roo.get(document).on('mousewheel', this.collapseIf, this);
16779         if (!this.editable) {
16780             Roo.get(document).on('keydown', this.listKeyPress, this);
16781         }
16782         
16783         this.fireEvent('expand', this);
16784     },
16785
16786     // private
16787     // Implements the default empty TriggerField.onTriggerClick function
16788     onTriggerClick : function(e)
16789     {
16790         Roo.log('trigger click');
16791         
16792         if(this.disabled || !this.triggerList){
16793             return;
16794         }
16795         
16796         this.page = 0;
16797         this.loadNext = false;
16798         
16799         if(this.isExpanded()){
16800             this.collapse();
16801             if (!this.blockFocus) {
16802                 this.inputEl().focus();
16803             }
16804             
16805         }else {
16806             this.hasFocus = true;
16807             if(this.triggerAction == 'all') {
16808                 this.doQuery(this.allQuery, true);
16809             } else {
16810                 this.doQuery(this.getRawValue());
16811             }
16812             if (!this.blockFocus) {
16813                 this.inputEl().focus();
16814             }
16815         }
16816     },
16817     
16818     onTickableTriggerClick : function(e)
16819     {
16820         if(this.disabled){
16821             return;
16822         }
16823         
16824         this.page = 0;
16825         this.loadNext = false;
16826         this.hasFocus = true;
16827         
16828         if(this.triggerAction == 'all') {
16829             this.doQuery(this.allQuery, true);
16830         } else {
16831             this.doQuery(this.getRawValue());
16832         }
16833     },
16834     
16835     onSearchFieldClick : function(e)
16836     {
16837         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16838             this.onTickableFooterButtonClick(e, false, false);
16839             return;
16840         }
16841         
16842         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16843             return;
16844         }
16845         
16846         this.page = 0;
16847         this.loadNext = false;
16848         this.hasFocus = true;
16849         
16850         if(this.triggerAction == 'all') {
16851             this.doQuery(this.allQuery, true);
16852         } else {
16853             this.doQuery(this.getRawValue());
16854         }
16855     },
16856     
16857     listKeyPress : function(e)
16858     {
16859         //Roo.log('listkeypress');
16860         // scroll to first matching element based on key pres..
16861         if (e.isSpecialKey()) {
16862             return false;
16863         }
16864         var k = String.fromCharCode(e.getKey()).toUpperCase();
16865         //Roo.log(k);
16866         var match  = false;
16867         var csel = this.view.getSelectedNodes();
16868         var cselitem = false;
16869         if (csel.length) {
16870             var ix = this.view.indexOf(csel[0]);
16871             cselitem  = this.store.getAt(ix);
16872             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16873                 cselitem = false;
16874             }
16875             
16876         }
16877         
16878         this.store.each(function(v) { 
16879             if (cselitem) {
16880                 // start at existing selection.
16881                 if (cselitem.id == v.id) {
16882                     cselitem = false;
16883                 }
16884                 return true;
16885             }
16886                 
16887             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16888                 match = this.store.indexOf(v);
16889                 return false;
16890             }
16891             return true;
16892         }, this);
16893         
16894         if (match === false) {
16895             return true; // no more action?
16896         }
16897         // scroll to?
16898         this.view.select(match);
16899         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16900         sn.scrollIntoView(sn.dom.parentNode, false);
16901     },
16902     
16903     onViewScroll : function(e, t){
16904         
16905         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){
16906             return;
16907         }
16908         
16909         this.hasQuery = true;
16910         
16911         this.loading = this.list.select('.loading', true).first();
16912         
16913         if(this.loading === null){
16914             this.list.createChild({
16915                 tag: 'div',
16916                 cls: 'loading roo-select2-more-results roo-select2-active',
16917                 html: 'Loading more results...'
16918             });
16919             
16920             this.loading = this.list.select('.loading', true).first();
16921             
16922             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16923             
16924             this.loading.hide();
16925         }
16926         
16927         this.loading.show();
16928         
16929         var _combo = this;
16930         
16931         this.page++;
16932         this.loadNext = true;
16933         
16934         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16935         
16936         return;
16937     },
16938     
16939     addItem : function(o)
16940     {   
16941         var dv = ''; // display value
16942         
16943         if (this.displayField) {
16944             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16945         } else {
16946             // this is an error condition!!!
16947             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16948         }
16949         
16950         if(!dv.length){
16951             return;
16952         }
16953         
16954         var choice = this.choices.createChild({
16955             tag: 'li',
16956             cls: 'roo-select2-search-choice',
16957             cn: [
16958                 {
16959                     tag: 'div',
16960                     html: dv
16961                 },
16962                 {
16963                     tag: 'a',
16964                     href: '#',
16965                     cls: 'roo-select2-search-choice-close fa fa-times',
16966                     tabindex: '-1'
16967                 }
16968             ]
16969             
16970         }, this.searchField);
16971         
16972         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16973         
16974         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16975         
16976         this.item.push(o);
16977         
16978         this.lastData = o;
16979         
16980         this.syncValue();
16981         
16982         this.inputEl().dom.value = '';
16983         
16984         this.validate();
16985     },
16986     
16987     onRemoveItem : function(e, _self, o)
16988     {
16989         e.preventDefault();
16990         
16991         this.lastItem = Roo.apply([], this.item);
16992         
16993         var index = this.item.indexOf(o.data) * 1;
16994         
16995         if( index < 0){
16996             Roo.log('not this item?!');
16997             return;
16998         }
16999         
17000         this.item.splice(index, 1);
17001         o.item.remove();
17002         
17003         this.syncValue();
17004         
17005         this.fireEvent('remove', this, e);
17006         
17007         this.validate();
17008         
17009     },
17010     
17011     syncValue : function()
17012     {
17013         if(!this.item.length){
17014             this.clearValue();
17015             return;
17016         }
17017             
17018         var value = [];
17019         var _this = this;
17020         Roo.each(this.item, function(i){
17021             if(_this.valueField){
17022                 value.push(i[_this.valueField]);
17023                 return;
17024             }
17025
17026             value.push(i);
17027         });
17028
17029         this.value = value.join(',');
17030
17031         if(this.hiddenField){
17032             this.hiddenField.dom.value = this.value;
17033         }
17034         
17035         this.store.fireEvent("datachanged", this.store);
17036         
17037         this.validate();
17038     },
17039     
17040     clearItem : function()
17041     {
17042         if(!this.multiple){
17043             return;
17044         }
17045         
17046         this.item = [];
17047         
17048         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17049            c.remove();
17050         });
17051         
17052         this.syncValue();
17053         
17054         this.validate();
17055         
17056         if(this.tickable && !Roo.isTouch){
17057             this.view.refresh();
17058         }
17059     },
17060     
17061     inputEl: function ()
17062     {
17063         if(Roo.isIOS && this.useNativeIOS){
17064             return this.el.select('select.roo-ios-select', true).first();
17065         }
17066         
17067         if(Roo.isTouch && this.mobileTouchView){
17068             return this.el.select('input.form-control',true).first();
17069         }
17070         
17071         if(this.tickable){
17072             return this.searchField;
17073         }
17074         
17075         return this.el.select('input.form-control',true).first();
17076     },
17077     
17078     onTickableFooterButtonClick : function(e, btn, el)
17079     {
17080         e.preventDefault();
17081         
17082         this.lastItem = Roo.apply([], this.item);
17083         
17084         if(btn && btn.name == 'cancel'){
17085             this.tickItems = Roo.apply([], this.item);
17086             this.collapse();
17087             return;
17088         }
17089         
17090         this.clearItem();
17091         
17092         var _this = this;
17093         
17094         Roo.each(this.tickItems, function(o){
17095             _this.addItem(o);
17096         });
17097         
17098         this.collapse();
17099         
17100     },
17101     
17102     validate : function()
17103     {
17104         if(this.getVisibilityEl().hasClass('hidden')){
17105             return true;
17106         }
17107         
17108         var v = this.getRawValue();
17109         
17110         if(this.multiple){
17111             v = this.getValue();
17112         }
17113         
17114         if(this.disabled || this.allowBlank || v.length){
17115             this.markValid();
17116             return true;
17117         }
17118         
17119         this.markInvalid();
17120         return false;
17121     },
17122     
17123     tickableInputEl : function()
17124     {
17125         if(!this.tickable || !this.editable){
17126             return this.inputEl();
17127         }
17128         
17129         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17130     },
17131     
17132     
17133     getAutoCreateTouchView : function()
17134     {
17135         var id = Roo.id();
17136         
17137         var cfg = {
17138             cls: 'form-group' //input-group
17139         };
17140         
17141         var input =  {
17142             tag: 'input',
17143             id : id,
17144             type : this.inputType,
17145             cls : 'form-control x-combo-noedit',
17146             autocomplete: 'new-password',
17147             placeholder : this.placeholder || '',
17148             readonly : true
17149         };
17150         
17151         if (this.name) {
17152             input.name = this.name;
17153         }
17154         
17155         if (this.size) {
17156             input.cls += ' input-' + this.size;
17157         }
17158         
17159         if (this.disabled) {
17160             input.disabled = true;
17161         }
17162         
17163         var inputblock = {
17164             cls : 'roo-combobox-wrap',
17165             cn : [
17166                 input
17167             ]
17168         };
17169         
17170         if(this.before){
17171             inputblock.cls += ' input-group';
17172             
17173             inputblock.cn.unshift({
17174                 tag :'span',
17175                 cls : 'input-group-addon input-group-prepend input-group-text',
17176                 html : this.before
17177             });
17178         }
17179         
17180         if(this.removable && !this.multiple){
17181             inputblock.cls += ' roo-removable';
17182             
17183             inputblock.cn.push({
17184                 tag: 'button',
17185                 html : 'x',
17186                 cls : 'roo-combo-removable-btn close'
17187             });
17188         }
17189
17190         if(this.hasFeedback && !this.allowBlank){
17191             
17192             inputblock.cls += ' has-feedback';
17193             
17194             inputblock.cn.push({
17195                 tag: 'span',
17196                 cls: 'glyphicon form-control-feedback'
17197             });
17198             
17199         }
17200         
17201         if (this.after) {
17202             
17203             inputblock.cls += (this.before) ? '' : ' input-group';
17204             
17205             inputblock.cn.push({
17206                 tag :'span',
17207                 cls : 'input-group-addon input-group-append input-group-text',
17208                 html : this.after
17209             });
17210         }
17211
17212         
17213         var ibwrap = inputblock;
17214         
17215         if(this.multiple){
17216             ibwrap = {
17217                 tag: 'ul',
17218                 cls: 'roo-select2-choices',
17219                 cn:[
17220                     {
17221                         tag: 'li',
17222                         cls: 'roo-select2-search-field',
17223                         cn: [
17224
17225                             inputblock
17226                         ]
17227                     }
17228                 ]
17229             };
17230         
17231             
17232         }
17233         
17234         var combobox = {
17235             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17236             cn: [
17237                 {
17238                     tag: 'input',
17239                     type : 'hidden',
17240                     cls: 'form-hidden-field'
17241                 },
17242                 ibwrap
17243             ]
17244         };
17245         
17246         if(!this.multiple && this.showToggleBtn){
17247             
17248             var caret = {
17249                 cls: 'caret'
17250             };
17251             
17252             if (this.caret != false) {
17253                 caret = {
17254                      tag: 'i',
17255                      cls: 'fa fa-' + this.caret
17256                 };
17257                 
17258             }
17259             
17260             combobox.cn.push({
17261                 tag :'span',
17262                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17263                 cn : [
17264                     Roo.bootstrap.version == 3 ? caret : '',
17265                     {
17266                         tag: 'span',
17267                         cls: 'combobox-clear',
17268                         cn  : [
17269                             {
17270                                 tag : 'i',
17271                                 cls: 'icon-remove'
17272                             }
17273                         ]
17274                     }
17275                 ]
17276
17277             })
17278         }
17279         
17280         if(this.multiple){
17281             combobox.cls += ' roo-select2-container-multi';
17282         }
17283         
17284         var align = this.labelAlign || this.parentLabelAlign();
17285         
17286         if (align ==='left' && this.fieldLabel.length) {
17287
17288             cfg.cn = [
17289                 {
17290                    tag : 'i',
17291                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17292                    tooltip : 'This field is required'
17293                 },
17294                 {
17295                     tag: 'label',
17296                     cls : 'control-label col-form-label',
17297                     html : this.fieldLabel
17298
17299                 },
17300                 {
17301                     cls : 'roo-combobox-wrap ', 
17302                     cn: [
17303                         combobox
17304                     ]
17305                 }
17306             ];
17307             
17308             var labelCfg = cfg.cn[1];
17309             var contentCfg = cfg.cn[2];
17310             
17311
17312             if(this.indicatorpos == 'right'){
17313                 cfg.cn = [
17314                     {
17315                         tag: 'label',
17316                         'for' :  id,
17317                         cls : 'control-label col-form-label',
17318                         cn : [
17319                             {
17320                                 tag : 'span',
17321                                 html : this.fieldLabel
17322                             },
17323                             {
17324                                 tag : 'i',
17325                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17326                                 tooltip : 'This field is required'
17327                             }
17328                         ]
17329                     },
17330                     {
17331                         cls : "roo-combobox-wrap ",
17332                         cn: [
17333                             combobox
17334                         ]
17335                     }
17336
17337                 ];
17338                 
17339                 labelCfg = cfg.cn[0];
17340                 contentCfg = cfg.cn[1];
17341             }
17342             
17343            
17344             
17345             if(this.labelWidth > 12){
17346                 labelCfg.style = "width: " + this.labelWidth + 'px';
17347             }
17348            
17349             if(this.labelWidth < 13 && this.labelmd == 0){
17350                 this.labelmd = this.labelWidth;
17351             }
17352             
17353             if(this.labellg > 0){
17354                 labelCfg.cls += ' col-lg-' + this.labellg;
17355                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17356             }
17357             
17358             if(this.labelmd > 0){
17359                 labelCfg.cls += ' col-md-' + this.labelmd;
17360                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17361             }
17362             
17363             if(this.labelsm > 0){
17364                 labelCfg.cls += ' col-sm-' + this.labelsm;
17365                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17366             }
17367             
17368             if(this.labelxs > 0){
17369                 labelCfg.cls += ' col-xs-' + this.labelxs;
17370                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17371             }
17372                 
17373                 
17374         } else if ( this.fieldLabel.length) {
17375             cfg.cn = [
17376                 {
17377                    tag : 'i',
17378                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17379                    tooltip : 'This field is required'
17380                 },
17381                 {
17382                     tag: 'label',
17383                     cls : 'control-label',
17384                     html : this.fieldLabel
17385
17386                 },
17387                 {
17388                     cls : '', 
17389                     cn: [
17390                         combobox
17391                     ]
17392                 }
17393             ];
17394             
17395             if(this.indicatorpos == 'right'){
17396                 cfg.cn = [
17397                     {
17398                         tag: 'label',
17399                         cls : 'control-label',
17400                         html : this.fieldLabel,
17401                         cn : [
17402                             {
17403                                tag : 'i',
17404                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17405                                tooltip : 'This field is required'
17406                             }
17407                         ]
17408                     },
17409                     {
17410                         cls : '', 
17411                         cn: [
17412                             combobox
17413                         ]
17414                     }
17415                 ];
17416             }
17417         } else {
17418             cfg.cn = combobox;    
17419         }
17420         
17421         
17422         var settings = this;
17423         
17424         ['xs','sm','md','lg'].map(function(size){
17425             if (settings[size]) {
17426                 cfg.cls += ' col-' + size + '-' + settings[size];
17427             }
17428         });
17429         
17430         return cfg;
17431     },
17432     
17433     initTouchView : function()
17434     {
17435         this.renderTouchView();
17436         
17437         this.touchViewEl.on('scroll', function(){
17438             this.el.dom.scrollTop = 0;
17439         }, this);
17440         
17441         this.originalValue = this.getValue();
17442         
17443         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17444         
17445         this.inputEl().on("click", this.showTouchView, this);
17446         if (this.triggerEl) {
17447             this.triggerEl.on("click", this.showTouchView, this);
17448         }
17449         
17450         
17451         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17452         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17453         
17454         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17455         
17456         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17457         this.store.on('load', this.onTouchViewLoad, this);
17458         this.store.on('loadexception', this.onTouchViewLoadException, this);
17459         
17460         if(this.hiddenName){
17461             
17462             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17463             
17464             this.hiddenField.dom.value =
17465                 this.hiddenValue !== undefined ? this.hiddenValue :
17466                 this.value !== undefined ? this.value : '';
17467         
17468             this.el.dom.removeAttribute('name');
17469             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17470         }
17471         
17472         if(this.multiple){
17473             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17474             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17475         }
17476         
17477         if(this.removable && !this.multiple){
17478             var close = this.closeTriggerEl();
17479             if(close){
17480                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17481                 close.on('click', this.removeBtnClick, this, close);
17482             }
17483         }
17484         /*
17485          * fix the bug in Safari iOS8
17486          */
17487         this.inputEl().on("focus", function(e){
17488             document.activeElement.blur();
17489         }, this);
17490         
17491         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17492         
17493         return;
17494         
17495         
17496     },
17497     
17498     renderTouchView : function()
17499     {
17500         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17501         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17502         
17503         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17504         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17505         
17506         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17507         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17508         this.touchViewBodyEl.setStyle('overflow', 'auto');
17509         
17510         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17511         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17512         
17513         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17514         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17515         
17516     },
17517     
17518     showTouchView : function()
17519     {
17520         if(this.disabled){
17521             return;
17522         }
17523         
17524         this.touchViewHeaderEl.hide();
17525
17526         if(this.modalTitle.length){
17527             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17528             this.touchViewHeaderEl.show();
17529         }
17530
17531         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17532         this.touchViewEl.show();
17533
17534         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17535         
17536         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17537         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17538
17539         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17540
17541         if(this.modalTitle.length){
17542             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17543         }
17544         
17545         this.touchViewBodyEl.setHeight(bodyHeight);
17546
17547         if(this.animate){
17548             var _this = this;
17549             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17550         }else{
17551             this.touchViewEl.addClass(['in','show']);
17552         }
17553         
17554         if(this._touchViewMask){
17555             Roo.get(document.body).addClass("x-body-masked");
17556             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17557             this._touchViewMask.setStyle('z-index', 10000);
17558             this._touchViewMask.addClass('show');
17559         }
17560         
17561         this.doTouchViewQuery();
17562         
17563     },
17564     
17565     hideTouchView : function()
17566     {
17567         this.touchViewEl.removeClass(['in','show']);
17568
17569         if(this.animate){
17570             var _this = this;
17571             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17572         }else{
17573             this.touchViewEl.setStyle('display', 'none');
17574         }
17575         
17576         if(this._touchViewMask){
17577             this._touchViewMask.removeClass('show');
17578             Roo.get(document.body).removeClass("x-body-masked");
17579         }
17580     },
17581     
17582     setTouchViewValue : function()
17583     {
17584         if(this.multiple){
17585             this.clearItem();
17586         
17587             var _this = this;
17588
17589             Roo.each(this.tickItems, function(o){
17590                 this.addItem(o);
17591             }, this);
17592         }
17593         
17594         this.hideTouchView();
17595     },
17596     
17597     doTouchViewQuery : function()
17598     {
17599         var qe = {
17600             query: '',
17601             forceAll: true,
17602             combo: this,
17603             cancel:false
17604         };
17605         
17606         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17607             return false;
17608         }
17609         
17610         if(!this.alwaysQuery || this.mode == 'local'){
17611             this.onTouchViewLoad();
17612             return;
17613         }
17614         
17615         this.store.load();
17616     },
17617     
17618     onTouchViewBeforeLoad : function(combo,opts)
17619     {
17620         return;
17621     },
17622
17623     // private
17624     onTouchViewLoad : function()
17625     {
17626         if(this.store.getCount() < 1){
17627             this.onTouchViewEmptyResults();
17628             return;
17629         }
17630         
17631         this.clearTouchView();
17632         
17633         var rawValue = this.getRawValue();
17634         
17635         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17636         
17637         this.tickItems = [];
17638         
17639         this.store.data.each(function(d, rowIndex){
17640             var row = this.touchViewListGroup.createChild(template);
17641             
17642             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17643                 row.addClass(d.data.cls);
17644             }
17645             
17646             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17647                 var cfg = {
17648                     data : d.data,
17649                     html : d.data[this.displayField]
17650                 };
17651                 
17652                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17653                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17654                 }
17655             }
17656             row.removeClass('selected');
17657             if(!this.multiple && this.valueField &&
17658                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17659             {
17660                 // radio buttons..
17661                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17662                 row.addClass('selected');
17663             }
17664             
17665             if(this.multiple && this.valueField &&
17666                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17667             {
17668                 
17669                 // checkboxes...
17670                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671                 this.tickItems.push(d.data);
17672             }
17673             
17674             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17675             
17676         }, this);
17677         
17678         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17679         
17680         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17681
17682         if(this.modalTitle.length){
17683             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17684         }
17685
17686         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17687         
17688         if(this.mobile_restrict_height && listHeight < bodyHeight){
17689             this.touchViewBodyEl.setHeight(listHeight);
17690         }
17691         
17692         var _this = this;
17693         
17694         if(firstChecked && listHeight > bodyHeight){
17695             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17696         }
17697         
17698     },
17699     
17700     onTouchViewLoadException : function()
17701     {
17702         this.hideTouchView();
17703     },
17704     
17705     onTouchViewEmptyResults : function()
17706     {
17707         this.clearTouchView();
17708         
17709         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17710         
17711         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17712         
17713     },
17714     
17715     clearTouchView : function()
17716     {
17717         this.touchViewListGroup.dom.innerHTML = '';
17718     },
17719     
17720     onTouchViewClick : function(e, el, o)
17721     {
17722         e.preventDefault();
17723         
17724         var row = o.row;
17725         var rowIndex = o.rowIndex;
17726         
17727         var r = this.store.getAt(rowIndex);
17728         
17729         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17730             
17731             if(!this.multiple){
17732                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17733                     c.dom.removeAttribute('checked');
17734                 }, this);
17735
17736                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17737
17738                 this.setFromData(r.data);
17739
17740                 var close = this.closeTriggerEl();
17741
17742                 if(close){
17743                     close.show();
17744                 }
17745
17746                 this.hideTouchView();
17747
17748                 this.fireEvent('select', this, r, rowIndex);
17749
17750                 return;
17751             }
17752
17753             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17754                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17755                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17756                 return;
17757             }
17758
17759             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17760             this.addItem(r.data);
17761             this.tickItems.push(r.data);
17762         }
17763     },
17764     
17765     getAutoCreateNativeIOS : function()
17766     {
17767         var cfg = {
17768             cls: 'form-group' //input-group,
17769         };
17770         
17771         var combobox =  {
17772             tag: 'select',
17773             cls : 'roo-ios-select'
17774         };
17775         
17776         if (this.name) {
17777             combobox.name = this.name;
17778         }
17779         
17780         if (this.disabled) {
17781             combobox.disabled = true;
17782         }
17783         
17784         var settings = this;
17785         
17786         ['xs','sm','md','lg'].map(function(size){
17787             if (settings[size]) {
17788                 cfg.cls += ' col-' + size + '-' + settings[size];
17789             }
17790         });
17791         
17792         cfg.cn = combobox;
17793         
17794         return cfg;
17795         
17796     },
17797     
17798     initIOSView : function()
17799     {
17800         this.store.on('load', this.onIOSViewLoad, this);
17801         
17802         return;
17803     },
17804     
17805     onIOSViewLoad : function()
17806     {
17807         if(this.store.getCount() < 1){
17808             return;
17809         }
17810         
17811         this.clearIOSView();
17812         
17813         if(this.allowBlank) {
17814             
17815             var default_text = '-- SELECT --';
17816             
17817             if(this.placeholder.length){
17818                 default_text = this.placeholder;
17819             }
17820             
17821             if(this.emptyTitle.length){
17822                 default_text += ' - ' + this.emptyTitle + ' -';
17823             }
17824             
17825             var opt = this.inputEl().createChild({
17826                 tag: 'option',
17827                 value : 0,
17828                 html : default_text
17829             });
17830             
17831             var o = {};
17832             o[this.valueField] = 0;
17833             o[this.displayField] = default_text;
17834             
17835             this.ios_options.push({
17836                 data : o,
17837                 el : opt
17838             });
17839             
17840         }
17841         
17842         this.store.data.each(function(d, rowIndex){
17843             
17844             var html = '';
17845             
17846             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17847                 html = d.data[this.displayField];
17848             }
17849             
17850             var value = '';
17851             
17852             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17853                 value = d.data[this.valueField];
17854             }
17855             
17856             var option = {
17857                 tag: 'option',
17858                 value : value,
17859                 html : html
17860             };
17861             
17862             if(this.value == d.data[this.valueField]){
17863                 option['selected'] = true;
17864             }
17865             
17866             var opt = this.inputEl().createChild(option);
17867             
17868             this.ios_options.push({
17869                 data : d.data,
17870                 el : opt
17871             });
17872             
17873         }, this);
17874         
17875         this.inputEl().on('change', function(){
17876            this.fireEvent('select', this);
17877         }, this);
17878         
17879     },
17880     
17881     clearIOSView: function()
17882     {
17883         this.inputEl().dom.innerHTML = '';
17884         
17885         this.ios_options = [];
17886     },
17887     
17888     setIOSValue: function(v)
17889     {
17890         this.value = v;
17891         
17892         if(!this.ios_options){
17893             return;
17894         }
17895         
17896         Roo.each(this.ios_options, function(opts){
17897            
17898            opts.el.dom.removeAttribute('selected');
17899            
17900            if(opts.data[this.valueField] != v){
17901                return;
17902            }
17903            
17904            opts.el.dom.setAttribute('selected', true);
17905            
17906         }, this);
17907     }
17908
17909     /** 
17910     * @cfg {Boolean} grow 
17911     * @hide 
17912     */
17913     /** 
17914     * @cfg {Number} growMin 
17915     * @hide 
17916     */
17917     /** 
17918     * @cfg {Number} growMax 
17919     * @hide 
17920     */
17921     /**
17922      * @hide
17923      * @method autoSize
17924      */
17925 });
17926
17927 Roo.apply(Roo.bootstrap.ComboBox,  {
17928     
17929     header : {
17930         tag: 'div',
17931         cls: 'modal-header',
17932         cn: [
17933             {
17934                 tag: 'h4',
17935                 cls: 'modal-title'
17936             }
17937         ]
17938     },
17939     
17940     body : {
17941         tag: 'div',
17942         cls: 'modal-body',
17943         cn: [
17944             {
17945                 tag: 'ul',
17946                 cls: 'list-group'
17947             }
17948         ]
17949     },
17950     
17951     listItemRadio : {
17952         tag: 'li',
17953         cls: 'list-group-item',
17954         cn: [
17955             {
17956                 tag: 'span',
17957                 cls: 'roo-combobox-list-group-item-value'
17958             },
17959             {
17960                 tag: 'div',
17961                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17962                 cn: [
17963                     {
17964                         tag: 'input',
17965                         type: 'radio'
17966                     },
17967                     {
17968                         tag: 'label'
17969                     }
17970                 ]
17971             }
17972         ]
17973     },
17974     
17975     listItemCheckbox : {
17976         tag: 'li',
17977         cls: 'list-group-item',
17978         cn: [
17979             {
17980                 tag: 'span',
17981                 cls: 'roo-combobox-list-group-item-value'
17982             },
17983             {
17984                 tag: 'div',
17985                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17986                 cn: [
17987                     {
17988                         tag: 'input',
17989                         type: 'checkbox'
17990                     },
17991                     {
17992                         tag: 'label'
17993                     }
17994                 ]
17995             }
17996         ]
17997     },
17998     
17999     emptyResult : {
18000         tag: 'div',
18001         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18002     },
18003     
18004     footer : {
18005         tag: 'div',
18006         cls: 'modal-footer',
18007         cn: [
18008             {
18009                 tag: 'div',
18010                 cls: 'row',
18011                 cn: [
18012                     {
18013                         tag: 'div',
18014                         cls: 'col-xs-6 text-left',
18015                         cn: {
18016                             tag: 'button',
18017                             cls: 'btn btn-danger roo-touch-view-cancel',
18018                             html: 'Cancel'
18019                         }
18020                     },
18021                     {
18022                         tag: 'div',
18023                         cls: 'col-xs-6 text-right',
18024                         cn: {
18025                             tag: 'button',
18026                             cls: 'btn btn-success roo-touch-view-ok',
18027                             html: 'OK'
18028                         }
18029                     }
18030                 ]
18031             }
18032         ]
18033         
18034     }
18035 });
18036
18037 Roo.apply(Roo.bootstrap.ComboBox,  {
18038     
18039     touchViewTemplate : {
18040         tag: 'div',
18041         cls: 'modal fade roo-combobox-touch-view',
18042         cn: [
18043             {
18044                 tag: 'div',
18045                 cls: 'modal-dialog',
18046                 style : 'position:fixed', // we have to fix position....
18047                 cn: [
18048                     {
18049                         tag: 'div',
18050                         cls: 'modal-content',
18051                         cn: [
18052                             Roo.bootstrap.ComboBox.header,
18053                             Roo.bootstrap.ComboBox.body,
18054                             Roo.bootstrap.ComboBox.footer
18055                         ]
18056                     }
18057                 ]
18058             }
18059         ]
18060     }
18061 });/*
18062  * Based on:
18063  * Ext JS Library 1.1.1
18064  * Copyright(c) 2006-2007, Ext JS, LLC.
18065  *
18066  * Originally Released Under LGPL - original licence link has changed is not relivant.
18067  *
18068  * Fork - LGPL
18069  * <script type="text/javascript">
18070  */
18071
18072 /**
18073  * @class Roo.View
18074  * @extends Roo.util.Observable
18075  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18076  * This class also supports single and multi selection modes. <br>
18077  * Create a data model bound view:
18078  <pre><code>
18079  var store = new Roo.data.Store(...);
18080
18081  var view = new Roo.View({
18082     el : "my-element",
18083     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18084  
18085     singleSelect: true,
18086     selectedClass: "ydataview-selected",
18087     store: store
18088  });
18089
18090  // listen for node click?
18091  view.on("click", function(vw, index, node, e){
18092  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18093  });
18094
18095  // load XML data
18096  dataModel.load("foobar.xml");
18097  </code></pre>
18098  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18099  * <br><br>
18100  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18101  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18102  * 
18103  * Note: old style constructor is still suported (container, template, config)
18104  * 
18105  * @constructor
18106  * Create a new View
18107  * @param {Object} config The config object
18108  * 
18109  */
18110 Roo.View = function(config, depreciated_tpl, depreciated_config){
18111     
18112     this.parent = false;
18113     
18114     if (typeof(depreciated_tpl) == 'undefined') {
18115         // new way.. - universal constructor.
18116         Roo.apply(this, config);
18117         this.el  = Roo.get(this.el);
18118     } else {
18119         // old format..
18120         this.el  = Roo.get(config);
18121         this.tpl = depreciated_tpl;
18122         Roo.apply(this, depreciated_config);
18123     }
18124     this.wrapEl  = this.el.wrap().wrap();
18125     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18126     
18127     
18128     if(typeof(this.tpl) == "string"){
18129         this.tpl = new Roo.Template(this.tpl);
18130     } else {
18131         // support xtype ctors..
18132         this.tpl = new Roo.factory(this.tpl, Roo);
18133     }
18134     
18135     
18136     this.tpl.compile();
18137     
18138     /** @private */
18139     this.addEvents({
18140         /**
18141          * @event beforeclick
18142          * Fires before a click is processed. Returns false to cancel the default action.
18143          * @param {Roo.View} this
18144          * @param {Number} index The index of the target node
18145          * @param {HTMLElement} node The target node
18146          * @param {Roo.EventObject} e The raw event object
18147          */
18148             "beforeclick" : true,
18149         /**
18150          * @event click
18151          * Fires when a template node is clicked.
18152          * @param {Roo.View} this
18153          * @param {Number} index The index of the target node
18154          * @param {HTMLElement} node The target node
18155          * @param {Roo.EventObject} e The raw event object
18156          */
18157             "click" : true,
18158         /**
18159          * @event dblclick
18160          * Fires when a template node is double clicked.
18161          * @param {Roo.View} this
18162          * @param {Number} index The index of the target node
18163          * @param {HTMLElement} node The target node
18164          * @param {Roo.EventObject} e The raw event object
18165          */
18166             "dblclick" : true,
18167         /**
18168          * @event contextmenu
18169          * Fires when a template node is right clicked.
18170          * @param {Roo.View} this
18171          * @param {Number} index The index of the target node
18172          * @param {HTMLElement} node The target node
18173          * @param {Roo.EventObject} e The raw event object
18174          */
18175             "contextmenu" : true,
18176         /**
18177          * @event selectionchange
18178          * Fires when the selected nodes change.
18179          * @param {Roo.View} this
18180          * @param {Array} selections Array of the selected nodes
18181          */
18182             "selectionchange" : true,
18183     
18184         /**
18185          * @event beforeselect
18186          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18187          * @param {Roo.View} this
18188          * @param {HTMLElement} node The node to be selected
18189          * @param {Array} selections Array of currently selected nodes
18190          */
18191             "beforeselect" : true,
18192         /**
18193          * @event preparedata
18194          * Fires on every row to render, to allow you to change the data.
18195          * @param {Roo.View} this
18196          * @param {Object} data to be rendered (change this)
18197          */
18198           "preparedata" : true
18199           
18200           
18201         });
18202
18203
18204
18205     this.el.on({
18206         "click": this.onClick,
18207         "dblclick": this.onDblClick,
18208         "contextmenu": this.onContextMenu,
18209         scope:this
18210     });
18211
18212     this.selections = [];
18213     this.nodes = [];
18214     this.cmp = new Roo.CompositeElementLite([]);
18215     if(this.store){
18216         this.store = Roo.factory(this.store, Roo.data);
18217         this.setStore(this.store, true);
18218     }
18219     
18220     if ( this.footer && this.footer.xtype) {
18221            
18222          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18223         
18224         this.footer.dataSource = this.store;
18225         this.footer.container = fctr;
18226         this.footer = Roo.factory(this.footer, Roo);
18227         fctr.insertFirst(this.el);
18228         
18229         // this is a bit insane - as the paging toolbar seems to detach the el..
18230 //        dom.parentNode.parentNode.parentNode
18231          // they get detached?
18232     }
18233     
18234     
18235     Roo.View.superclass.constructor.call(this);
18236     
18237     
18238 };
18239
18240 Roo.extend(Roo.View, Roo.util.Observable, {
18241     
18242      /**
18243      * @cfg {Roo.data.Store} store Data store to load data from.
18244      */
18245     store : false,
18246     
18247     /**
18248      * @cfg {String|Roo.Element} el The container element.
18249      */
18250     el : '',
18251     
18252     /**
18253      * @cfg {String|Roo.Template} tpl The template used by this View 
18254      */
18255     tpl : false,
18256     /**
18257      * @cfg {String} dataName the named area of the template to use as the data area
18258      *                          Works with domtemplates roo-name="name"
18259      */
18260     dataName: false,
18261     /**
18262      * @cfg {String} selectedClass The css class to add to selected nodes
18263      */
18264     selectedClass : "x-view-selected",
18265      /**
18266      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18267      */
18268     emptyText : "",
18269     
18270     /**
18271      * @cfg {String} text to display on mask (default Loading)
18272      */
18273     mask : false,
18274     /**
18275      * @cfg {Boolean} multiSelect Allow multiple selection
18276      */
18277     multiSelect : false,
18278     /**
18279      * @cfg {Boolean} singleSelect Allow single selection
18280      */
18281     singleSelect:  false,
18282     
18283     /**
18284      * @cfg {Boolean} toggleSelect - selecting 
18285      */
18286     toggleSelect : false,
18287     
18288     /**
18289      * @cfg {Boolean} tickable - selecting 
18290      */
18291     tickable : false,
18292     
18293     /**
18294      * Returns the element this view is bound to.
18295      * @return {Roo.Element}
18296      */
18297     getEl : function(){
18298         return this.wrapEl;
18299     },
18300     
18301     
18302
18303     /**
18304      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18305      */
18306     refresh : function(){
18307         //Roo.log('refresh');
18308         var t = this.tpl;
18309         
18310         // if we are using something like 'domtemplate', then
18311         // the what gets used is:
18312         // t.applySubtemplate(NAME, data, wrapping data..)
18313         // the outer template then get' applied with
18314         //     the store 'extra data'
18315         // and the body get's added to the
18316         //      roo-name="data" node?
18317         //      <span class='roo-tpl-{name}'></span> ?????
18318         
18319         
18320         
18321         this.clearSelections();
18322         this.el.update("");
18323         var html = [];
18324         var records = this.store.getRange();
18325         if(records.length < 1) {
18326             
18327             // is this valid??  = should it render a template??
18328             
18329             this.el.update(this.emptyText);
18330             return;
18331         }
18332         var el = this.el;
18333         if (this.dataName) {
18334             this.el.update(t.apply(this.store.meta)); //????
18335             el = this.el.child('.roo-tpl-' + this.dataName);
18336         }
18337         
18338         for(var i = 0, len = records.length; i < len; i++){
18339             var data = this.prepareData(records[i].data, i, records[i]);
18340             this.fireEvent("preparedata", this, data, i, records[i]);
18341             
18342             var d = Roo.apply({}, data);
18343             
18344             if(this.tickable){
18345                 Roo.apply(d, {'roo-id' : Roo.id()});
18346                 
18347                 var _this = this;
18348             
18349                 Roo.each(this.parent.item, function(item){
18350                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18351                         return;
18352                     }
18353                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18354                 });
18355             }
18356             
18357             html[html.length] = Roo.util.Format.trim(
18358                 this.dataName ?
18359                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18360                     t.apply(d)
18361             );
18362         }
18363         
18364         
18365         
18366         el.update(html.join(""));
18367         this.nodes = el.dom.childNodes;
18368         this.updateIndexes(0);
18369     },
18370     
18371
18372     /**
18373      * Function to override to reformat the data that is sent to
18374      * the template for each node.
18375      * DEPRICATED - use the preparedata event handler.
18376      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18377      * a JSON object for an UpdateManager bound view).
18378      */
18379     prepareData : function(data, index, record)
18380     {
18381         this.fireEvent("preparedata", this, data, index, record);
18382         return data;
18383     },
18384
18385     onUpdate : function(ds, record){
18386         // Roo.log('on update');   
18387         this.clearSelections();
18388         var index = this.store.indexOf(record);
18389         var n = this.nodes[index];
18390         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18391         n.parentNode.removeChild(n);
18392         this.updateIndexes(index, index);
18393     },
18394
18395     
18396     
18397 // --------- FIXME     
18398     onAdd : function(ds, records, index)
18399     {
18400         //Roo.log(['on Add', ds, records, index] );        
18401         this.clearSelections();
18402         if(this.nodes.length == 0){
18403             this.refresh();
18404             return;
18405         }
18406         var n = this.nodes[index];
18407         for(var i = 0, len = records.length; i < len; i++){
18408             var d = this.prepareData(records[i].data, i, records[i]);
18409             if(n){
18410                 this.tpl.insertBefore(n, d);
18411             }else{
18412                 
18413                 this.tpl.append(this.el, d);
18414             }
18415         }
18416         this.updateIndexes(index);
18417     },
18418
18419     onRemove : function(ds, record, index){
18420        // Roo.log('onRemove');
18421         this.clearSelections();
18422         var el = this.dataName  ?
18423             this.el.child('.roo-tpl-' + this.dataName) :
18424             this.el; 
18425         
18426         el.dom.removeChild(this.nodes[index]);
18427         this.updateIndexes(index);
18428     },
18429
18430     /**
18431      * Refresh an individual node.
18432      * @param {Number} index
18433      */
18434     refreshNode : function(index){
18435         this.onUpdate(this.store, this.store.getAt(index));
18436     },
18437
18438     updateIndexes : function(startIndex, endIndex){
18439         var ns = this.nodes;
18440         startIndex = startIndex || 0;
18441         endIndex = endIndex || ns.length - 1;
18442         for(var i = startIndex; i <= endIndex; i++){
18443             ns[i].nodeIndex = i;
18444         }
18445     },
18446
18447     /**
18448      * Changes the data store this view uses and refresh the view.
18449      * @param {Store} store
18450      */
18451     setStore : function(store, initial){
18452         if(!initial && this.store){
18453             this.store.un("datachanged", this.refresh);
18454             this.store.un("add", this.onAdd);
18455             this.store.un("remove", this.onRemove);
18456             this.store.un("update", this.onUpdate);
18457             this.store.un("clear", this.refresh);
18458             this.store.un("beforeload", this.onBeforeLoad);
18459             this.store.un("load", this.onLoad);
18460             this.store.un("loadexception", this.onLoad);
18461         }
18462         if(store){
18463           
18464             store.on("datachanged", this.refresh, this);
18465             store.on("add", this.onAdd, this);
18466             store.on("remove", this.onRemove, this);
18467             store.on("update", this.onUpdate, this);
18468             store.on("clear", this.refresh, this);
18469             store.on("beforeload", this.onBeforeLoad, this);
18470             store.on("load", this.onLoad, this);
18471             store.on("loadexception", this.onLoad, this);
18472         }
18473         
18474         if(store){
18475             this.refresh();
18476         }
18477     },
18478     /**
18479      * onbeforeLoad - masks the loading area.
18480      *
18481      */
18482     onBeforeLoad : function(store,opts)
18483     {
18484          //Roo.log('onBeforeLoad');   
18485         if (!opts.add) {
18486             this.el.update("");
18487         }
18488         this.el.mask(this.mask ? this.mask : "Loading" ); 
18489     },
18490     onLoad : function ()
18491     {
18492         this.el.unmask();
18493     },
18494     
18495
18496     /**
18497      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18498      * @param {HTMLElement} node
18499      * @return {HTMLElement} The template node
18500      */
18501     findItemFromChild : function(node){
18502         var el = this.dataName  ?
18503             this.el.child('.roo-tpl-' + this.dataName,true) :
18504             this.el.dom; 
18505         
18506         if(!node || node.parentNode == el){
18507                     return node;
18508             }
18509             var p = node.parentNode;
18510             while(p && p != el){
18511             if(p.parentNode == el){
18512                 return p;
18513             }
18514             p = p.parentNode;
18515         }
18516             return null;
18517     },
18518
18519     /** @ignore */
18520     onClick : function(e){
18521         var item = this.findItemFromChild(e.getTarget());
18522         if(item){
18523             var index = this.indexOf(item);
18524             if(this.onItemClick(item, index, e) !== false){
18525                 this.fireEvent("click", this, index, item, e);
18526             }
18527         }else{
18528             this.clearSelections();
18529         }
18530     },
18531
18532     /** @ignore */
18533     onContextMenu : function(e){
18534         var item = this.findItemFromChild(e.getTarget());
18535         if(item){
18536             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18537         }
18538     },
18539
18540     /** @ignore */
18541     onDblClick : function(e){
18542         var item = this.findItemFromChild(e.getTarget());
18543         if(item){
18544             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18545         }
18546     },
18547
18548     onItemClick : function(item, index, e)
18549     {
18550         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18551             return false;
18552         }
18553         if (this.toggleSelect) {
18554             var m = this.isSelected(item) ? 'unselect' : 'select';
18555             //Roo.log(m);
18556             var _t = this;
18557             _t[m](item, true, false);
18558             return true;
18559         }
18560         if(this.multiSelect || this.singleSelect){
18561             if(this.multiSelect && e.shiftKey && this.lastSelection){
18562                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18563             }else{
18564                 this.select(item, this.multiSelect && e.ctrlKey);
18565                 this.lastSelection = item;
18566             }
18567             
18568             if(!this.tickable){
18569                 e.preventDefault();
18570             }
18571             
18572         }
18573         return true;
18574     },
18575
18576     /**
18577      * Get the number of selected nodes.
18578      * @return {Number}
18579      */
18580     getSelectionCount : function(){
18581         return this.selections.length;
18582     },
18583
18584     /**
18585      * Get the currently selected nodes.
18586      * @return {Array} An array of HTMLElements
18587      */
18588     getSelectedNodes : function(){
18589         return this.selections;
18590     },
18591
18592     /**
18593      * Get the indexes of the selected nodes.
18594      * @return {Array}
18595      */
18596     getSelectedIndexes : function(){
18597         var indexes = [], s = this.selections;
18598         for(var i = 0, len = s.length; i < len; i++){
18599             indexes.push(s[i].nodeIndex);
18600         }
18601         return indexes;
18602     },
18603
18604     /**
18605      * Clear all selections
18606      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18607      */
18608     clearSelections : function(suppressEvent){
18609         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18610             this.cmp.elements = this.selections;
18611             this.cmp.removeClass(this.selectedClass);
18612             this.selections = [];
18613             if(!suppressEvent){
18614                 this.fireEvent("selectionchange", this, this.selections);
18615             }
18616         }
18617     },
18618
18619     /**
18620      * Returns true if the passed node is selected
18621      * @param {HTMLElement/Number} node The node or node index
18622      * @return {Boolean}
18623      */
18624     isSelected : function(node){
18625         var s = this.selections;
18626         if(s.length < 1){
18627             return false;
18628         }
18629         node = this.getNode(node);
18630         return s.indexOf(node) !== -1;
18631     },
18632
18633     /**
18634      * Selects nodes.
18635      * @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
18636      * @param {Boolean} keepExisting (optional) true to keep existing selections
18637      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18638      */
18639     select : function(nodeInfo, keepExisting, suppressEvent){
18640         if(nodeInfo instanceof Array){
18641             if(!keepExisting){
18642                 this.clearSelections(true);
18643             }
18644             for(var i = 0, len = nodeInfo.length; i < len; i++){
18645                 this.select(nodeInfo[i], true, true);
18646             }
18647             return;
18648         } 
18649         var node = this.getNode(nodeInfo);
18650         if(!node || this.isSelected(node)){
18651             return; // already selected.
18652         }
18653         if(!keepExisting){
18654             this.clearSelections(true);
18655         }
18656         
18657         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18658             Roo.fly(node).addClass(this.selectedClass);
18659             this.selections.push(node);
18660             if(!suppressEvent){
18661                 this.fireEvent("selectionchange", this, this.selections);
18662             }
18663         }
18664         
18665         
18666     },
18667       /**
18668      * Unselects nodes.
18669      * @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
18670      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18671      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18672      */
18673     unselect : function(nodeInfo, keepExisting, suppressEvent)
18674     {
18675         if(nodeInfo instanceof Array){
18676             Roo.each(this.selections, function(s) {
18677                 this.unselect(s, nodeInfo);
18678             }, this);
18679             return;
18680         }
18681         var node = this.getNode(nodeInfo);
18682         if(!node || !this.isSelected(node)){
18683             //Roo.log("not selected");
18684             return; // not selected.
18685         }
18686         // fireevent???
18687         var ns = [];
18688         Roo.each(this.selections, function(s) {
18689             if (s == node ) {
18690                 Roo.fly(node).removeClass(this.selectedClass);
18691
18692                 return;
18693             }
18694             ns.push(s);
18695         },this);
18696         
18697         this.selections= ns;
18698         this.fireEvent("selectionchange", this, this.selections);
18699     },
18700
18701     /**
18702      * Gets a template node.
18703      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18704      * @return {HTMLElement} The node or null if it wasn't found
18705      */
18706     getNode : function(nodeInfo){
18707         if(typeof nodeInfo == "string"){
18708             return document.getElementById(nodeInfo);
18709         }else if(typeof nodeInfo == "number"){
18710             return this.nodes[nodeInfo];
18711         }
18712         return nodeInfo;
18713     },
18714
18715     /**
18716      * Gets a range template nodes.
18717      * @param {Number} startIndex
18718      * @param {Number} endIndex
18719      * @return {Array} An array of nodes
18720      */
18721     getNodes : function(start, end){
18722         var ns = this.nodes;
18723         start = start || 0;
18724         end = typeof end == "undefined" ? ns.length - 1 : end;
18725         var nodes = [];
18726         if(start <= end){
18727             for(var i = start; i <= end; i++){
18728                 nodes.push(ns[i]);
18729             }
18730         } else{
18731             for(var i = start; i >= end; i--){
18732                 nodes.push(ns[i]);
18733             }
18734         }
18735         return nodes;
18736     },
18737
18738     /**
18739      * Finds the index of the passed node
18740      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18741      * @return {Number} The index of the node or -1
18742      */
18743     indexOf : function(node){
18744         node = this.getNode(node);
18745         if(typeof node.nodeIndex == "number"){
18746             return node.nodeIndex;
18747         }
18748         var ns = this.nodes;
18749         for(var i = 0, len = ns.length; i < len; i++){
18750             if(ns[i] == node){
18751                 return i;
18752             }
18753         }
18754         return -1;
18755     }
18756 });
18757 /*
18758  * - LGPL
18759  *
18760  * based on jquery fullcalendar
18761  * 
18762  */
18763
18764 Roo.bootstrap = Roo.bootstrap || {};
18765 /**
18766  * @class Roo.bootstrap.Calendar
18767  * @extends Roo.bootstrap.Component
18768  * Bootstrap Calendar class
18769  * @cfg {Boolean} loadMask (true|false) default false
18770  * @cfg {Object} header generate the user specific header of the calendar, default false
18771
18772  * @constructor
18773  * Create a new Container
18774  * @param {Object} config The config object
18775  */
18776
18777
18778
18779 Roo.bootstrap.Calendar = function(config){
18780     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18781      this.addEvents({
18782         /**
18783              * @event select
18784              * Fires when a date is selected
18785              * @param {DatePicker} this
18786              * @param {Date} date The selected date
18787              */
18788         'select': true,
18789         /**
18790              * @event monthchange
18791              * Fires when the displayed month changes 
18792              * @param {DatePicker} this
18793              * @param {Date} date The selected month
18794              */
18795         'monthchange': true,
18796         /**
18797              * @event evententer
18798              * Fires when mouse over an event
18799              * @param {Calendar} this
18800              * @param {event} Event
18801              */
18802         'evententer': true,
18803         /**
18804              * @event eventleave
18805              * Fires when the mouse leaves an
18806              * @param {Calendar} this
18807              * @param {event}
18808              */
18809         'eventleave': true,
18810         /**
18811              * @event eventclick
18812              * Fires when the mouse click an
18813              * @param {Calendar} this
18814              * @param {event}
18815              */
18816         'eventclick': true
18817         
18818     });
18819
18820 };
18821
18822 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18823     
18824      /**
18825      * @cfg {Number} startDay
18826      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18827      */
18828     startDay : 0,
18829     
18830     loadMask : false,
18831     
18832     header : false,
18833       
18834     getAutoCreate : function(){
18835         
18836         
18837         var fc_button = function(name, corner, style, content ) {
18838             return Roo.apply({},{
18839                 tag : 'span',
18840                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18841                          (corner.length ?
18842                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18843                             ''
18844                         ),
18845                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18846                 unselectable: 'on'
18847             });
18848         };
18849         
18850         var header = {};
18851         
18852         if(!this.header){
18853             header = {
18854                 tag : 'table',
18855                 cls : 'fc-header',
18856                 style : 'width:100%',
18857                 cn : [
18858                     {
18859                         tag: 'tr',
18860                         cn : [
18861                             {
18862                                 tag : 'td',
18863                                 cls : 'fc-header-left',
18864                                 cn : [
18865                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18866                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18867                                     { tag: 'span', cls: 'fc-header-space' },
18868                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18869
18870
18871                                 ]
18872                             },
18873
18874                             {
18875                                 tag : 'td',
18876                                 cls : 'fc-header-center',
18877                                 cn : [
18878                                     {
18879                                         tag: 'span',
18880                                         cls: 'fc-header-title',
18881                                         cn : {
18882                                             tag: 'H2',
18883                                             html : 'month / year'
18884                                         }
18885                                     }
18886
18887                                 ]
18888                             },
18889                             {
18890                                 tag : 'td',
18891                                 cls : 'fc-header-right',
18892                                 cn : [
18893                               /*      fc_button('month', 'left', '', 'month' ),
18894                                     fc_button('week', '', '', 'week' ),
18895                                     fc_button('day', 'right', '', 'day' )
18896                                 */    
18897
18898                                 ]
18899                             }
18900
18901                         ]
18902                     }
18903                 ]
18904             };
18905         }
18906         
18907         header = this.header;
18908         
18909        
18910         var cal_heads = function() {
18911             var ret = [];
18912             // fixme - handle this.
18913             
18914             for (var i =0; i < Date.dayNames.length; i++) {
18915                 var d = Date.dayNames[i];
18916                 ret.push({
18917                     tag: 'th',
18918                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18919                     html : d.substring(0,3)
18920                 });
18921                 
18922             }
18923             ret[0].cls += ' fc-first';
18924             ret[6].cls += ' fc-last';
18925             return ret;
18926         };
18927         var cal_cell = function(n) {
18928             return  {
18929                 tag: 'td',
18930                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18931                 cn : [
18932                     {
18933                         cn : [
18934                             {
18935                                 cls: 'fc-day-number',
18936                                 html: 'D'
18937                             },
18938                             {
18939                                 cls: 'fc-day-content',
18940                              
18941                                 cn : [
18942                                      {
18943                                         style: 'position: relative;' // height: 17px;
18944                                     }
18945                                 ]
18946                             }
18947                             
18948                             
18949                         ]
18950                     }
18951                 ]
18952                 
18953             }
18954         };
18955         var cal_rows = function() {
18956             
18957             var ret = [];
18958             for (var r = 0; r < 6; r++) {
18959                 var row= {
18960                     tag : 'tr',
18961                     cls : 'fc-week',
18962                     cn : []
18963                 };
18964                 
18965                 for (var i =0; i < Date.dayNames.length; i++) {
18966                     var d = Date.dayNames[i];
18967                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18968
18969                 }
18970                 row.cn[0].cls+=' fc-first';
18971                 row.cn[0].cn[0].style = 'min-height:90px';
18972                 row.cn[6].cls+=' fc-last';
18973                 ret.push(row);
18974                 
18975             }
18976             ret[0].cls += ' fc-first';
18977             ret[4].cls += ' fc-prev-last';
18978             ret[5].cls += ' fc-last';
18979             return ret;
18980             
18981         };
18982         
18983         var cal_table = {
18984             tag: 'table',
18985             cls: 'fc-border-separate',
18986             style : 'width:100%',
18987             cellspacing  : 0,
18988             cn : [
18989                 { 
18990                     tag: 'thead',
18991                     cn : [
18992                         { 
18993                             tag: 'tr',
18994                             cls : 'fc-first fc-last',
18995                             cn : cal_heads()
18996                         }
18997                     ]
18998                 },
18999                 { 
19000                     tag: 'tbody',
19001                     cn : cal_rows()
19002                 }
19003                   
19004             ]
19005         };
19006          
19007          var cfg = {
19008             cls : 'fc fc-ltr',
19009             cn : [
19010                 header,
19011                 {
19012                     cls : 'fc-content',
19013                     style : "position: relative;",
19014                     cn : [
19015                         {
19016                             cls : 'fc-view fc-view-month fc-grid',
19017                             style : 'position: relative',
19018                             unselectable : 'on',
19019                             cn : [
19020                                 {
19021                                     cls : 'fc-event-container',
19022                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19023                                 },
19024                                 cal_table
19025                             ]
19026                         }
19027                     ]
19028     
19029                 }
19030            ] 
19031             
19032         };
19033         
19034          
19035         
19036         return cfg;
19037     },
19038     
19039     
19040     initEvents : function()
19041     {
19042         if(!this.store){
19043             throw "can not find store for calendar";
19044         }
19045         
19046         var mark = {
19047             tag: "div",
19048             cls:"x-dlg-mask",
19049             style: "text-align:center",
19050             cn: [
19051                 {
19052                     tag: "div",
19053                     style: "background-color:white;width:50%;margin:250 auto",
19054                     cn: [
19055                         {
19056                             tag: "img",
19057                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19058                         },
19059                         {
19060                             tag: "span",
19061                             html: "Loading"
19062                         }
19063                         
19064                     ]
19065                 }
19066             ]
19067         };
19068         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19069         
19070         var size = this.el.select('.fc-content', true).first().getSize();
19071         this.maskEl.setSize(size.width, size.height);
19072         this.maskEl.enableDisplayMode("block");
19073         if(!this.loadMask){
19074             this.maskEl.hide();
19075         }
19076         
19077         this.store = Roo.factory(this.store, Roo.data);
19078         this.store.on('load', this.onLoad, this);
19079         this.store.on('beforeload', this.onBeforeLoad, this);
19080         
19081         this.resize();
19082         
19083         this.cells = this.el.select('.fc-day',true);
19084         //Roo.log(this.cells);
19085         this.textNodes = this.el.query('.fc-day-number');
19086         this.cells.addClassOnOver('fc-state-hover');
19087         
19088         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19089         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19090         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19091         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19092         
19093         this.on('monthchange', this.onMonthChange, this);
19094         
19095         this.update(new Date().clearTime());
19096     },
19097     
19098     resize : function() {
19099         var sz  = this.el.getSize();
19100         
19101         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19102         this.el.select('.fc-day-content div',true).setHeight(34);
19103     },
19104     
19105     
19106     // private
19107     showPrevMonth : function(e){
19108         this.update(this.activeDate.add("mo", -1));
19109     },
19110     showToday : function(e){
19111         this.update(new Date().clearTime());
19112     },
19113     // private
19114     showNextMonth : function(e){
19115         this.update(this.activeDate.add("mo", 1));
19116     },
19117
19118     // private
19119     showPrevYear : function(){
19120         this.update(this.activeDate.add("y", -1));
19121     },
19122
19123     // private
19124     showNextYear : function(){
19125         this.update(this.activeDate.add("y", 1));
19126     },
19127
19128     
19129    // private
19130     update : function(date)
19131     {
19132         var vd = this.activeDate;
19133         this.activeDate = date;
19134 //        if(vd && this.el){
19135 //            var t = date.getTime();
19136 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19137 //                Roo.log('using add remove');
19138 //                
19139 //                this.fireEvent('monthchange', this, date);
19140 //                
19141 //                this.cells.removeClass("fc-state-highlight");
19142 //                this.cells.each(function(c){
19143 //                   if(c.dateValue == t){
19144 //                       c.addClass("fc-state-highlight");
19145 //                       setTimeout(function(){
19146 //                            try{c.dom.firstChild.focus();}catch(e){}
19147 //                       }, 50);
19148 //                       return false;
19149 //                   }
19150 //                   return true;
19151 //                });
19152 //                return;
19153 //            }
19154 //        }
19155         
19156         var days = date.getDaysInMonth();
19157         
19158         var firstOfMonth = date.getFirstDateOfMonth();
19159         var startingPos = firstOfMonth.getDay()-this.startDay;
19160         
19161         if(startingPos < this.startDay){
19162             startingPos += 7;
19163         }
19164         
19165         var pm = date.add(Date.MONTH, -1);
19166         var prevStart = pm.getDaysInMonth()-startingPos;
19167 //        
19168         this.cells = this.el.select('.fc-day',true);
19169         this.textNodes = this.el.query('.fc-day-number');
19170         this.cells.addClassOnOver('fc-state-hover');
19171         
19172         var cells = this.cells.elements;
19173         var textEls = this.textNodes;
19174         
19175         Roo.each(cells, function(cell){
19176             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19177         });
19178         
19179         days += startingPos;
19180
19181         // convert everything to numbers so it's fast
19182         var day = 86400000;
19183         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19184         //Roo.log(d);
19185         //Roo.log(pm);
19186         //Roo.log(prevStart);
19187         
19188         var today = new Date().clearTime().getTime();
19189         var sel = date.clearTime().getTime();
19190         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19191         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19192         var ddMatch = this.disabledDatesRE;
19193         var ddText = this.disabledDatesText;
19194         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19195         var ddaysText = this.disabledDaysText;
19196         var format = this.format;
19197         
19198         var setCellClass = function(cal, cell){
19199             cell.row = 0;
19200             cell.events = [];
19201             cell.more = [];
19202             //Roo.log('set Cell Class');
19203             cell.title = "";
19204             var t = d.getTime();
19205             
19206             //Roo.log(d);
19207             
19208             cell.dateValue = t;
19209             if(t == today){
19210                 cell.className += " fc-today";
19211                 cell.className += " fc-state-highlight";
19212                 cell.title = cal.todayText;
19213             }
19214             if(t == sel){
19215                 // disable highlight in other month..
19216                 //cell.className += " fc-state-highlight";
19217                 
19218             }
19219             // disabling
19220             if(t < min) {
19221                 cell.className = " fc-state-disabled";
19222                 cell.title = cal.minText;
19223                 return;
19224             }
19225             if(t > max) {
19226                 cell.className = " fc-state-disabled";
19227                 cell.title = cal.maxText;
19228                 return;
19229             }
19230             if(ddays){
19231                 if(ddays.indexOf(d.getDay()) != -1){
19232                     cell.title = ddaysText;
19233                     cell.className = " fc-state-disabled";
19234                 }
19235             }
19236             if(ddMatch && format){
19237                 var fvalue = d.dateFormat(format);
19238                 if(ddMatch.test(fvalue)){
19239                     cell.title = ddText.replace("%0", fvalue);
19240                     cell.className = " fc-state-disabled";
19241                 }
19242             }
19243             
19244             if (!cell.initialClassName) {
19245                 cell.initialClassName = cell.dom.className;
19246             }
19247             
19248             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19249         };
19250
19251         var i = 0;
19252         
19253         for(; i < startingPos; i++) {
19254             textEls[i].innerHTML = (++prevStart);
19255             d.setDate(d.getDate()+1);
19256             
19257             cells[i].className = "fc-past fc-other-month";
19258             setCellClass(this, cells[i]);
19259         }
19260         
19261         var intDay = 0;
19262         
19263         for(; i < days; i++){
19264             intDay = i - startingPos + 1;
19265             textEls[i].innerHTML = (intDay);
19266             d.setDate(d.getDate()+1);
19267             
19268             cells[i].className = ''; // "x-date-active";
19269             setCellClass(this, cells[i]);
19270         }
19271         var extraDays = 0;
19272         
19273         for(; i < 42; i++) {
19274             textEls[i].innerHTML = (++extraDays);
19275             d.setDate(d.getDate()+1);
19276             
19277             cells[i].className = "fc-future fc-other-month";
19278             setCellClass(this, cells[i]);
19279         }
19280         
19281         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19282         
19283         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19284         
19285         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19286         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19287         
19288         if(totalRows != 6){
19289             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19290             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19291         }
19292         
19293         this.fireEvent('monthchange', this, date);
19294         
19295         
19296         /*
19297         if(!this.internalRender){
19298             var main = this.el.dom.firstChild;
19299             var w = main.offsetWidth;
19300             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19301             Roo.fly(main).setWidth(w);
19302             this.internalRender = true;
19303             // opera does not respect the auto grow header center column
19304             // then, after it gets a width opera refuses to recalculate
19305             // without a second pass
19306             if(Roo.isOpera && !this.secondPass){
19307                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19308                 this.secondPass = true;
19309                 this.update.defer(10, this, [date]);
19310             }
19311         }
19312         */
19313         
19314     },
19315     
19316     findCell : function(dt) {
19317         dt = dt.clearTime().getTime();
19318         var ret = false;
19319         this.cells.each(function(c){
19320             //Roo.log("check " +c.dateValue + '?=' + dt);
19321             if(c.dateValue == dt){
19322                 ret = c;
19323                 return false;
19324             }
19325             return true;
19326         });
19327         
19328         return ret;
19329     },
19330     
19331     findCells : function(ev) {
19332         var s = ev.start.clone().clearTime().getTime();
19333        // Roo.log(s);
19334         var e= ev.end.clone().clearTime().getTime();
19335        // Roo.log(e);
19336         var ret = [];
19337         this.cells.each(function(c){
19338              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19339             
19340             if(c.dateValue > e){
19341                 return ;
19342             }
19343             if(c.dateValue < s){
19344                 return ;
19345             }
19346             ret.push(c);
19347         });
19348         
19349         return ret;    
19350     },
19351     
19352 //    findBestRow: function(cells)
19353 //    {
19354 //        var ret = 0;
19355 //        
19356 //        for (var i =0 ; i < cells.length;i++) {
19357 //            ret  = Math.max(cells[i].rows || 0,ret);
19358 //        }
19359 //        return ret;
19360 //        
19361 //    },
19362     
19363     
19364     addItem : function(ev)
19365     {
19366         // look for vertical location slot in
19367         var cells = this.findCells(ev);
19368         
19369 //        ev.row = this.findBestRow(cells);
19370         
19371         // work out the location.
19372         
19373         var crow = false;
19374         var rows = [];
19375         for(var i =0; i < cells.length; i++) {
19376             
19377             cells[i].row = cells[0].row;
19378             
19379             if(i == 0){
19380                 cells[i].row = cells[i].row + 1;
19381             }
19382             
19383             if (!crow) {
19384                 crow = {
19385                     start : cells[i],
19386                     end :  cells[i]
19387                 };
19388                 continue;
19389             }
19390             if (crow.start.getY() == cells[i].getY()) {
19391                 // on same row.
19392                 crow.end = cells[i];
19393                 continue;
19394             }
19395             // different row.
19396             rows.push(crow);
19397             crow = {
19398                 start: cells[i],
19399                 end : cells[i]
19400             };
19401             
19402         }
19403         
19404         rows.push(crow);
19405         ev.els = [];
19406         ev.rows = rows;
19407         ev.cells = cells;
19408         
19409         cells[0].events.push(ev);
19410         
19411         this.calevents.push(ev);
19412     },
19413     
19414     clearEvents: function() {
19415         
19416         if(!this.calevents){
19417             return;
19418         }
19419         
19420         Roo.each(this.cells.elements, function(c){
19421             c.row = 0;
19422             c.events = [];
19423             c.more = [];
19424         });
19425         
19426         Roo.each(this.calevents, function(e) {
19427             Roo.each(e.els, function(el) {
19428                 el.un('mouseenter' ,this.onEventEnter, this);
19429                 el.un('mouseleave' ,this.onEventLeave, this);
19430                 el.remove();
19431             },this);
19432         },this);
19433         
19434         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19435             e.remove();
19436         });
19437         
19438     },
19439     
19440     renderEvents: function()
19441     {   
19442         var _this = this;
19443         
19444         this.cells.each(function(c) {
19445             
19446             if(c.row < 5){
19447                 return;
19448             }
19449             
19450             var ev = c.events;
19451             
19452             var r = 4;
19453             if(c.row != c.events.length){
19454                 r = 4 - (4 - (c.row - c.events.length));
19455             }
19456             
19457             c.events = ev.slice(0, r);
19458             c.more = ev.slice(r);
19459             
19460             if(c.more.length && c.more.length == 1){
19461                 c.events.push(c.more.pop());
19462             }
19463             
19464             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19465             
19466         });
19467             
19468         this.cells.each(function(c) {
19469             
19470             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19471             
19472             
19473             for (var e = 0; e < c.events.length; e++){
19474                 var ev = c.events[e];
19475                 var rows = ev.rows;
19476                 
19477                 for(var i = 0; i < rows.length; i++) {
19478                 
19479                     // how many rows should it span..
19480
19481                     var  cfg = {
19482                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19483                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19484
19485                         unselectable : "on",
19486                         cn : [
19487                             {
19488                                 cls: 'fc-event-inner',
19489                                 cn : [
19490     //                                {
19491     //                                  tag:'span',
19492     //                                  cls: 'fc-event-time',
19493     //                                  html : cells.length > 1 ? '' : ev.time
19494     //                                },
19495                                     {
19496                                       tag:'span',
19497                                       cls: 'fc-event-title',
19498                                       html : String.format('{0}', ev.title)
19499                                     }
19500
19501
19502                                 ]
19503                             },
19504                             {
19505                                 cls: 'ui-resizable-handle ui-resizable-e',
19506                                 html : '&nbsp;&nbsp;&nbsp'
19507                             }
19508
19509                         ]
19510                     };
19511
19512                     if (i == 0) {
19513                         cfg.cls += ' fc-event-start';
19514                     }
19515                     if ((i+1) == rows.length) {
19516                         cfg.cls += ' fc-event-end';
19517                     }
19518
19519                     var ctr = _this.el.select('.fc-event-container',true).first();
19520                     var cg = ctr.createChild(cfg);
19521
19522                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19523                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19524
19525                     var r = (c.more.length) ? 1 : 0;
19526                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19527                     cg.setWidth(ebox.right - sbox.x -2);
19528
19529                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19530                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19531                     cg.on('click', _this.onEventClick, _this, ev);
19532
19533                     ev.els.push(cg);
19534                     
19535                 }
19536                 
19537             }
19538             
19539             
19540             if(c.more.length){
19541                 var  cfg = {
19542                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19543                     style : 'position: absolute',
19544                     unselectable : "on",
19545                     cn : [
19546                         {
19547                             cls: 'fc-event-inner',
19548                             cn : [
19549                                 {
19550                                   tag:'span',
19551                                   cls: 'fc-event-title',
19552                                   html : 'More'
19553                                 }
19554
19555
19556                             ]
19557                         },
19558                         {
19559                             cls: 'ui-resizable-handle ui-resizable-e',
19560                             html : '&nbsp;&nbsp;&nbsp'
19561                         }
19562
19563                     ]
19564                 };
19565
19566                 var ctr = _this.el.select('.fc-event-container',true).first();
19567                 var cg = ctr.createChild(cfg);
19568
19569                 var sbox = c.select('.fc-day-content',true).first().getBox();
19570                 var ebox = c.select('.fc-day-content',true).first().getBox();
19571                 //Roo.log(cg);
19572                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19573                 cg.setWidth(ebox.right - sbox.x -2);
19574
19575                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19576                 
19577             }
19578             
19579         });
19580         
19581         
19582         
19583     },
19584     
19585     onEventEnter: function (e, el,event,d) {
19586         this.fireEvent('evententer', this, el, event);
19587     },
19588     
19589     onEventLeave: function (e, el,event,d) {
19590         this.fireEvent('eventleave', this, el, event);
19591     },
19592     
19593     onEventClick: function (e, el,event,d) {
19594         this.fireEvent('eventclick', this, el, event);
19595     },
19596     
19597     onMonthChange: function () {
19598         this.store.load();
19599     },
19600     
19601     onMoreEventClick: function(e, el, more)
19602     {
19603         var _this = this;
19604         
19605         this.calpopover.placement = 'right';
19606         this.calpopover.setTitle('More');
19607         
19608         this.calpopover.setContent('');
19609         
19610         var ctr = this.calpopover.el.select('.popover-content', true).first();
19611         
19612         Roo.each(more, function(m){
19613             var cfg = {
19614                 cls : 'fc-event-hori fc-event-draggable',
19615                 html : m.title
19616             };
19617             var cg = ctr.createChild(cfg);
19618             
19619             cg.on('click', _this.onEventClick, _this, m);
19620         });
19621         
19622         this.calpopover.show(el);
19623         
19624         
19625     },
19626     
19627     onLoad: function () 
19628     {   
19629         this.calevents = [];
19630         var cal = this;
19631         
19632         if(this.store.getCount() > 0){
19633             this.store.data.each(function(d){
19634                cal.addItem({
19635                     id : d.data.id,
19636                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19637                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19638                     time : d.data.start_time,
19639                     title : d.data.title,
19640                     description : d.data.description,
19641                     venue : d.data.venue
19642                 });
19643             });
19644         }
19645         
19646         this.renderEvents();
19647         
19648         if(this.calevents.length && this.loadMask){
19649             this.maskEl.hide();
19650         }
19651     },
19652     
19653     onBeforeLoad: function()
19654     {
19655         this.clearEvents();
19656         if(this.loadMask){
19657             this.maskEl.show();
19658         }
19659     }
19660 });
19661
19662  
19663  /*
19664  * - LGPL
19665  *
19666  * element
19667  * 
19668  */
19669
19670 /**
19671  * @class Roo.bootstrap.Popover
19672  * @extends Roo.bootstrap.Component
19673  * Bootstrap Popover class
19674  * @cfg {String} html contents of the popover   (or false to use children..)
19675  * @cfg {String} title of popover (or false to hide)
19676  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19677  * @cfg {String} trigger click || hover (or false to trigger manually)
19678  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19679  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19680  *      - if false and it has a 'parent' then it will be automatically added to that element
19681  *      - if string - Roo.get  will be called 
19682  * @cfg {Number} delay - delay before showing
19683  
19684  * @constructor
19685  * Create a new Popover
19686  * @param {Object} config The config object
19687  */
19688
19689 Roo.bootstrap.Popover = function(config){
19690     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19691     
19692     this.addEvents({
19693         // raw events
19694          /**
19695          * @event show
19696          * After the popover show
19697          * 
19698          * @param {Roo.bootstrap.Popover} this
19699          */
19700         "show" : true,
19701         /**
19702          * @event hide
19703          * After the popover hide
19704          * 
19705          * @param {Roo.bootstrap.Popover} this
19706          */
19707         "hide" : true
19708     });
19709 };
19710
19711 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19712     
19713     title: false,
19714     html: false,
19715     
19716     placement : 'right',
19717     trigger : 'hover', // hover
19718     modal : false,
19719     delay : 0,
19720     
19721     over: false,
19722     
19723     can_build_overlaid : false,
19724     
19725     maskEl : false, // the mask element
19726     headerEl : false,
19727     contentEl : false,
19728     alignEl : false, // when show is called with an element - this get's stored.
19729     
19730     getChildContainer : function()
19731     {
19732         return this.contentEl;
19733         
19734     },
19735     getPopoverHeader : function()
19736     {
19737         this.title = true; // flag not to hide it..
19738         this.headerEl.addClass('p-0');
19739         return this.headerEl
19740     },
19741     
19742     
19743     getAutoCreate : function(){
19744          
19745         var cfg = {
19746            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19747            style: 'display:block',
19748            cn : [
19749                 {
19750                     cls : 'arrow'
19751                 },
19752                 {
19753                     cls : 'popover-inner ',
19754                     cn : [
19755                         {
19756                             tag: 'h3',
19757                             cls: 'popover-title popover-header',
19758                             html : this.title === false ? '' : this.title
19759                         },
19760                         {
19761                             cls : 'popover-content popover-body '  + (this.cls || ''),
19762                             html : this.html || ''
19763                         }
19764                     ]
19765                     
19766                 }
19767            ]
19768         };
19769         
19770         return cfg;
19771     },
19772     /**
19773      * @param {string} the title
19774      */
19775     setTitle: function(str)
19776     {
19777         this.title = str;
19778         if (this.el) {
19779             this.headerEl.dom.innerHTML = str;
19780         }
19781         
19782     },
19783     /**
19784      * @param {string} the body content
19785      */
19786     setContent: function(str)
19787     {
19788         this.html = str;
19789         if (this.contentEl) {
19790             this.contentEl.dom.innerHTML = str;
19791         }
19792         
19793     },
19794     // as it get's added to the bottom of the page.
19795     onRender : function(ct, position)
19796     {
19797         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19798         
19799         
19800         
19801         if(!this.el){
19802             var cfg = Roo.apply({},  this.getAutoCreate());
19803             cfg.id = Roo.id();
19804             
19805             if (this.cls) {
19806                 cfg.cls += ' ' + this.cls;
19807             }
19808             if (this.style) {
19809                 cfg.style = this.style;
19810             }
19811             //Roo.log("adding to ");
19812             this.el = Roo.get(document.body).createChild(cfg, position);
19813 //            Roo.log(this.el);
19814         }
19815         
19816         this.contentEl = this.el.select('.popover-content',true).first();
19817         this.headerEl =  this.el.select('.popover-title',true).first();
19818         
19819         var nitems = [];
19820         if(typeof(this.items) != 'undefined'){
19821             var items = this.items;
19822             delete this.items;
19823
19824             for(var i =0;i < items.length;i++) {
19825                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19826             }
19827         }
19828
19829         this.items = nitems;
19830         
19831         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19832         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19833         
19834         
19835         
19836         this.initEvents();
19837     },
19838     
19839     resizeMask : function()
19840     {
19841         this.maskEl.setSize(
19842             Roo.lib.Dom.getViewWidth(true),
19843             Roo.lib.Dom.getViewHeight(true)
19844         );
19845     },
19846     
19847     initEvents : function()
19848     {
19849         
19850         if (!this.modal) { 
19851             Roo.bootstrap.Popover.register(this);
19852         }
19853          
19854         this.arrowEl = this.el.select('.arrow',true).first();
19855         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19856         this.el.enableDisplayMode('block');
19857         this.el.hide();
19858  
19859         
19860         if (this.over === false && !this.parent()) {
19861             return; 
19862         }
19863         if (this.triggers === false) {
19864             return;
19865         }
19866          
19867         // support parent
19868         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19869         var triggers = this.trigger ? this.trigger.split(' ') : [];
19870         Roo.each(triggers, function(trigger) {
19871         
19872             if (trigger == 'click') {
19873                 on_el.on('click', this.toggle, this);
19874             } else if (trigger != 'manual') {
19875                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19876                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19877       
19878                 on_el.on(eventIn  ,this.enter, this);
19879                 on_el.on(eventOut, this.leave, this);
19880             }
19881         }, this);
19882     },
19883     
19884     
19885     // private
19886     timeout : null,
19887     hoverState : null,
19888     
19889     toggle : function () {
19890         this.hoverState == 'in' ? this.leave() : this.enter();
19891     },
19892     
19893     enter : function () {
19894         
19895         clearTimeout(this.timeout);
19896     
19897         this.hoverState = 'in';
19898     
19899         if (!this.delay || !this.delay.show) {
19900             this.show();
19901             return;
19902         }
19903         var _t = this;
19904         this.timeout = setTimeout(function () {
19905             if (_t.hoverState == 'in') {
19906                 _t.show();
19907             }
19908         }, this.delay.show)
19909     },
19910     
19911     leave : function() {
19912         clearTimeout(this.timeout);
19913     
19914         this.hoverState = 'out';
19915     
19916         if (!this.delay || !this.delay.hide) {
19917             this.hide();
19918             return;
19919         }
19920         var _t = this;
19921         this.timeout = setTimeout(function () {
19922             if (_t.hoverState == 'out') {
19923                 _t.hide();
19924             }
19925         }, this.delay.hide)
19926     },
19927     /**
19928      * Show the popover
19929      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19930      * @param {string} (left|right|top|bottom) position
19931      */
19932     show : function (on_el, placement)
19933     {
19934         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19935         on_el = on_el || false; // default to false
19936          
19937         if (!on_el) {
19938             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19939                 on_el = this.parent().el;
19940             } else if (this.over) {
19941                 Roo.get(this.over);
19942             }
19943             
19944         }
19945         
19946         this.alignEl = Roo.get( on_el );
19947
19948         if (!this.el) {
19949             this.render(document.body);
19950         }
19951         
19952         
19953          
19954         
19955         if (this.title === false) {
19956             this.headerEl.hide();
19957         }
19958         
19959        
19960         this.el.show();
19961         this.el.dom.style.display = 'block';
19962          
19963  
19964         if (this.alignEl) {
19965             this.updatePosition(this.placement, true);
19966              
19967         } else {
19968             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19969             var es = this.el.getSize();
19970             var x = Roo.lib.Dom.getViewWidth()/2;
19971             var y = Roo.lib.Dom.getViewHeight()/2;
19972             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19973             
19974         }
19975
19976         
19977         //var arrow = this.el.select('.arrow',true).first();
19978         //arrow.set(align[2], 
19979         
19980         this.el.addClass('in');
19981         
19982          
19983         
19984         this.hoverState = 'in';
19985         
19986         if (this.modal) {
19987             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19988             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19989             this.maskEl.dom.style.display = 'block';
19990             this.maskEl.addClass('show');
19991         }
19992         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19993  
19994         this.fireEvent('show', this);
19995         
19996     },
19997     /**
19998      * fire this manually after loading a grid in the table for example
19999      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20000      * @param {Boolean} try and move it if we cant get right position.
20001      */
20002     updatePosition : function(placement, try_move)
20003     {
20004         // allow for calling with no parameters
20005         placement = placement   ? placement :  this.placement;
20006         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20007         
20008         this.el.removeClass([
20009             'fade','top','bottom', 'left', 'right','in',
20010             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20011         ]);
20012         this.el.addClass(placement + ' bs-popover-' + placement);
20013         
20014         if (!this.alignEl ) {
20015             return false;
20016         }
20017         
20018         switch (placement) {
20019             case 'right':
20020                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20021                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20022                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20023                     //normal display... or moved up/down.
20024                     this.el.setXY(offset);
20025                     var xy = this.alignEl.getAnchorXY('tr', false);
20026                     xy[0]+=2;xy[1]+=5;
20027                     this.arrowEl.setXY(xy);
20028                     return true;
20029                 }
20030                 // continue through...
20031                 return this.updatePosition('left', false);
20032                 
20033             
20034             case 'left':
20035                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20036                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20037                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20038                     //normal display... or moved up/down.
20039                     this.el.setXY(offset);
20040                     var xy = this.alignEl.getAnchorXY('tl', false);
20041                     xy[0]-=10;xy[1]+=5; // << fix me
20042                     this.arrowEl.setXY(xy);
20043                     return true;
20044                 }
20045                 // call self...
20046                 return this.updatePosition('right', false);
20047             
20048             case 'top':
20049                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20050                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20051                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20052                     //normal display... or moved up/down.
20053                     this.el.setXY(offset);
20054                     var xy = this.alignEl.getAnchorXY('t', false);
20055                     xy[1]-=10; // << fix me
20056                     this.arrowEl.setXY(xy);
20057                     return true;
20058                 }
20059                 // fall through
20060                return this.updatePosition('bottom', false);
20061             
20062             case 'bottom':
20063                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20064                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20065                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20066                     //normal display... or moved up/down.
20067                     this.el.setXY(offset);
20068                     var xy = this.alignEl.getAnchorXY('b', false);
20069                      xy[1]+=2; // << fix me
20070                     this.arrowEl.setXY(xy);
20071                     return true;
20072                 }
20073                 // fall through
20074                 return this.updatePosition('top', false);
20075                 
20076             
20077         }
20078         
20079         
20080         return false;
20081     },
20082     
20083     hide : function()
20084     {
20085         this.el.setXY([0,0]);
20086         this.el.removeClass('in');
20087         this.el.hide();
20088         this.hoverState = null;
20089         this.maskEl.hide(); // always..
20090         this.fireEvent('hide', this);
20091     }
20092     
20093 });
20094
20095
20096 Roo.apply(Roo.bootstrap.Popover, {
20097
20098     alignment : {
20099         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20100         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20101         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20102         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20103     },
20104     
20105     zIndex : 20001,
20106
20107     clickHander : false,
20108     
20109
20110     onMouseDown : function(e)
20111     {
20112         if (!e.getTarget(".roo-popover")) {
20113             this.hideAll();
20114         }
20115          
20116     },
20117     
20118     popups : [],
20119     
20120     register : function(popup)
20121     {
20122         if (!Roo.bootstrap.Popover.clickHandler) {
20123             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20124         }
20125         // hide other popups.
20126         this.hideAll();
20127         this.popups.push(popup);
20128     },
20129     hideAll : function()
20130     {
20131         this.popups.forEach(function(p) {
20132             p.hide();
20133         });
20134     }
20135
20136 });/*
20137  * - LGPL
20138  *
20139  * Card header - holder for the card header elements.
20140  * 
20141  */
20142
20143 /**
20144  * @class Roo.bootstrap.PopoverNav
20145  * @extends Roo.bootstrap.NavGroup
20146  * Bootstrap Popover header navigation class
20147  * @constructor
20148  * Create a new Popover Header Navigation 
20149  * @param {Object} config The config object
20150  */
20151
20152 Roo.bootstrap.PopoverNav = function(config){
20153     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20154 };
20155
20156 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20157     
20158     
20159     container_method : 'getPopoverHeader' 
20160     
20161      
20162     
20163     
20164    
20165 });
20166
20167  
20168
20169  /*
20170  * - LGPL
20171  *
20172  * Progress
20173  * 
20174  */
20175
20176 /**
20177  * @class Roo.bootstrap.Progress
20178  * @extends Roo.bootstrap.Component
20179  * Bootstrap Progress class
20180  * @cfg {Boolean} striped striped of the progress bar
20181  * @cfg {Boolean} active animated of the progress bar
20182  * 
20183  * 
20184  * @constructor
20185  * Create a new Progress
20186  * @param {Object} config The config object
20187  */
20188
20189 Roo.bootstrap.Progress = function(config){
20190     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20191 };
20192
20193 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20194     
20195     striped : false,
20196     active: false,
20197     
20198     getAutoCreate : function(){
20199         var cfg = {
20200             tag: 'div',
20201             cls: 'progress'
20202         };
20203         
20204         
20205         if(this.striped){
20206             cfg.cls += ' progress-striped';
20207         }
20208       
20209         if(this.active){
20210             cfg.cls += ' active';
20211         }
20212         
20213         
20214         return cfg;
20215     }
20216    
20217 });
20218
20219  
20220
20221  /*
20222  * - LGPL
20223  *
20224  * ProgressBar
20225  * 
20226  */
20227
20228 /**
20229  * @class Roo.bootstrap.ProgressBar
20230  * @extends Roo.bootstrap.Component
20231  * Bootstrap ProgressBar class
20232  * @cfg {Number} aria_valuenow aria-value now
20233  * @cfg {Number} aria_valuemin aria-value min
20234  * @cfg {Number} aria_valuemax aria-value max
20235  * @cfg {String} label label for the progress bar
20236  * @cfg {String} panel (success | info | warning | danger )
20237  * @cfg {String} role role of the progress bar
20238  * @cfg {String} sr_only text
20239  * 
20240  * 
20241  * @constructor
20242  * Create a new ProgressBar
20243  * @param {Object} config The config object
20244  */
20245
20246 Roo.bootstrap.ProgressBar = function(config){
20247     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20248 };
20249
20250 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20251     
20252     aria_valuenow : 0,
20253     aria_valuemin : 0,
20254     aria_valuemax : 100,
20255     label : false,
20256     panel : false,
20257     role : false,
20258     sr_only: false,
20259     
20260     getAutoCreate : function()
20261     {
20262         
20263         var cfg = {
20264             tag: 'div',
20265             cls: 'progress-bar',
20266             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20267         };
20268         
20269         if(this.sr_only){
20270             cfg.cn = {
20271                 tag: 'span',
20272                 cls: 'sr-only',
20273                 html: this.sr_only
20274             }
20275         }
20276         
20277         if(this.role){
20278             cfg.role = this.role;
20279         }
20280         
20281         if(this.aria_valuenow){
20282             cfg['aria-valuenow'] = this.aria_valuenow;
20283         }
20284         
20285         if(this.aria_valuemin){
20286             cfg['aria-valuemin'] = this.aria_valuemin;
20287         }
20288         
20289         if(this.aria_valuemax){
20290             cfg['aria-valuemax'] = this.aria_valuemax;
20291         }
20292         
20293         if(this.label && !this.sr_only){
20294             cfg.html = this.label;
20295         }
20296         
20297         if(this.panel){
20298             cfg.cls += ' progress-bar-' + this.panel;
20299         }
20300         
20301         return cfg;
20302     },
20303     
20304     update : function(aria_valuenow)
20305     {
20306         this.aria_valuenow = aria_valuenow;
20307         
20308         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20309     }
20310    
20311 });
20312
20313  
20314
20315  /*
20316  * - LGPL
20317  *
20318  * column
20319  * 
20320  */
20321
20322 /**
20323  * @class Roo.bootstrap.TabGroup
20324  * @extends Roo.bootstrap.Column
20325  * Bootstrap Column class
20326  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20327  * @cfg {Boolean} carousel true to make the group behave like a carousel
20328  * @cfg {Boolean} bullets show bullets for the panels
20329  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20330  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20331  * @cfg {Boolean} showarrow (true|false) show arrow default true
20332  * 
20333  * @constructor
20334  * Create a new TabGroup
20335  * @param {Object} config The config object
20336  */
20337
20338 Roo.bootstrap.TabGroup = function(config){
20339     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20340     if (!this.navId) {
20341         this.navId = Roo.id();
20342     }
20343     this.tabs = [];
20344     Roo.bootstrap.TabGroup.register(this);
20345     
20346 };
20347
20348 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20349     
20350     carousel : false,
20351     transition : false,
20352     bullets : 0,
20353     timer : 0,
20354     autoslide : false,
20355     slideFn : false,
20356     slideOnTouch : false,
20357     showarrow : true,
20358     
20359     getAutoCreate : function()
20360     {
20361         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20362         
20363         cfg.cls += ' tab-content';
20364         
20365         if (this.carousel) {
20366             cfg.cls += ' carousel slide';
20367             
20368             cfg.cn = [{
20369                cls : 'carousel-inner',
20370                cn : []
20371             }];
20372         
20373             if(this.bullets  && !Roo.isTouch){
20374                 
20375                 var bullets = {
20376                     cls : 'carousel-bullets',
20377                     cn : []
20378                 };
20379                
20380                 if(this.bullets_cls){
20381                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20382                 }
20383                 
20384                 bullets.cn.push({
20385                     cls : 'clear'
20386                 });
20387                 
20388                 cfg.cn[0].cn.push(bullets);
20389             }
20390             
20391             if(this.showarrow){
20392                 cfg.cn[0].cn.push({
20393                     tag : 'div',
20394                     class : 'carousel-arrow',
20395                     cn : [
20396                         {
20397                             tag : 'div',
20398                             class : 'carousel-prev',
20399                             cn : [
20400                                 {
20401                                     tag : 'i',
20402                                     class : 'fa fa-chevron-left'
20403                                 }
20404                             ]
20405                         },
20406                         {
20407                             tag : 'div',
20408                             class : 'carousel-next',
20409                             cn : [
20410                                 {
20411                                     tag : 'i',
20412                                     class : 'fa fa-chevron-right'
20413                                 }
20414                             ]
20415                         }
20416                     ]
20417                 });
20418             }
20419             
20420         }
20421         
20422         return cfg;
20423     },
20424     
20425     initEvents:  function()
20426     {
20427 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20428 //            this.el.on("touchstart", this.onTouchStart, this);
20429 //        }
20430         
20431         if(this.autoslide){
20432             var _this = this;
20433             
20434             this.slideFn = window.setInterval(function() {
20435                 _this.showPanelNext();
20436             }, this.timer);
20437         }
20438         
20439         if(this.showarrow){
20440             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20441             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20442         }
20443         
20444         
20445     },
20446     
20447 //    onTouchStart : function(e, el, o)
20448 //    {
20449 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20450 //            return;
20451 //        }
20452 //        
20453 //        this.showPanelNext();
20454 //    },
20455     
20456     
20457     getChildContainer : function()
20458     {
20459         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20460     },
20461     
20462     /**
20463     * register a Navigation item
20464     * @param {Roo.bootstrap.NavItem} the navitem to add
20465     */
20466     register : function(item)
20467     {
20468         this.tabs.push( item);
20469         item.navId = this.navId; // not really needed..
20470         this.addBullet();
20471     
20472     },
20473     
20474     getActivePanel : function()
20475     {
20476         var r = false;
20477         Roo.each(this.tabs, function(t) {
20478             if (t.active) {
20479                 r = t;
20480                 return false;
20481             }
20482             return null;
20483         });
20484         return r;
20485         
20486     },
20487     getPanelByName : function(n)
20488     {
20489         var r = false;
20490         Roo.each(this.tabs, function(t) {
20491             if (t.tabId == n) {
20492                 r = t;
20493                 return false;
20494             }
20495             return null;
20496         });
20497         return r;
20498     },
20499     indexOfPanel : function(p)
20500     {
20501         var r = false;
20502         Roo.each(this.tabs, function(t,i) {
20503             if (t.tabId == p.tabId) {
20504                 r = i;
20505                 return false;
20506             }
20507             return null;
20508         });
20509         return r;
20510     },
20511     /**
20512      * show a specific panel
20513      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20514      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20515      */
20516     showPanel : function (pan)
20517     {
20518         if(this.transition || typeof(pan) == 'undefined'){
20519             Roo.log("waiting for the transitionend");
20520             return false;
20521         }
20522         
20523         if (typeof(pan) == 'number') {
20524             pan = this.tabs[pan];
20525         }
20526         
20527         if (typeof(pan) == 'string') {
20528             pan = this.getPanelByName(pan);
20529         }
20530         
20531         var cur = this.getActivePanel();
20532         
20533         if(!pan || !cur){
20534             Roo.log('pan or acitve pan is undefined');
20535             return false;
20536         }
20537         
20538         if (pan.tabId == this.getActivePanel().tabId) {
20539             return true;
20540         }
20541         
20542         if (false === cur.fireEvent('beforedeactivate')) {
20543             return false;
20544         }
20545         
20546         if(this.bullets > 0 && !Roo.isTouch){
20547             this.setActiveBullet(this.indexOfPanel(pan));
20548         }
20549         
20550         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20551             
20552             //class="carousel-item carousel-item-next carousel-item-left"
20553             
20554             this.transition = true;
20555             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20556             var lr = dir == 'next' ? 'left' : 'right';
20557             pan.el.addClass(dir); // or prev
20558             pan.el.addClass('carousel-item-' + dir); // or prev
20559             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20560             cur.el.addClass(lr); // or right
20561             pan.el.addClass(lr);
20562             cur.el.addClass('carousel-item-' +lr); // or right
20563             pan.el.addClass('carousel-item-' +lr);
20564             
20565             
20566             var _this = this;
20567             cur.el.on('transitionend', function() {
20568                 Roo.log("trans end?");
20569                 
20570                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20571                 pan.setActive(true);
20572                 
20573                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20574                 cur.setActive(false);
20575                 
20576                 _this.transition = false;
20577                 
20578             }, this, { single:  true } );
20579             
20580             return true;
20581         }
20582         
20583         cur.setActive(false);
20584         pan.setActive(true);
20585         
20586         return true;
20587         
20588     },
20589     showPanelNext : function()
20590     {
20591         var i = this.indexOfPanel(this.getActivePanel());
20592         
20593         if (i >= this.tabs.length - 1 && !this.autoslide) {
20594             return;
20595         }
20596         
20597         if (i >= this.tabs.length - 1 && this.autoslide) {
20598             i = -1;
20599         }
20600         
20601         this.showPanel(this.tabs[i+1]);
20602     },
20603     
20604     showPanelPrev : function()
20605     {
20606         var i = this.indexOfPanel(this.getActivePanel());
20607         
20608         if (i  < 1 && !this.autoslide) {
20609             return;
20610         }
20611         
20612         if (i < 1 && this.autoslide) {
20613             i = this.tabs.length;
20614         }
20615         
20616         this.showPanel(this.tabs[i-1]);
20617     },
20618     
20619     
20620     addBullet: function()
20621     {
20622         if(!this.bullets || Roo.isTouch){
20623             return;
20624         }
20625         var ctr = this.el.select('.carousel-bullets',true).first();
20626         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20627         var bullet = ctr.createChild({
20628             cls : 'bullet bullet-' + i
20629         },ctr.dom.lastChild);
20630         
20631         
20632         var _this = this;
20633         
20634         bullet.on('click', (function(e, el, o, ii, t){
20635
20636             e.preventDefault();
20637
20638             this.showPanel(ii);
20639
20640             if(this.autoslide && this.slideFn){
20641                 clearInterval(this.slideFn);
20642                 this.slideFn = window.setInterval(function() {
20643                     _this.showPanelNext();
20644                 }, this.timer);
20645             }
20646
20647         }).createDelegate(this, [i, bullet], true));
20648                 
20649         
20650     },
20651      
20652     setActiveBullet : function(i)
20653     {
20654         if(Roo.isTouch){
20655             return;
20656         }
20657         
20658         Roo.each(this.el.select('.bullet', true).elements, function(el){
20659             el.removeClass('selected');
20660         });
20661
20662         var bullet = this.el.select('.bullet-' + i, true).first();
20663         
20664         if(!bullet){
20665             return;
20666         }
20667         
20668         bullet.addClass('selected');
20669     }
20670     
20671     
20672   
20673 });
20674
20675  
20676
20677  
20678  
20679 Roo.apply(Roo.bootstrap.TabGroup, {
20680     
20681     groups: {},
20682      /**
20683     * register a Navigation Group
20684     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20685     */
20686     register : function(navgrp)
20687     {
20688         this.groups[navgrp.navId] = navgrp;
20689         
20690     },
20691     /**
20692     * fetch a Navigation Group based on the navigation ID
20693     * if one does not exist , it will get created.
20694     * @param {string} the navgroup to add
20695     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20696     */
20697     get: function(navId) {
20698         if (typeof(this.groups[navId]) == 'undefined') {
20699             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20700         }
20701         return this.groups[navId] ;
20702     }
20703     
20704     
20705     
20706 });
20707
20708  /*
20709  * - LGPL
20710  *
20711  * TabPanel
20712  * 
20713  */
20714
20715 /**
20716  * @class Roo.bootstrap.TabPanel
20717  * @extends Roo.bootstrap.Component
20718  * Bootstrap TabPanel class
20719  * @cfg {Boolean} active panel active
20720  * @cfg {String} html panel content
20721  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20722  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20723  * @cfg {String} href click to link..
20724  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20725  * 
20726  * 
20727  * @constructor
20728  * Create a new TabPanel
20729  * @param {Object} config The config object
20730  */
20731
20732 Roo.bootstrap.TabPanel = function(config){
20733     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20734     this.addEvents({
20735         /**
20736              * @event changed
20737              * Fires when the active status changes
20738              * @param {Roo.bootstrap.TabPanel} this
20739              * @param {Boolean} state the new state
20740             
20741          */
20742         'changed': true,
20743         /**
20744              * @event beforedeactivate
20745              * Fires before a tab is de-activated - can be used to do validation on a form.
20746              * @param {Roo.bootstrap.TabPanel} this
20747              * @return {Boolean} false if there is an error
20748             
20749          */
20750         'beforedeactivate': true
20751      });
20752     
20753     this.tabId = this.tabId || Roo.id();
20754   
20755 };
20756
20757 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20758     
20759     active: false,
20760     html: false,
20761     tabId: false,
20762     navId : false,
20763     href : '',
20764     touchSlide : false,
20765     getAutoCreate : function(){
20766         
20767         
20768         var cfg = {
20769             tag: 'div',
20770             // item is needed for carousel - not sure if it has any effect otherwise
20771             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20772             html: this.html || ''
20773         };
20774         
20775         if(this.active){
20776             cfg.cls += ' active';
20777         }
20778         
20779         if(this.tabId){
20780             cfg.tabId = this.tabId;
20781         }
20782         
20783         
20784         
20785         return cfg;
20786     },
20787     
20788     initEvents:  function()
20789     {
20790         var p = this.parent();
20791         
20792         this.navId = this.navId || p.navId;
20793         
20794         if (typeof(this.navId) != 'undefined') {
20795             // not really needed.. but just in case.. parent should be a NavGroup.
20796             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20797             
20798             tg.register(this);
20799             
20800             var i = tg.tabs.length - 1;
20801             
20802             if(this.active && tg.bullets > 0 && i < tg.bullets){
20803                 tg.setActiveBullet(i);
20804             }
20805         }
20806         
20807         this.el.on('click', this.onClick, this);
20808         
20809         if(Roo.isTouch && this.touchSlide){
20810             this.el.on("touchstart", this.onTouchStart, this);
20811             this.el.on("touchmove", this.onTouchMove, this);
20812             this.el.on("touchend", this.onTouchEnd, this);
20813         }
20814         
20815     },
20816     
20817     onRender : function(ct, position)
20818     {
20819         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20820     },
20821     
20822     setActive : function(state)
20823     {
20824         Roo.log("panel - set active " + this.tabId + "=" + state);
20825         
20826         this.active = state;
20827         if (!state) {
20828             this.el.removeClass('active');
20829             
20830         } else  if (!this.el.hasClass('active')) {
20831             this.el.addClass('active');
20832         }
20833         
20834         this.fireEvent('changed', this, state);
20835     },
20836     
20837     onClick : function(e)
20838     {
20839         e.preventDefault();
20840         
20841         if(!this.href.length){
20842             return;
20843         }
20844         
20845         window.location.href = this.href;
20846     },
20847     
20848     startX : 0,
20849     startY : 0,
20850     endX : 0,
20851     endY : 0,
20852     swiping : false,
20853     
20854     onTouchStart : function(e)
20855     {
20856         this.swiping = false;
20857         
20858         this.startX = e.browserEvent.touches[0].clientX;
20859         this.startY = e.browserEvent.touches[0].clientY;
20860     },
20861     
20862     onTouchMove : function(e)
20863     {
20864         this.swiping = true;
20865         
20866         this.endX = e.browserEvent.touches[0].clientX;
20867         this.endY = e.browserEvent.touches[0].clientY;
20868     },
20869     
20870     onTouchEnd : function(e)
20871     {
20872         if(!this.swiping){
20873             this.onClick(e);
20874             return;
20875         }
20876         
20877         var tabGroup = this.parent();
20878         
20879         if(this.endX > this.startX){ // swiping right
20880             tabGroup.showPanelPrev();
20881             return;
20882         }
20883         
20884         if(this.startX > this.endX){ // swiping left
20885             tabGroup.showPanelNext();
20886             return;
20887         }
20888     }
20889     
20890     
20891 });
20892  
20893
20894  
20895
20896  /*
20897  * - LGPL
20898  *
20899  * DateField
20900  * 
20901  */
20902
20903 /**
20904  * @class Roo.bootstrap.DateField
20905  * @extends Roo.bootstrap.Input
20906  * Bootstrap DateField class
20907  * @cfg {Number} weekStart default 0
20908  * @cfg {String} viewMode default empty, (months|years)
20909  * @cfg {String} minViewMode default empty, (months|years)
20910  * @cfg {Number} startDate default -Infinity
20911  * @cfg {Number} endDate default Infinity
20912  * @cfg {Boolean} todayHighlight default false
20913  * @cfg {Boolean} todayBtn default false
20914  * @cfg {Boolean} calendarWeeks default false
20915  * @cfg {Object} daysOfWeekDisabled default empty
20916  * @cfg {Boolean} singleMode default false (true | false)
20917  * 
20918  * @cfg {Boolean} keyboardNavigation default true
20919  * @cfg {String} language default en
20920  * 
20921  * @constructor
20922  * Create a new DateField
20923  * @param {Object} config The config object
20924  */
20925
20926 Roo.bootstrap.DateField = function(config){
20927     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20928      this.addEvents({
20929             /**
20930              * @event show
20931              * Fires when this field show.
20932              * @param {Roo.bootstrap.DateField} this
20933              * @param {Mixed} date The date value
20934              */
20935             show : true,
20936             /**
20937              * @event show
20938              * Fires when this field hide.
20939              * @param {Roo.bootstrap.DateField} this
20940              * @param {Mixed} date The date value
20941              */
20942             hide : true,
20943             /**
20944              * @event select
20945              * Fires when select a date.
20946              * @param {Roo.bootstrap.DateField} this
20947              * @param {Mixed} date The date value
20948              */
20949             select : true,
20950             /**
20951              * @event beforeselect
20952              * Fires when before select a date.
20953              * @param {Roo.bootstrap.DateField} this
20954              * @param {Mixed} date The date value
20955              */
20956             beforeselect : true
20957         });
20958 };
20959
20960 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20961     
20962     /**
20963      * @cfg {String} format
20964      * The default date format string which can be overriden for localization support.  The format must be
20965      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20966      */
20967     format : "m/d/y",
20968     /**
20969      * @cfg {String} altFormats
20970      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20971      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20972      */
20973     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20974     
20975     weekStart : 0,
20976     
20977     viewMode : '',
20978     
20979     minViewMode : '',
20980     
20981     todayHighlight : false,
20982     
20983     todayBtn: false,
20984     
20985     language: 'en',
20986     
20987     keyboardNavigation: true,
20988     
20989     calendarWeeks: false,
20990     
20991     startDate: -Infinity,
20992     
20993     endDate: Infinity,
20994     
20995     daysOfWeekDisabled: [],
20996     
20997     _events: [],
20998     
20999     singleMode : false,
21000     
21001     UTCDate: function()
21002     {
21003         return new Date(Date.UTC.apply(Date, arguments));
21004     },
21005     
21006     UTCToday: function()
21007     {
21008         var today = new Date();
21009         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21010     },
21011     
21012     getDate: function() {
21013             var d = this.getUTCDate();
21014             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21015     },
21016     
21017     getUTCDate: function() {
21018             return this.date;
21019     },
21020     
21021     setDate: function(d) {
21022             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21023     },
21024     
21025     setUTCDate: function(d) {
21026             this.date = d;
21027             this.setValue(this.formatDate(this.date));
21028     },
21029         
21030     onRender: function(ct, position)
21031     {
21032         
21033         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21034         
21035         this.language = this.language || 'en';
21036         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21037         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21038         
21039         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21040         this.format = this.format || 'm/d/y';
21041         this.isInline = false;
21042         this.isInput = true;
21043         this.component = this.el.select('.add-on', true).first() || false;
21044         this.component = (this.component && this.component.length === 0) ? false : this.component;
21045         this.hasInput = this.component && this.inputEl().length;
21046         
21047         if (typeof(this.minViewMode === 'string')) {
21048             switch (this.minViewMode) {
21049                 case 'months':
21050                     this.minViewMode = 1;
21051                     break;
21052                 case 'years':
21053                     this.minViewMode = 2;
21054                     break;
21055                 default:
21056                     this.minViewMode = 0;
21057                     break;
21058             }
21059         }
21060         
21061         if (typeof(this.viewMode === 'string')) {
21062             switch (this.viewMode) {
21063                 case 'months':
21064                     this.viewMode = 1;
21065                     break;
21066                 case 'years':
21067                     this.viewMode = 2;
21068                     break;
21069                 default:
21070                     this.viewMode = 0;
21071                     break;
21072             }
21073         }
21074                 
21075         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21076         
21077 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21078         
21079         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21080         
21081         this.picker().on('mousedown', this.onMousedown, this);
21082         this.picker().on('click', this.onClick, this);
21083         
21084         this.picker().addClass('datepicker-dropdown');
21085         
21086         this.startViewMode = this.viewMode;
21087         
21088         if(this.singleMode){
21089             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21090                 v.setVisibilityMode(Roo.Element.DISPLAY);
21091                 v.hide();
21092             });
21093             
21094             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21095                 v.setStyle('width', '189px');
21096             });
21097         }
21098         
21099         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21100             if(!this.calendarWeeks){
21101                 v.remove();
21102                 return;
21103             }
21104             
21105             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21106             v.attr('colspan', function(i, val){
21107                 return parseInt(val) + 1;
21108             });
21109         });
21110                         
21111         
21112         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21113         
21114         this.setStartDate(this.startDate);
21115         this.setEndDate(this.endDate);
21116         
21117         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21118         
21119         this.fillDow();
21120         this.fillMonths();
21121         this.update();
21122         this.showMode();
21123         
21124         if(this.isInline) {
21125             this.showPopup();
21126         }
21127     },
21128     
21129     picker : function()
21130     {
21131         return this.pickerEl;
21132 //        return this.el.select('.datepicker', true).first();
21133     },
21134     
21135     fillDow: function()
21136     {
21137         var dowCnt = this.weekStart;
21138         
21139         var dow = {
21140             tag: 'tr',
21141             cn: [
21142                 
21143             ]
21144         };
21145         
21146         if(this.calendarWeeks){
21147             dow.cn.push({
21148                 tag: 'th',
21149                 cls: 'cw',
21150                 html: '&nbsp;'
21151             })
21152         }
21153         
21154         while (dowCnt < this.weekStart + 7) {
21155             dow.cn.push({
21156                 tag: 'th',
21157                 cls: 'dow',
21158                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21159             });
21160         }
21161         
21162         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21163     },
21164     
21165     fillMonths: function()
21166     {    
21167         var i = 0;
21168         var months = this.picker().select('>.datepicker-months td', true).first();
21169         
21170         months.dom.innerHTML = '';
21171         
21172         while (i < 12) {
21173             var month = {
21174                 tag: 'span',
21175                 cls: 'month',
21176                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21177             };
21178             
21179             months.createChild(month);
21180         }
21181         
21182     },
21183     
21184     update: function()
21185     {
21186         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;
21187         
21188         if (this.date < this.startDate) {
21189             this.viewDate = new Date(this.startDate);
21190         } else if (this.date > this.endDate) {
21191             this.viewDate = new Date(this.endDate);
21192         } else {
21193             this.viewDate = new Date(this.date);
21194         }
21195         
21196         this.fill();
21197     },
21198     
21199     fill: function() 
21200     {
21201         var d = new Date(this.viewDate),
21202                 year = d.getUTCFullYear(),
21203                 month = d.getUTCMonth(),
21204                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21205                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21206                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21207                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21208                 currentDate = this.date && this.date.valueOf(),
21209                 today = this.UTCToday();
21210         
21211         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21212         
21213 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21214         
21215 //        this.picker.select('>tfoot th.today').
21216 //                                              .text(dates[this.language].today)
21217 //                                              .toggle(this.todayBtn !== false);
21218     
21219         this.updateNavArrows();
21220         this.fillMonths();
21221                                                 
21222         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21223         
21224         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21225          
21226         prevMonth.setUTCDate(day);
21227         
21228         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21229         
21230         var nextMonth = new Date(prevMonth);
21231         
21232         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21233         
21234         nextMonth = nextMonth.valueOf();
21235         
21236         var fillMonths = false;
21237         
21238         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21239         
21240         while(prevMonth.valueOf() <= nextMonth) {
21241             var clsName = '';
21242             
21243             if (prevMonth.getUTCDay() === this.weekStart) {
21244                 if(fillMonths){
21245                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21246                 }
21247                     
21248                 fillMonths = {
21249                     tag: 'tr',
21250                     cn: []
21251                 };
21252                 
21253                 if(this.calendarWeeks){
21254                     // ISO 8601: First week contains first thursday.
21255                     // ISO also states week starts on Monday, but we can be more abstract here.
21256                     var
21257                     // Start of current week: based on weekstart/current date
21258                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21259                     // Thursday of this week
21260                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21261                     // First Thursday of year, year from thursday
21262                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21263                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21264                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21265                     
21266                     fillMonths.cn.push({
21267                         tag: 'td',
21268                         cls: 'cw',
21269                         html: calWeek
21270                     });
21271                 }
21272             }
21273             
21274             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21275                 clsName += ' old';
21276             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21277                 clsName += ' new';
21278             }
21279             if (this.todayHighlight &&
21280                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21281                 prevMonth.getUTCMonth() == today.getMonth() &&
21282                 prevMonth.getUTCDate() == today.getDate()) {
21283                 clsName += ' today';
21284             }
21285             
21286             if (currentDate && prevMonth.valueOf() === currentDate) {
21287                 clsName += ' active';
21288             }
21289             
21290             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21291                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21292                     clsName += ' disabled';
21293             }
21294             
21295             fillMonths.cn.push({
21296                 tag: 'td',
21297                 cls: 'day ' + clsName,
21298                 html: prevMonth.getDate()
21299             });
21300             
21301             prevMonth.setDate(prevMonth.getDate()+1);
21302         }
21303           
21304         var currentYear = this.date && this.date.getUTCFullYear();
21305         var currentMonth = this.date && this.date.getUTCMonth();
21306         
21307         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21308         
21309         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21310             v.removeClass('active');
21311             
21312             if(currentYear === year && k === currentMonth){
21313                 v.addClass('active');
21314             }
21315             
21316             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21317                 v.addClass('disabled');
21318             }
21319             
21320         });
21321         
21322         
21323         year = parseInt(year/10, 10) * 10;
21324         
21325         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21326         
21327         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21328         
21329         year -= 1;
21330         for (var i = -1; i < 11; i++) {
21331             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21332                 tag: 'span',
21333                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21334                 html: year
21335             });
21336             
21337             year += 1;
21338         }
21339     },
21340     
21341     showMode: function(dir) 
21342     {
21343         if (dir) {
21344             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21345         }
21346         
21347         Roo.each(this.picker().select('>div',true).elements, function(v){
21348             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21349             v.hide();
21350         });
21351         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21352     },
21353     
21354     place: function()
21355     {
21356         if(this.isInline) {
21357             return;
21358         }
21359         
21360         this.picker().removeClass(['bottom', 'top']);
21361         
21362         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21363             /*
21364              * place to the top of element!
21365              *
21366              */
21367             
21368             this.picker().addClass('top');
21369             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21370             
21371             return;
21372         }
21373         
21374         this.picker().addClass('bottom');
21375         
21376         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21377     },
21378     
21379     parseDate : function(value)
21380     {
21381         if(!value || value instanceof Date){
21382             return value;
21383         }
21384         var v = Date.parseDate(value, this.format);
21385         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21386             v = Date.parseDate(value, 'Y-m-d');
21387         }
21388         if(!v && this.altFormats){
21389             if(!this.altFormatsArray){
21390                 this.altFormatsArray = this.altFormats.split("|");
21391             }
21392             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21393                 v = Date.parseDate(value, this.altFormatsArray[i]);
21394             }
21395         }
21396         return v;
21397     },
21398     
21399     formatDate : function(date, fmt)
21400     {   
21401         return (!date || !(date instanceof Date)) ?
21402         date : date.dateFormat(fmt || this.format);
21403     },
21404     
21405     onFocus : function()
21406     {
21407         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21408         this.showPopup();
21409     },
21410     
21411     onBlur : function()
21412     {
21413         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21414         
21415         var d = this.inputEl().getValue();
21416         
21417         this.setValue(d);
21418                 
21419         this.hidePopup();
21420     },
21421     
21422     showPopup : function()
21423     {
21424         this.picker().show();
21425         this.update();
21426         this.place();
21427         
21428         this.fireEvent('showpopup', this, this.date);
21429     },
21430     
21431     hidePopup : function()
21432     {
21433         if(this.isInline) {
21434             return;
21435         }
21436         this.picker().hide();
21437         this.viewMode = this.startViewMode;
21438         this.showMode();
21439         
21440         this.fireEvent('hidepopup', this, this.date);
21441         
21442     },
21443     
21444     onMousedown: function(e)
21445     {
21446         e.stopPropagation();
21447         e.preventDefault();
21448     },
21449     
21450     keyup: function(e)
21451     {
21452         Roo.bootstrap.DateField.superclass.keyup.call(this);
21453         this.update();
21454     },
21455
21456     setValue: function(v)
21457     {
21458         if(this.fireEvent('beforeselect', this, v) !== false){
21459             var d = new Date(this.parseDate(v) ).clearTime();
21460         
21461             if(isNaN(d.getTime())){
21462                 this.date = this.viewDate = '';
21463                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21464                 return;
21465             }
21466
21467             v = this.formatDate(d);
21468
21469             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21470
21471             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21472
21473             this.update();
21474
21475             this.fireEvent('select', this, this.date);
21476         }
21477     },
21478     
21479     getValue: function()
21480     {
21481         return this.formatDate(this.date);
21482     },
21483     
21484     fireKey: function(e)
21485     {
21486         if (!this.picker().isVisible()){
21487             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21488                 this.showPopup();
21489             }
21490             return;
21491         }
21492         
21493         var dateChanged = false,
21494         dir, day, month,
21495         newDate, newViewDate;
21496         
21497         switch(e.keyCode){
21498             case 27: // escape
21499                 this.hidePopup();
21500                 e.preventDefault();
21501                 break;
21502             case 37: // left
21503             case 39: // right
21504                 if (!this.keyboardNavigation) {
21505                     break;
21506                 }
21507                 dir = e.keyCode == 37 ? -1 : 1;
21508                 
21509                 if (e.ctrlKey){
21510                     newDate = this.moveYear(this.date, dir);
21511                     newViewDate = this.moveYear(this.viewDate, dir);
21512                 } else if (e.shiftKey){
21513                     newDate = this.moveMonth(this.date, dir);
21514                     newViewDate = this.moveMonth(this.viewDate, dir);
21515                 } else {
21516                     newDate = new Date(this.date);
21517                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21518                     newViewDate = new Date(this.viewDate);
21519                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21520                 }
21521                 if (this.dateWithinRange(newDate)){
21522                     this.date = newDate;
21523                     this.viewDate = newViewDate;
21524                     this.setValue(this.formatDate(this.date));
21525 //                    this.update();
21526                     e.preventDefault();
21527                     dateChanged = true;
21528                 }
21529                 break;
21530             case 38: // up
21531             case 40: // down
21532                 if (!this.keyboardNavigation) {
21533                     break;
21534                 }
21535                 dir = e.keyCode == 38 ? -1 : 1;
21536                 if (e.ctrlKey){
21537                     newDate = this.moveYear(this.date, dir);
21538                     newViewDate = this.moveYear(this.viewDate, dir);
21539                 } else if (e.shiftKey){
21540                     newDate = this.moveMonth(this.date, dir);
21541                     newViewDate = this.moveMonth(this.viewDate, dir);
21542                 } else {
21543                     newDate = new Date(this.date);
21544                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21545                     newViewDate = new Date(this.viewDate);
21546                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21547                 }
21548                 if (this.dateWithinRange(newDate)){
21549                     this.date = newDate;
21550                     this.viewDate = newViewDate;
21551                     this.setValue(this.formatDate(this.date));
21552 //                    this.update();
21553                     e.preventDefault();
21554                     dateChanged = true;
21555                 }
21556                 break;
21557             case 13: // enter
21558                 this.setValue(this.formatDate(this.date));
21559                 this.hidePopup();
21560                 e.preventDefault();
21561                 break;
21562             case 9: // tab
21563                 this.setValue(this.formatDate(this.date));
21564                 this.hidePopup();
21565                 break;
21566             case 16: // shift
21567             case 17: // ctrl
21568             case 18: // alt
21569                 break;
21570             default :
21571                 this.hidePopup();
21572                 
21573         }
21574     },
21575     
21576     
21577     onClick: function(e) 
21578     {
21579         e.stopPropagation();
21580         e.preventDefault();
21581         
21582         var target = e.getTarget();
21583         
21584         if(target.nodeName.toLowerCase() === 'i'){
21585             target = Roo.get(target).dom.parentNode;
21586         }
21587         
21588         var nodeName = target.nodeName;
21589         var className = target.className;
21590         var html = target.innerHTML;
21591         //Roo.log(nodeName);
21592         
21593         switch(nodeName.toLowerCase()) {
21594             case 'th':
21595                 switch(className) {
21596                     case 'switch':
21597                         this.showMode(1);
21598                         break;
21599                     case 'prev':
21600                     case 'next':
21601                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21602                         switch(this.viewMode){
21603                                 case 0:
21604                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21605                                         break;
21606                                 case 1:
21607                                 case 2:
21608                                         this.viewDate = this.moveYear(this.viewDate, dir);
21609                                         break;
21610                         }
21611                         this.fill();
21612                         break;
21613                     case 'today':
21614                         var date = new Date();
21615                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21616 //                        this.fill()
21617                         this.setValue(this.formatDate(this.date));
21618                         
21619                         this.hidePopup();
21620                         break;
21621                 }
21622                 break;
21623             case 'span':
21624                 if (className.indexOf('disabled') < 0) {
21625                     this.viewDate.setUTCDate(1);
21626                     if (className.indexOf('month') > -1) {
21627                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21628                     } else {
21629                         var year = parseInt(html, 10) || 0;
21630                         this.viewDate.setUTCFullYear(year);
21631                         
21632                     }
21633                     
21634                     if(this.singleMode){
21635                         this.setValue(this.formatDate(this.viewDate));
21636                         this.hidePopup();
21637                         return;
21638                     }
21639                     
21640                     this.showMode(-1);
21641                     this.fill();
21642                 }
21643                 break;
21644                 
21645             case 'td':
21646                 //Roo.log(className);
21647                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21648                     var day = parseInt(html, 10) || 1;
21649                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21650                         month = (this.viewDate || new Date()).getUTCMonth();
21651
21652                     if (className.indexOf('old') > -1) {
21653                         if(month === 0 ){
21654                             month = 11;
21655                             year -= 1;
21656                         }else{
21657                             month -= 1;
21658                         }
21659                     } else if (className.indexOf('new') > -1) {
21660                         if (month == 11) {
21661                             month = 0;
21662                             year += 1;
21663                         } else {
21664                             month += 1;
21665                         }
21666                     }
21667                     //Roo.log([year,month,day]);
21668                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21669                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21670 //                    this.fill();
21671                     //Roo.log(this.formatDate(this.date));
21672                     this.setValue(this.formatDate(this.date));
21673                     this.hidePopup();
21674                 }
21675                 break;
21676         }
21677     },
21678     
21679     setStartDate: function(startDate)
21680     {
21681         this.startDate = startDate || -Infinity;
21682         if (this.startDate !== -Infinity) {
21683             this.startDate = this.parseDate(this.startDate);
21684         }
21685         this.update();
21686         this.updateNavArrows();
21687     },
21688
21689     setEndDate: function(endDate)
21690     {
21691         this.endDate = endDate || Infinity;
21692         if (this.endDate !== Infinity) {
21693             this.endDate = this.parseDate(this.endDate);
21694         }
21695         this.update();
21696         this.updateNavArrows();
21697     },
21698     
21699     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21700     {
21701         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21702         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21703             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21704         }
21705         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21706             return parseInt(d, 10);
21707         });
21708         this.update();
21709         this.updateNavArrows();
21710     },
21711     
21712     updateNavArrows: function() 
21713     {
21714         if(this.singleMode){
21715             return;
21716         }
21717         
21718         var d = new Date(this.viewDate),
21719         year = d.getUTCFullYear(),
21720         month = d.getUTCMonth();
21721         
21722         Roo.each(this.picker().select('.prev', true).elements, function(v){
21723             v.show();
21724             switch (this.viewMode) {
21725                 case 0:
21726
21727                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21728                         v.hide();
21729                     }
21730                     break;
21731                 case 1:
21732                 case 2:
21733                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21734                         v.hide();
21735                     }
21736                     break;
21737             }
21738         });
21739         
21740         Roo.each(this.picker().select('.next', true).elements, function(v){
21741             v.show();
21742             switch (this.viewMode) {
21743                 case 0:
21744
21745                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21746                         v.hide();
21747                     }
21748                     break;
21749                 case 1:
21750                 case 2:
21751                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21752                         v.hide();
21753                     }
21754                     break;
21755             }
21756         })
21757     },
21758     
21759     moveMonth: function(date, dir)
21760     {
21761         if (!dir) {
21762             return date;
21763         }
21764         var new_date = new Date(date.valueOf()),
21765         day = new_date.getUTCDate(),
21766         month = new_date.getUTCMonth(),
21767         mag = Math.abs(dir),
21768         new_month, test;
21769         dir = dir > 0 ? 1 : -1;
21770         if (mag == 1){
21771             test = dir == -1
21772             // If going back one month, make sure month is not current month
21773             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21774             ? function(){
21775                 return new_date.getUTCMonth() == month;
21776             }
21777             // If going forward one month, make sure month is as expected
21778             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21779             : function(){
21780                 return new_date.getUTCMonth() != new_month;
21781             };
21782             new_month = month + dir;
21783             new_date.setUTCMonth(new_month);
21784             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21785             if (new_month < 0 || new_month > 11) {
21786                 new_month = (new_month + 12) % 12;
21787             }
21788         } else {
21789             // For magnitudes >1, move one month at a time...
21790             for (var i=0; i<mag; i++) {
21791                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21792                 new_date = this.moveMonth(new_date, dir);
21793             }
21794             // ...then reset the day, keeping it in the new month
21795             new_month = new_date.getUTCMonth();
21796             new_date.setUTCDate(day);
21797             test = function(){
21798                 return new_month != new_date.getUTCMonth();
21799             };
21800         }
21801         // Common date-resetting loop -- if date is beyond end of month, make it
21802         // end of month
21803         while (test()){
21804             new_date.setUTCDate(--day);
21805             new_date.setUTCMonth(new_month);
21806         }
21807         return new_date;
21808     },
21809
21810     moveYear: function(date, dir)
21811     {
21812         return this.moveMonth(date, dir*12);
21813     },
21814
21815     dateWithinRange: function(date)
21816     {
21817         return date >= this.startDate && date <= this.endDate;
21818     },
21819
21820     
21821     remove: function() 
21822     {
21823         this.picker().remove();
21824     },
21825     
21826     validateValue : function(value)
21827     {
21828         if(this.getVisibilityEl().hasClass('hidden')){
21829             return true;
21830         }
21831         
21832         if(value.length < 1)  {
21833             if(this.allowBlank){
21834                 return true;
21835             }
21836             return false;
21837         }
21838         
21839         if(value.length < this.minLength){
21840             return false;
21841         }
21842         if(value.length > this.maxLength){
21843             return false;
21844         }
21845         if(this.vtype){
21846             var vt = Roo.form.VTypes;
21847             if(!vt[this.vtype](value, this)){
21848                 return false;
21849             }
21850         }
21851         if(typeof this.validator == "function"){
21852             var msg = this.validator(value);
21853             if(msg !== true){
21854                 return false;
21855             }
21856         }
21857         
21858         if(this.regex && !this.regex.test(value)){
21859             return false;
21860         }
21861         
21862         if(typeof(this.parseDate(value)) == 'undefined'){
21863             return false;
21864         }
21865         
21866         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21867             return false;
21868         }      
21869         
21870         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21871             return false;
21872         } 
21873         
21874         
21875         return true;
21876     },
21877     
21878     reset : function()
21879     {
21880         this.date = this.viewDate = '';
21881         
21882         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21883     }
21884    
21885 });
21886
21887 Roo.apply(Roo.bootstrap.DateField,  {
21888     
21889     head : {
21890         tag: 'thead',
21891         cn: [
21892         {
21893             tag: 'tr',
21894             cn: [
21895             {
21896                 tag: 'th',
21897                 cls: 'prev',
21898                 html: '<i class="fa fa-arrow-left"/>'
21899             },
21900             {
21901                 tag: 'th',
21902                 cls: 'switch',
21903                 colspan: '5'
21904             },
21905             {
21906                 tag: 'th',
21907                 cls: 'next',
21908                 html: '<i class="fa fa-arrow-right"/>'
21909             }
21910
21911             ]
21912         }
21913         ]
21914     },
21915     
21916     content : {
21917         tag: 'tbody',
21918         cn: [
21919         {
21920             tag: 'tr',
21921             cn: [
21922             {
21923                 tag: 'td',
21924                 colspan: '7'
21925             }
21926             ]
21927         }
21928         ]
21929     },
21930     
21931     footer : {
21932         tag: 'tfoot',
21933         cn: [
21934         {
21935             tag: 'tr',
21936             cn: [
21937             {
21938                 tag: 'th',
21939                 colspan: '7',
21940                 cls: 'today'
21941             }
21942                     
21943             ]
21944         }
21945         ]
21946     },
21947     
21948     dates:{
21949         en: {
21950             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21951             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21952             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21953             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21954             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21955             today: "Today"
21956         }
21957     },
21958     
21959     modes: [
21960     {
21961         clsName: 'days',
21962         navFnc: 'Month',
21963         navStep: 1
21964     },
21965     {
21966         clsName: 'months',
21967         navFnc: 'FullYear',
21968         navStep: 1
21969     },
21970     {
21971         clsName: 'years',
21972         navFnc: 'FullYear',
21973         navStep: 10
21974     }]
21975 });
21976
21977 Roo.apply(Roo.bootstrap.DateField,  {
21978   
21979     template : {
21980         tag: 'div',
21981         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21982         cn: [
21983         {
21984             tag: 'div',
21985             cls: 'datepicker-days',
21986             cn: [
21987             {
21988                 tag: 'table',
21989                 cls: 'table-condensed',
21990                 cn:[
21991                 Roo.bootstrap.DateField.head,
21992                 {
21993                     tag: 'tbody'
21994                 },
21995                 Roo.bootstrap.DateField.footer
21996                 ]
21997             }
21998             ]
21999         },
22000         {
22001             tag: 'div',
22002             cls: 'datepicker-months',
22003             cn: [
22004             {
22005                 tag: 'table',
22006                 cls: 'table-condensed',
22007                 cn:[
22008                 Roo.bootstrap.DateField.head,
22009                 Roo.bootstrap.DateField.content,
22010                 Roo.bootstrap.DateField.footer
22011                 ]
22012             }
22013             ]
22014         },
22015         {
22016             tag: 'div',
22017             cls: 'datepicker-years',
22018             cn: [
22019             {
22020                 tag: 'table',
22021                 cls: 'table-condensed',
22022                 cn:[
22023                 Roo.bootstrap.DateField.head,
22024                 Roo.bootstrap.DateField.content,
22025                 Roo.bootstrap.DateField.footer
22026                 ]
22027             }
22028             ]
22029         }
22030         ]
22031     }
22032 });
22033
22034  
22035
22036  /*
22037  * - LGPL
22038  *
22039  * TimeField
22040  * 
22041  */
22042
22043 /**
22044  * @class Roo.bootstrap.TimeField
22045  * @extends Roo.bootstrap.Input
22046  * Bootstrap DateField class
22047  * 
22048  * 
22049  * @constructor
22050  * Create a new TimeField
22051  * @param {Object} config The config object
22052  */
22053
22054 Roo.bootstrap.TimeField = function(config){
22055     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22056     this.addEvents({
22057             /**
22058              * @event show
22059              * Fires when this field show.
22060              * @param {Roo.bootstrap.DateField} thisthis
22061              * @param {Mixed} date The date value
22062              */
22063             show : true,
22064             /**
22065              * @event show
22066              * Fires when this field hide.
22067              * @param {Roo.bootstrap.DateField} this
22068              * @param {Mixed} date The date value
22069              */
22070             hide : true,
22071             /**
22072              * @event select
22073              * Fires when select a date.
22074              * @param {Roo.bootstrap.DateField} this
22075              * @param {Mixed} date The date value
22076              */
22077             select : true
22078         });
22079 };
22080
22081 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22082     
22083     /**
22084      * @cfg {String} format
22085      * The default time format string which can be overriden for localization support.  The format must be
22086      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22087      */
22088     format : "H:i",
22089
22090     getAutoCreate : function()
22091     {
22092         this.after = '<i class="fa far fa-clock"></i>';
22093         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22094         
22095          
22096     },
22097     onRender: function(ct, position)
22098     {
22099         
22100         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22101                 
22102         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22103         
22104         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22105         
22106         this.pop = this.picker().select('>.datepicker-time',true).first();
22107         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22108         
22109         this.picker().on('mousedown', this.onMousedown, this);
22110         this.picker().on('click', this.onClick, this);
22111         
22112         this.picker().addClass('datepicker-dropdown');
22113     
22114         this.fillTime();
22115         this.update();
22116             
22117         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22118         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22119         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22120         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22121         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22122         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22123
22124     },
22125     
22126     fireKey: function(e){
22127         if (!this.picker().isVisible()){
22128             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22129                 this.show();
22130             }
22131             return;
22132         }
22133
22134         e.preventDefault();
22135         
22136         switch(e.keyCode){
22137             case 27: // escape
22138                 this.hide();
22139                 break;
22140             case 37: // left
22141             case 39: // right
22142                 this.onTogglePeriod();
22143                 break;
22144             case 38: // up
22145                 this.onIncrementMinutes();
22146                 break;
22147             case 40: // down
22148                 this.onDecrementMinutes();
22149                 break;
22150             case 13: // enter
22151             case 9: // tab
22152                 this.setTime();
22153                 break;
22154         }
22155     },
22156     
22157     onClick: function(e) {
22158         e.stopPropagation();
22159         e.preventDefault();
22160     },
22161     
22162     picker : function()
22163     {
22164         return this.pickerEl;
22165     },
22166     
22167     fillTime: function()
22168     {    
22169         var time = this.pop.select('tbody', true).first();
22170         
22171         time.dom.innerHTML = '';
22172         
22173         time.createChild({
22174             tag: 'tr',
22175             cn: [
22176                 {
22177                     tag: 'td',
22178                     cn: [
22179                         {
22180                             tag: 'a',
22181                             href: '#',
22182                             cls: 'btn',
22183                             cn: [
22184                                 {
22185                                     tag: 'i',
22186                                     cls: 'hours-up fa fas fa-chevron-up'
22187                                 }
22188                             ]
22189                         } 
22190                     ]
22191                 },
22192                 {
22193                     tag: 'td',
22194                     cls: 'separator'
22195                 },
22196                 {
22197                     tag: 'td',
22198                     cn: [
22199                         {
22200                             tag: 'a',
22201                             href: '#',
22202                             cls: 'btn',
22203                             cn: [
22204                                 {
22205                                     tag: 'i',
22206                                     cls: 'minutes-up fa fas fa-chevron-up'
22207                                 }
22208                             ]
22209                         }
22210                     ]
22211                 },
22212                 {
22213                     tag: 'td',
22214                     cls: 'separator'
22215                 }
22216             ]
22217         });
22218         
22219         time.createChild({
22220             tag: 'tr',
22221             cn: [
22222                 {
22223                     tag: 'td',
22224                     cn: [
22225                         {
22226                             tag: 'span',
22227                             cls: 'timepicker-hour',
22228                             html: '00'
22229                         }  
22230                     ]
22231                 },
22232                 {
22233                     tag: 'td',
22234                     cls: 'separator',
22235                     html: ':'
22236                 },
22237                 {
22238                     tag: 'td',
22239                     cn: [
22240                         {
22241                             tag: 'span',
22242                             cls: 'timepicker-minute',
22243                             html: '00'
22244                         }  
22245                     ]
22246                 },
22247                 {
22248                     tag: 'td',
22249                     cls: 'separator'
22250                 },
22251                 {
22252                     tag: 'td',
22253                     cn: [
22254                         {
22255                             tag: 'button',
22256                             type: 'button',
22257                             cls: 'btn btn-primary period',
22258                             html: 'AM'
22259                             
22260                         }
22261                     ]
22262                 }
22263             ]
22264         });
22265         
22266         time.createChild({
22267             tag: 'tr',
22268             cn: [
22269                 {
22270                     tag: 'td',
22271                     cn: [
22272                         {
22273                             tag: 'a',
22274                             href: '#',
22275                             cls: 'btn',
22276                             cn: [
22277                                 {
22278                                     tag: 'span',
22279                                     cls: 'hours-down fa fas fa-chevron-down'
22280                                 }
22281                             ]
22282                         }
22283                     ]
22284                 },
22285                 {
22286                     tag: 'td',
22287                     cls: 'separator'
22288                 },
22289                 {
22290                     tag: 'td',
22291                     cn: [
22292                         {
22293                             tag: 'a',
22294                             href: '#',
22295                             cls: 'btn',
22296                             cn: [
22297                                 {
22298                                     tag: 'span',
22299                                     cls: 'minutes-down fa fas fa-chevron-down'
22300                                 }
22301                             ]
22302                         }
22303                     ]
22304                 },
22305                 {
22306                     tag: 'td',
22307                     cls: 'separator'
22308                 }
22309             ]
22310         });
22311         
22312     },
22313     
22314     update: function()
22315     {
22316         
22317         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22318         
22319         this.fill();
22320     },
22321     
22322     fill: function() 
22323     {
22324         var hours = this.time.getHours();
22325         var minutes = this.time.getMinutes();
22326         var period = 'AM';
22327         
22328         if(hours > 11){
22329             period = 'PM';
22330         }
22331         
22332         if(hours == 0){
22333             hours = 12;
22334         }
22335         
22336         
22337         if(hours > 12){
22338             hours = hours - 12;
22339         }
22340         
22341         if(hours < 10){
22342             hours = '0' + hours;
22343         }
22344         
22345         if(minutes < 10){
22346             minutes = '0' + minutes;
22347         }
22348         
22349         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22350         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22351         this.pop.select('button', true).first().dom.innerHTML = period;
22352         
22353     },
22354     
22355     place: function()
22356     {   
22357         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22358         
22359         var cls = ['bottom'];
22360         
22361         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22362             cls.pop();
22363             cls.push('top');
22364         }
22365         
22366         cls.push('right');
22367         
22368         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22369             cls.pop();
22370             cls.push('left');
22371         }
22372         //this.picker().setXY(20000,20000);
22373         this.picker().addClass(cls.join('-'));
22374         
22375         var _this = this;
22376         
22377         Roo.each(cls, function(c){
22378             if(c == 'bottom'){
22379                 (function() {
22380                  //  
22381                 }).defer(200);
22382                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22383                 //_this.picker().setTop(_this.inputEl().getHeight());
22384                 return;
22385             }
22386             if(c == 'top'){
22387                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22388                 
22389                 //_this.picker().setTop(0 - _this.picker().getHeight());
22390                 return;
22391             }
22392             /*
22393             if(c == 'left'){
22394                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22395                 return;
22396             }
22397             if(c == 'right'){
22398                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22399                 return;
22400             }
22401             */
22402         });
22403         
22404     },
22405   
22406     onFocus : function()
22407     {
22408         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22409         this.show();
22410     },
22411     
22412     onBlur : function()
22413     {
22414         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22415         this.hide();
22416     },
22417     
22418     show : function()
22419     {
22420         this.picker().show();
22421         this.pop.show();
22422         this.update();
22423         this.place();
22424         
22425         this.fireEvent('show', this, this.date);
22426     },
22427     
22428     hide : function()
22429     {
22430         this.picker().hide();
22431         this.pop.hide();
22432         
22433         this.fireEvent('hide', this, this.date);
22434     },
22435     
22436     setTime : function()
22437     {
22438         this.hide();
22439         this.setValue(this.time.format(this.format));
22440         
22441         this.fireEvent('select', this, this.date);
22442         
22443         
22444     },
22445     
22446     onMousedown: function(e){
22447         e.stopPropagation();
22448         e.preventDefault();
22449     },
22450     
22451     onIncrementHours: function()
22452     {
22453         Roo.log('onIncrementHours');
22454         this.time = this.time.add(Date.HOUR, 1);
22455         this.update();
22456         
22457     },
22458     
22459     onDecrementHours: function()
22460     {
22461         Roo.log('onDecrementHours');
22462         this.time = this.time.add(Date.HOUR, -1);
22463         this.update();
22464     },
22465     
22466     onIncrementMinutes: function()
22467     {
22468         Roo.log('onIncrementMinutes');
22469         this.time = this.time.add(Date.MINUTE, 1);
22470         this.update();
22471     },
22472     
22473     onDecrementMinutes: function()
22474     {
22475         Roo.log('onDecrementMinutes');
22476         this.time = this.time.add(Date.MINUTE, -1);
22477         this.update();
22478     },
22479     
22480     onTogglePeriod: function()
22481     {
22482         Roo.log('onTogglePeriod');
22483         this.time = this.time.add(Date.HOUR, 12);
22484         this.update();
22485     }
22486     
22487    
22488 });
22489  
22490
22491 Roo.apply(Roo.bootstrap.TimeField,  {
22492   
22493     template : {
22494         tag: 'div',
22495         cls: 'datepicker dropdown-menu',
22496         cn: [
22497             {
22498                 tag: 'div',
22499                 cls: 'datepicker-time',
22500                 cn: [
22501                 {
22502                     tag: 'table',
22503                     cls: 'table-condensed',
22504                     cn:[
22505                         {
22506                             tag: 'tbody',
22507                             cn: [
22508                                 {
22509                                     tag: 'tr',
22510                                     cn: [
22511                                     {
22512                                         tag: 'td',
22513                                         colspan: '7'
22514                                     }
22515                                     ]
22516                                 }
22517                             ]
22518                         },
22519                         {
22520                             tag: 'tfoot',
22521                             cn: [
22522                                 {
22523                                     tag: 'tr',
22524                                     cn: [
22525                                     {
22526                                         tag: 'th',
22527                                         colspan: '7',
22528                                         cls: '',
22529                                         cn: [
22530                                             {
22531                                                 tag: 'button',
22532                                                 cls: 'btn btn-info ok',
22533                                                 html: 'OK'
22534                                             }
22535                                         ]
22536                                     }
22537                     
22538                                     ]
22539                                 }
22540                             ]
22541                         }
22542                     ]
22543                 }
22544                 ]
22545             }
22546         ]
22547     }
22548 });
22549
22550  
22551
22552  /*
22553  * - LGPL
22554  *
22555  * MonthField
22556  * 
22557  */
22558
22559 /**
22560  * @class Roo.bootstrap.MonthField
22561  * @extends Roo.bootstrap.Input
22562  * Bootstrap MonthField class
22563  * 
22564  * @cfg {String} language default en
22565  * 
22566  * @constructor
22567  * Create a new MonthField
22568  * @param {Object} config The config object
22569  */
22570
22571 Roo.bootstrap.MonthField = function(config){
22572     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22573     
22574     this.addEvents({
22575         /**
22576          * @event show
22577          * Fires when this field show.
22578          * @param {Roo.bootstrap.MonthField} this
22579          * @param {Mixed} date The date value
22580          */
22581         show : true,
22582         /**
22583          * @event show
22584          * Fires when this field hide.
22585          * @param {Roo.bootstrap.MonthField} this
22586          * @param {Mixed} date The date value
22587          */
22588         hide : true,
22589         /**
22590          * @event select
22591          * Fires when select a date.
22592          * @param {Roo.bootstrap.MonthField} this
22593          * @param {String} oldvalue The old value
22594          * @param {String} newvalue The new value
22595          */
22596         select : true
22597     });
22598 };
22599
22600 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22601     
22602     onRender: function(ct, position)
22603     {
22604         
22605         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22606         
22607         this.language = this.language || 'en';
22608         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22609         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22610         
22611         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22612         this.isInline = false;
22613         this.isInput = true;
22614         this.component = this.el.select('.add-on', true).first() || false;
22615         this.component = (this.component && this.component.length === 0) ? false : this.component;
22616         this.hasInput = this.component && this.inputEL().length;
22617         
22618         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22619         
22620         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22621         
22622         this.picker().on('mousedown', this.onMousedown, this);
22623         this.picker().on('click', this.onClick, this);
22624         
22625         this.picker().addClass('datepicker-dropdown');
22626         
22627         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22628             v.setStyle('width', '189px');
22629         });
22630         
22631         this.fillMonths();
22632         
22633         this.update();
22634         
22635         if(this.isInline) {
22636             this.show();
22637         }
22638         
22639     },
22640     
22641     setValue: function(v, suppressEvent)
22642     {   
22643         var o = this.getValue();
22644         
22645         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22646         
22647         this.update();
22648
22649         if(suppressEvent !== true){
22650             this.fireEvent('select', this, o, v);
22651         }
22652         
22653     },
22654     
22655     getValue: function()
22656     {
22657         return this.value;
22658     },
22659     
22660     onClick: function(e) 
22661     {
22662         e.stopPropagation();
22663         e.preventDefault();
22664         
22665         var target = e.getTarget();
22666         
22667         if(target.nodeName.toLowerCase() === 'i'){
22668             target = Roo.get(target).dom.parentNode;
22669         }
22670         
22671         var nodeName = target.nodeName;
22672         var className = target.className;
22673         var html = target.innerHTML;
22674         
22675         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22676             return;
22677         }
22678         
22679         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22680         
22681         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22682         
22683         this.hide();
22684                         
22685     },
22686     
22687     picker : function()
22688     {
22689         return this.pickerEl;
22690     },
22691     
22692     fillMonths: function()
22693     {    
22694         var i = 0;
22695         var months = this.picker().select('>.datepicker-months td', true).first();
22696         
22697         months.dom.innerHTML = '';
22698         
22699         while (i < 12) {
22700             var month = {
22701                 tag: 'span',
22702                 cls: 'month',
22703                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22704             };
22705             
22706             months.createChild(month);
22707         }
22708         
22709     },
22710     
22711     update: function()
22712     {
22713         var _this = this;
22714         
22715         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22716             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22717         }
22718         
22719         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22720             e.removeClass('active');
22721             
22722             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22723                 e.addClass('active');
22724             }
22725         })
22726     },
22727     
22728     place: function()
22729     {
22730         if(this.isInline) {
22731             return;
22732         }
22733         
22734         this.picker().removeClass(['bottom', 'top']);
22735         
22736         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22737             /*
22738              * place to the top of element!
22739              *
22740              */
22741             
22742             this.picker().addClass('top');
22743             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22744             
22745             return;
22746         }
22747         
22748         this.picker().addClass('bottom');
22749         
22750         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22751     },
22752     
22753     onFocus : function()
22754     {
22755         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22756         this.show();
22757     },
22758     
22759     onBlur : function()
22760     {
22761         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22762         
22763         var d = this.inputEl().getValue();
22764         
22765         this.setValue(d);
22766                 
22767         this.hide();
22768     },
22769     
22770     show : function()
22771     {
22772         this.picker().show();
22773         this.picker().select('>.datepicker-months', true).first().show();
22774         this.update();
22775         this.place();
22776         
22777         this.fireEvent('show', this, this.date);
22778     },
22779     
22780     hide : function()
22781     {
22782         if(this.isInline) {
22783             return;
22784         }
22785         this.picker().hide();
22786         this.fireEvent('hide', this, this.date);
22787         
22788     },
22789     
22790     onMousedown: function(e)
22791     {
22792         e.stopPropagation();
22793         e.preventDefault();
22794     },
22795     
22796     keyup: function(e)
22797     {
22798         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22799         this.update();
22800     },
22801
22802     fireKey: function(e)
22803     {
22804         if (!this.picker().isVisible()){
22805             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22806                 this.show();
22807             }
22808             return;
22809         }
22810         
22811         var dir;
22812         
22813         switch(e.keyCode){
22814             case 27: // escape
22815                 this.hide();
22816                 e.preventDefault();
22817                 break;
22818             case 37: // left
22819             case 39: // right
22820                 dir = e.keyCode == 37 ? -1 : 1;
22821                 
22822                 this.vIndex = this.vIndex + dir;
22823                 
22824                 if(this.vIndex < 0){
22825                     this.vIndex = 0;
22826                 }
22827                 
22828                 if(this.vIndex > 11){
22829                     this.vIndex = 11;
22830                 }
22831                 
22832                 if(isNaN(this.vIndex)){
22833                     this.vIndex = 0;
22834                 }
22835                 
22836                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22837                 
22838                 break;
22839             case 38: // up
22840             case 40: // down
22841                 
22842                 dir = e.keyCode == 38 ? -1 : 1;
22843                 
22844                 this.vIndex = this.vIndex + dir * 4;
22845                 
22846                 if(this.vIndex < 0){
22847                     this.vIndex = 0;
22848                 }
22849                 
22850                 if(this.vIndex > 11){
22851                     this.vIndex = 11;
22852                 }
22853                 
22854                 if(isNaN(this.vIndex)){
22855                     this.vIndex = 0;
22856                 }
22857                 
22858                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22859                 break;
22860                 
22861             case 13: // enter
22862                 
22863                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22864                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22865                 }
22866                 
22867                 this.hide();
22868                 e.preventDefault();
22869                 break;
22870             case 9: // tab
22871                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22872                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22873                 }
22874                 this.hide();
22875                 break;
22876             case 16: // shift
22877             case 17: // ctrl
22878             case 18: // alt
22879                 break;
22880             default :
22881                 this.hide();
22882                 
22883         }
22884     },
22885     
22886     remove: function() 
22887     {
22888         this.picker().remove();
22889     }
22890    
22891 });
22892
22893 Roo.apply(Roo.bootstrap.MonthField,  {
22894     
22895     content : {
22896         tag: 'tbody',
22897         cn: [
22898         {
22899             tag: 'tr',
22900             cn: [
22901             {
22902                 tag: 'td',
22903                 colspan: '7'
22904             }
22905             ]
22906         }
22907         ]
22908     },
22909     
22910     dates:{
22911         en: {
22912             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22913             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22914         }
22915     }
22916 });
22917
22918 Roo.apply(Roo.bootstrap.MonthField,  {
22919   
22920     template : {
22921         tag: 'div',
22922         cls: 'datepicker dropdown-menu roo-dynamic',
22923         cn: [
22924             {
22925                 tag: 'div',
22926                 cls: 'datepicker-months',
22927                 cn: [
22928                 {
22929                     tag: 'table',
22930                     cls: 'table-condensed',
22931                     cn:[
22932                         Roo.bootstrap.DateField.content
22933                     ]
22934                 }
22935                 ]
22936             }
22937         ]
22938     }
22939 });
22940
22941  
22942
22943  
22944  /*
22945  * - LGPL
22946  *
22947  * CheckBox
22948  * 
22949  */
22950
22951 /**
22952  * @class Roo.bootstrap.CheckBox
22953  * @extends Roo.bootstrap.Input
22954  * Bootstrap CheckBox class
22955  * 
22956  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22957  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22958  * @cfg {String} boxLabel The text that appears beside the checkbox
22959  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22960  * @cfg {Boolean} checked initnal the element
22961  * @cfg {Boolean} inline inline the element (default false)
22962  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22963  * @cfg {String} tooltip label tooltip
22964  * 
22965  * @constructor
22966  * Create a new CheckBox
22967  * @param {Object} config The config object
22968  */
22969
22970 Roo.bootstrap.CheckBox = function(config){
22971     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22972    
22973     this.addEvents({
22974         /**
22975         * @event check
22976         * Fires when the element is checked or unchecked.
22977         * @param {Roo.bootstrap.CheckBox} this This input
22978         * @param {Boolean} checked The new checked value
22979         */
22980        check : true,
22981        /**
22982         * @event click
22983         * Fires when the element is click.
22984         * @param {Roo.bootstrap.CheckBox} this This input
22985         */
22986        click : true
22987     });
22988     
22989 };
22990
22991 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22992   
22993     inputType: 'checkbox',
22994     inputValue: 1,
22995     valueOff: 0,
22996     boxLabel: false,
22997     checked: false,
22998     weight : false,
22999     inline: false,
23000     tooltip : '',
23001     
23002     // checkbox success does not make any sense really.. 
23003     invalidClass : "",
23004     validClass : "",
23005     
23006     
23007     getAutoCreate : function()
23008     {
23009         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23010         
23011         var id = Roo.id();
23012         
23013         var cfg = {};
23014         
23015         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23016         
23017         if(this.inline){
23018             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23019         }
23020         
23021         var input =  {
23022             tag: 'input',
23023             id : id,
23024             type : this.inputType,
23025             value : this.inputValue,
23026             cls : 'roo-' + this.inputType, //'form-box',
23027             placeholder : this.placeholder || ''
23028             
23029         };
23030         
23031         if(this.inputType != 'radio'){
23032             var hidden =  {
23033                 tag: 'input',
23034                 type : 'hidden',
23035                 cls : 'roo-hidden-value',
23036                 value : this.checked ? this.inputValue : this.valueOff
23037             };
23038         }
23039         
23040             
23041         if (this.weight) { // Validity check?
23042             cfg.cls += " " + this.inputType + "-" + this.weight;
23043         }
23044         
23045         if (this.disabled) {
23046             input.disabled=true;
23047         }
23048         
23049         if(this.checked){
23050             input.checked = this.checked;
23051         }
23052         
23053         if (this.name) {
23054             
23055             input.name = this.name;
23056             
23057             if(this.inputType != 'radio'){
23058                 hidden.name = this.name;
23059                 input.name = '_hidden_' + this.name;
23060             }
23061         }
23062         
23063         if (this.size) {
23064             input.cls += ' input-' + this.size;
23065         }
23066         
23067         var settings=this;
23068         
23069         ['xs','sm','md','lg'].map(function(size){
23070             if (settings[size]) {
23071                 cfg.cls += ' col-' + size + '-' + settings[size];
23072             }
23073         });
23074         
23075         var inputblock = input;
23076          
23077         if (this.before || this.after) {
23078             
23079             inputblock = {
23080                 cls : 'input-group',
23081                 cn :  [] 
23082             };
23083             
23084             if (this.before) {
23085                 inputblock.cn.push({
23086                     tag :'span',
23087                     cls : 'input-group-addon',
23088                     html : this.before
23089                 });
23090             }
23091             
23092             inputblock.cn.push(input);
23093             
23094             if(this.inputType != 'radio'){
23095                 inputblock.cn.push(hidden);
23096             }
23097             
23098             if (this.after) {
23099                 inputblock.cn.push({
23100                     tag :'span',
23101                     cls : 'input-group-addon',
23102                     html : this.after
23103                 });
23104             }
23105             
23106         }
23107         var boxLabelCfg = false;
23108         
23109         if(this.boxLabel){
23110            
23111             boxLabelCfg = {
23112                 tag: 'label',
23113                 //'for': id, // box label is handled by onclick - so no for...
23114                 cls: 'box-label',
23115                 html: this.boxLabel
23116             };
23117             if(this.tooltip){
23118                 boxLabelCfg.tooltip = this.tooltip;
23119             }
23120              
23121         }
23122         
23123         
23124         if (align ==='left' && this.fieldLabel.length) {
23125 //                Roo.log("left and has label");
23126             cfg.cn = [
23127                 {
23128                     tag: 'label',
23129                     'for' :  id,
23130                     cls : 'control-label',
23131                     html : this.fieldLabel
23132                 },
23133                 {
23134                     cls : "", 
23135                     cn: [
23136                         inputblock
23137                     ]
23138                 }
23139             ];
23140             
23141             if (boxLabelCfg) {
23142                 cfg.cn[1].cn.push(boxLabelCfg);
23143             }
23144             
23145             if(this.labelWidth > 12){
23146                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23147             }
23148             
23149             if(this.labelWidth < 13 && this.labelmd == 0){
23150                 this.labelmd = this.labelWidth;
23151             }
23152             
23153             if(this.labellg > 0){
23154                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23155                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23156             }
23157             
23158             if(this.labelmd > 0){
23159                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23160                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23161             }
23162             
23163             if(this.labelsm > 0){
23164                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23165                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23166             }
23167             
23168             if(this.labelxs > 0){
23169                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23170                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23171             }
23172             
23173         } else if ( this.fieldLabel.length) {
23174 //                Roo.log(" label");
23175                 cfg.cn = [
23176                    
23177                     {
23178                         tag: this.boxLabel ? 'span' : 'label',
23179                         'for': id,
23180                         cls: 'control-label box-input-label',
23181                         //cls : 'input-group-addon',
23182                         html : this.fieldLabel
23183                     },
23184                     
23185                     inputblock
23186                     
23187                 ];
23188                 if (boxLabelCfg) {
23189                     cfg.cn.push(boxLabelCfg);
23190                 }
23191
23192         } else {
23193             
23194 //                Roo.log(" no label && no align");
23195                 cfg.cn = [  inputblock ] ;
23196                 if (boxLabelCfg) {
23197                     cfg.cn.push(boxLabelCfg);
23198                 }
23199
23200                 
23201         }
23202         
23203        
23204         
23205         if(this.inputType != 'radio'){
23206             cfg.cn.push(hidden);
23207         }
23208         
23209         return cfg;
23210         
23211     },
23212     
23213     /**
23214      * return the real input element.
23215      */
23216     inputEl: function ()
23217     {
23218         return this.el.select('input.roo-' + this.inputType,true).first();
23219     },
23220     hiddenEl: function ()
23221     {
23222         return this.el.select('input.roo-hidden-value',true).first();
23223     },
23224     
23225     labelEl: function()
23226     {
23227         return this.el.select('label.control-label',true).first();
23228     },
23229     /* depricated... */
23230     
23231     label: function()
23232     {
23233         return this.labelEl();
23234     },
23235     
23236     boxLabelEl: function()
23237     {
23238         return this.el.select('label.box-label',true).first();
23239     },
23240     
23241     initEvents : function()
23242     {
23243 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23244         
23245         this.inputEl().on('click', this.onClick,  this);
23246         
23247         if (this.boxLabel) { 
23248             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23249         }
23250         
23251         this.startValue = this.getValue();
23252         
23253         if(this.groupId){
23254             Roo.bootstrap.CheckBox.register(this);
23255         }
23256     },
23257     
23258     onClick : function(e)
23259     {   
23260         if(this.fireEvent('click', this, e) !== false){
23261             this.setChecked(!this.checked);
23262         }
23263         
23264     },
23265     
23266     setChecked : function(state,suppressEvent)
23267     {
23268         this.startValue = this.getValue();
23269
23270         if(this.inputType == 'radio'){
23271             
23272             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23273                 e.dom.checked = false;
23274             });
23275             
23276             this.inputEl().dom.checked = true;
23277             
23278             this.inputEl().dom.value = this.inputValue;
23279             
23280             if(suppressEvent !== true){
23281                 this.fireEvent('check', this, true);
23282             }
23283             
23284             this.validate();
23285             
23286             return;
23287         }
23288         
23289         this.checked = state;
23290         
23291         this.inputEl().dom.checked = state;
23292         
23293         
23294         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23295         
23296         if(suppressEvent !== true){
23297             this.fireEvent('check', this, state);
23298         }
23299         
23300         this.validate();
23301     },
23302     
23303     getValue : function()
23304     {
23305         if(this.inputType == 'radio'){
23306             return this.getGroupValue();
23307         }
23308         
23309         return this.hiddenEl().dom.value;
23310         
23311     },
23312     
23313     getGroupValue : function()
23314     {
23315         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23316             return '';
23317         }
23318         
23319         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23320     },
23321     
23322     setValue : function(v,suppressEvent)
23323     {
23324         if(this.inputType == 'radio'){
23325             this.setGroupValue(v, suppressEvent);
23326             return;
23327         }
23328         
23329         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23330         
23331         this.validate();
23332     },
23333     
23334     setGroupValue : function(v, suppressEvent)
23335     {
23336         this.startValue = this.getValue();
23337         
23338         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23339             e.dom.checked = false;
23340             
23341             if(e.dom.value == v){
23342                 e.dom.checked = true;
23343             }
23344         });
23345         
23346         if(suppressEvent !== true){
23347             this.fireEvent('check', this, true);
23348         }
23349
23350         this.validate();
23351         
23352         return;
23353     },
23354     
23355     validate : function()
23356     {
23357         if(this.getVisibilityEl().hasClass('hidden')){
23358             return true;
23359         }
23360         
23361         if(
23362                 this.disabled || 
23363                 (this.inputType == 'radio' && this.validateRadio()) ||
23364                 (this.inputType == 'checkbox' && this.validateCheckbox())
23365         ){
23366             this.markValid();
23367             return true;
23368         }
23369         
23370         this.markInvalid();
23371         return false;
23372     },
23373     
23374     validateRadio : function()
23375     {
23376         if(this.getVisibilityEl().hasClass('hidden')){
23377             return true;
23378         }
23379         
23380         if(this.allowBlank){
23381             return true;
23382         }
23383         
23384         var valid = false;
23385         
23386         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23387             if(!e.dom.checked){
23388                 return;
23389             }
23390             
23391             valid = true;
23392             
23393             return false;
23394         });
23395         
23396         return valid;
23397     },
23398     
23399     validateCheckbox : function()
23400     {
23401         if(!this.groupId){
23402             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23403             //return (this.getValue() == this.inputValue) ? true : false;
23404         }
23405         
23406         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23407         
23408         if(!group){
23409             return false;
23410         }
23411         
23412         var r = false;
23413         
23414         for(var i in group){
23415             if(group[i].el.isVisible(true)){
23416                 r = false;
23417                 break;
23418             }
23419             
23420             r = true;
23421         }
23422         
23423         for(var i in group){
23424             if(r){
23425                 break;
23426             }
23427             
23428             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23429         }
23430         
23431         return r;
23432     },
23433     
23434     /**
23435      * Mark this field as valid
23436      */
23437     markValid : function()
23438     {
23439         var _this = this;
23440         
23441         this.fireEvent('valid', this);
23442         
23443         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23444         
23445         if(this.groupId){
23446             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23447         }
23448         
23449         if(label){
23450             label.markValid();
23451         }
23452
23453         if(this.inputType == 'radio'){
23454             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23455                 var fg = e.findParent('.form-group', false, true);
23456                 if (Roo.bootstrap.version == 3) {
23457                     fg.removeClass([_this.invalidClass, _this.validClass]);
23458                     fg.addClass(_this.validClass);
23459                 } else {
23460                     fg.removeClass(['is-valid', 'is-invalid']);
23461                     fg.addClass('is-valid');
23462                 }
23463             });
23464             
23465             return;
23466         }
23467
23468         if(!this.groupId){
23469             var fg = this.el.findParent('.form-group', false, true);
23470             if (Roo.bootstrap.version == 3) {
23471                 fg.removeClass([this.invalidClass, this.validClass]);
23472                 fg.addClass(this.validClass);
23473             } else {
23474                 fg.removeClass(['is-valid', 'is-invalid']);
23475                 fg.addClass('is-valid');
23476             }
23477             return;
23478         }
23479         
23480         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23481         
23482         if(!group){
23483             return;
23484         }
23485         
23486         for(var i in group){
23487             var fg = group[i].el.findParent('.form-group', false, true);
23488             if (Roo.bootstrap.version == 3) {
23489                 fg.removeClass([this.invalidClass, this.validClass]);
23490                 fg.addClass(this.validClass);
23491             } else {
23492                 fg.removeClass(['is-valid', 'is-invalid']);
23493                 fg.addClass('is-valid');
23494             }
23495         }
23496     },
23497     
23498      /**
23499      * Mark this field as invalid
23500      * @param {String} msg The validation message
23501      */
23502     markInvalid : function(msg)
23503     {
23504         if(this.allowBlank){
23505             return;
23506         }
23507         
23508         var _this = this;
23509         
23510         this.fireEvent('invalid', this, msg);
23511         
23512         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23513         
23514         if(this.groupId){
23515             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23516         }
23517         
23518         if(label){
23519             label.markInvalid();
23520         }
23521             
23522         if(this.inputType == 'radio'){
23523             
23524             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23525                 var fg = e.findParent('.form-group', false, true);
23526                 if (Roo.bootstrap.version == 3) {
23527                     fg.removeClass([_this.invalidClass, _this.validClass]);
23528                     fg.addClass(_this.invalidClass);
23529                 } else {
23530                     fg.removeClass(['is-invalid', 'is-valid']);
23531                     fg.addClass('is-invalid');
23532                 }
23533             });
23534             
23535             return;
23536         }
23537         
23538         if(!this.groupId){
23539             var fg = this.el.findParent('.form-group', false, true);
23540             if (Roo.bootstrap.version == 3) {
23541                 fg.removeClass([_this.invalidClass, _this.validClass]);
23542                 fg.addClass(_this.invalidClass);
23543             } else {
23544                 fg.removeClass(['is-invalid', 'is-valid']);
23545                 fg.addClass('is-invalid');
23546             }
23547             return;
23548         }
23549         
23550         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23551         
23552         if(!group){
23553             return;
23554         }
23555         
23556         for(var i in group){
23557             var fg = group[i].el.findParent('.form-group', false, true);
23558             if (Roo.bootstrap.version == 3) {
23559                 fg.removeClass([_this.invalidClass, _this.validClass]);
23560                 fg.addClass(_this.invalidClass);
23561             } else {
23562                 fg.removeClass(['is-invalid', 'is-valid']);
23563                 fg.addClass('is-invalid');
23564             }
23565         }
23566         
23567     },
23568     
23569     clearInvalid : function()
23570     {
23571         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23572         
23573         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23574         
23575         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23576         
23577         if (label && label.iconEl) {
23578             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23579             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23580         }
23581     },
23582     
23583     disable : function()
23584     {
23585         if(this.inputType != 'radio'){
23586             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23587             return;
23588         }
23589         
23590         var _this = this;
23591         
23592         if(this.rendered){
23593             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23594                 _this.getActionEl().addClass(this.disabledClass);
23595                 e.dom.disabled = true;
23596             });
23597         }
23598         
23599         this.disabled = true;
23600         this.fireEvent("disable", this);
23601         return this;
23602     },
23603
23604     enable : function()
23605     {
23606         if(this.inputType != 'radio'){
23607             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23608             return;
23609         }
23610         
23611         var _this = this;
23612         
23613         if(this.rendered){
23614             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23615                 _this.getActionEl().removeClass(this.disabledClass);
23616                 e.dom.disabled = false;
23617             });
23618         }
23619         
23620         this.disabled = false;
23621         this.fireEvent("enable", this);
23622         return this;
23623     },
23624     
23625     setBoxLabel : function(v)
23626     {
23627         this.boxLabel = v;
23628         
23629         if(this.rendered){
23630             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23631         }
23632     }
23633
23634 });
23635
23636 Roo.apply(Roo.bootstrap.CheckBox, {
23637     
23638     groups: {},
23639     
23640      /**
23641     * register a CheckBox Group
23642     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23643     */
23644     register : function(checkbox)
23645     {
23646         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23647             this.groups[checkbox.groupId] = {};
23648         }
23649         
23650         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23651             return;
23652         }
23653         
23654         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23655         
23656     },
23657     /**
23658     * fetch a CheckBox Group based on the group ID
23659     * @param {string} the group ID
23660     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23661     */
23662     get: function(groupId) {
23663         if (typeof(this.groups[groupId]) == 'undefined') {
23664             return false;
23665         }
23666         
23667         return this.groups[groupId] ;
23668     }
23669     
23670     
23671 });
23672 /*
23673  * - LGPL
23674  *
23675  * RadioItem
23676  * 
23677  */
23678
23679 /**
23680  * @class Roo.bootstrap.Radio
23681  * @extends Roo.bootstrap.Component
23682  * Bootstrap Radio class
23683  * @cfg {String} boxLabel - the label associated
23684  * @cfg {String} value - the value of radio
23685  * 
23686  * @constructor
23687  * Create a new Radio
23688  * @param {Object} config The config object
23689  */
23690 Roo.bootstrap.Radio = function(config){
23691     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23692     
23693 };
23694
23695 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23696     
23697     boxLabel : '',
23698     
23699     value : '',
23700     
23701     getAutoCreate : function()
23702     {
23703         var cfg = {
23704             tag : 'div',
23705             cls : 'form-group radio',
23706             cn : [
23707                 {
23708                     tag : 'label',
23709                     cls : 'box-label',
23710                     html : this.boxLabel
23711                 }
23712             ]
23713         };
23714         
23715         return cfg;
23716     },
23717     
23718     initEvents : function() 
23719     {
23720         this.parent().register(this);
23721         
23722         this.el.on('click', this.onClick, this);
23723         
23724     },
23725     
23726     onClick : function(e)
23727     {
23728         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23729             this.setChecked(true);
23730         }
23731     },
23732     
23733     setChecked : function(state, suppressEvent)
23734     {
23735         this.parent().setValue(this.value, suppressEvent);
23736         
23737     },
23738     
23739     setBoxLabel : function(v)
23740     {
23741         this.boxLabel = v;
23742         
23743         if(this.rendered){
23744             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23745         }
23746     }
23747     
23748 });
23749  
23750
23751  /*
23752  * - LGPL
23753  *
23754  * Input
23755  * 
23756  */
23757
23758 /**
23759  * @class Roo.bootstrap.SecurePass
23760  * @extends Roo.bootstrap.Input
23761  * Bootstrap SecurePass class
23762  *
23763  * 
23764  * @constructor
23765  * Create a new SecurePass
23766  * @param {Object} config The config object
23767  */
23768  
23769 Roo.bootstrap.SecurePass = function (config) {
23770     // these go here, so the translation tool can replace them..
23771     this.errors = {
23772         PwdEmpty: "Please type a password, and then retype it to confirm.",
23773         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23774         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23775         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23776         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23777         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23778         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23779         TooWeak: "Your password is Too Weak."
23780     },
23781     this.meterLabel = "Password strength:";
23782     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23783     this.meterClass = [
23784         "roo-password-meter-tooweak", 
23785         "roo-password-meter-weak", 
23786         "roo-password-meter-medium", 
23787         "roo-password-meter-strong", 
23788         "roo-password-meter-grey"
23789     ];
23790     
23791     this.errors = {};
23792     
23793     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23794 }
23795
23796 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23797     /**
23798      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23799      * {
23800      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23801      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23802      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23803      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23804      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23805      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23806      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23807      * })
23808      */
23809     // private
23810     
23811     meterWidth: 300,
23812     errorMsg :'',    
23813     errors: false,
23814     imageRoot: '/',
23815     /**
23816      * @cfg {String/Object} Label for the strength meter (defaults to
23817      * 'Password strength:')
23818      */
23819     // private
23820     meterLabel: '',
23821     /**
23822      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23823      * ['Weak', 'Medium', 'Strong'])
23824      */
23825     // private    
23826     pwdStrengths: false,    
23827     // private
23828     strength: 0,
23829     // private
23830     _lastPwd: null,
23831     // private
23832     kCapitalLetter: 0,
23833     kSmallLetter: 1,
23834     kDigit: 2,
23835     kPunctuation: 3,
23836     
23837     insecure: false,
23838     // private
23839     initEvents: function ()
23840     {
23841         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23842
23843         if (this.el.is('input[type=password]') && Roo.isSafari) {
23844             this.el.on('keydown', this.SafariOnKeyDown, this);
23845         }
23846
23847         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23848     },
23849     // private
23850     onRender: function (ct, position)
23851     {
23852         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23853         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23854         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23855
23856         this.trigger.createChild({
23857                    cn: [
23858                     {
23859                     //id: 'PwdMeter',
23860                     tag: 'div',
23861                     cls: 'roo-password-meter-grey col-xs-12',
23862                     style: {
23863                         //width: 0,
23864                         //width: this.meterWidth + 'px'                                                
23865                         }
23866                     },
23867                     {                            
23868                          cls: 'roo-password-meter-text'                          
23869                     }
23870                 ]            
23871         });
23872
23873          
23874         if (this.hideTrigger) {
23875             this.trigger.setDisplayed(false);
23876         }
23877         this.setSize(this.width || '', this.height || '');
23878     },
23879     // private
23880     onDestroy: function ()
23881     {
23882         if (this.trigger) {
23883             this.trigger.removeAllListeners();
23884             this.trigger.remove();
23885         }
23886         if (this.wrap) {
23887             this.wrap.remove();
23888         }
23889         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23890     },
23891     // private
23892     checkStrength: function ()
23893     {
23894         var pwd = this.inputEl().getValue();
23895         if (pwd == this._lastPwd) {
23896             return;
23897         }
23898
23899         var strength;
23900         if (this.ClientSideStrongPassword(pwd)) {
23901             strength = 3;
23902         } else if (this.ClientSideMediumPassword(pwd)) {
23903             strength = 2;
23904         } else if (this.ClientSideWeakPassword(pwd)) {
23905             strength = 1;
23906         } else {
23907             strength = 0;
23908         }
23909         
23910         Roo.log('strength1: ' + strength);
23911         
23912         //var pm = this.trigger.child('div/div/div').dom;
23913         var pm = this.trigger.child('div/div');
23914         pm.removeClass(this.meterClass);
23915         pm.addClass(this.meterClass[strength]);
23916                 
23917         
23918         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23919                 
23920         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23921         
23922         this._lastPwd = pwd;
23923     },
23924     reset: function ()
23925     {
23926         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23927         
23928         this._lastPwd = '';
23929         
23930         var pm = this.trigger.child('div/div');
23931         pm.removeClass(this.meterClass);
23932         pm.addClass('roo-password-meter-grey');        
23933         
23934         
23935         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23936         
23937         pt.innerHTML = '';
23938         this.inputEl().dom.type='password';
23939     },
23940     // private
23941     validateValue: function (value)
23942     {
23943         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23944             return false;
23945         }
23946         if (value.length == 0) {
23947             if (this.allowBlank) {
23948                 this.clearInvalid();
23949                 return true;
23950             }
23951
23952             this.markInvalid(this.errors.PwdEmpty);
23953             this.errorMsg = this.errors.PwdEmpty;
23954             return false;
23955         }
23956         
23957         if(this.insecure){
23958             return true;
23959         }
23960         
23961         if (!value.match(/[\x21-\x7e]+/)) {
23962             this.markInvalid(this.errors.PwdBadChar);
23963             this.errorMsg = this.errors.PwdBadChar;
23964             return false;
23965         }
23966         if (value.length < 6) {
23967             this.markInvalid(this.errors.PwdShort);
23968             this.errorMsg = this.errors.PwdShort;
23969             return false;
23970         }
23971         if (value.length > 16) {
23972             this.markInvalid(this.errors.PwdLong);
23973             this.errorMsg = this.errors.PwdLong;
23974             return false;
23975         }
23976         var strength;
23977         if (this.ClientSideStrongPassword(value)) {
23978             strength = 3;
23979         } else if (this.ClientSideMediumPassword(value)) {
23980             strength = 2;
23981         } else if (this.ClientSideWeakPassword(value)) {
23982             strength = 1;
23983         } else {
23984             strength = 0;
23985         }
23986
23987         
23988         if (strength < 2) {
23989             //this.markInvalid(this.errors.TooWeak);
23990             this.errorMsg = this.errors.TooWeak;
23991             //return false;
23992         }
23993         
23994         
23995         console.log('strength2: ' + strength);
23996         
23997         //var pm = this.trigger.child('div/div/div').dom;
23998         
23999         var pm = this.trigger.child('div/div');
24000         pm.removeClass(this.meterClass);
24001         pm.addClass(this.meterClass[strength]);
24002                 
24003         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24004                 
24005         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24006         
24007         this.errorMsg = ''; 
24008         return true;
24009     },
24010     // private
24011     CharacterSetChecks: function (type)
24012     {
24013         this.type = type;
24014         this.fResult = false;
24015     },
24016     // private
24017     isctype: function (character, type)
24018     {
24019         switch (type) {  
24020             case this.kCapitalLetter:
24021                 if (character >= 'A' && character <= 'Z') {
24022                     return true;
24023                 }
24024                 break;
24025             
24026             case this.kSmallLetter:
24027                 if (character >= 'a' && character <= 'z') {
24028                     return true;
24029                 }
24030                 break;
24031             
24032             case this.kDigit:
24033                 if (character >= '0' && character <= '9') {
24034                     return true;
24035                 }
24036                 break;
24037             
24038             case this.kPunctuation:
24039                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24040                     return true;
24041                 }
24042                 break;
24043             
24044             default:
24045                 return false;
24046         }
24047
24048     },
24049     // private
24050     IsLongEnough: function (pwd, size)
24051     {
24052         return !(pwd == null || isNaN(size) || pwd.length < size);
24053     },
24054     // private
24055     SpansEnoughCharacterSets: function (word, nb)
24056     {
24057         if (!this.IsLongEnough(word, nb))
24058         {
24059             return false;
24060         }
24061
24062         var characterSetChecks = new Array(
24063             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24064             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24065         );
24066         
24067         for (var index = 0; index < word.length; ++index) {
24068             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24069                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24070                     characterSetChecks[nCharSet].fResult = true;
24071                     break;
24072                 }
24073             }
24074         }
24075
24076         var nCharSets = 0;
24077         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24078             if (characterSetChecks[nCharSet].fResult) {
24079                 ++nCharSets;
24080             }
24081         }
24082
24083         if (nCharSets < nb) {
24084             return false;
24085         }
24086         return true;
24087     },
24088     // private
24089     ClientSideStrongPassword: function (pwd)
24090     {
24091         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24092     },
24093     // private
24094     ClientSideMediumPassword: function (pwd)
24095     {
24096         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24097     },
24098     // private
24099     ClientSideWeakPassword: function (pwd)
24100     {
24101         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24102     }
24103           
24104 })//<script type="text/javascript">
24105
24106 /*
24107  * Based  Ext JS Library 1.1.1
24108  * Copyright(c) 2006-2007, Ext JS, LLC.
24109  * LGPL
24110  *
24111  */
24112  
24113 /**
24114  * @class Roo.HtmlEditorCore
24115  * @extends Roo.Component
24116  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24117  *
24118  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24119  */
24120
24121 Roo.HtmlEditorCore = function(config){
24122     
24123     
24124     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24125     
24126     
24127     this.addEvents({
24128         /**
24129          * @event initialize
24130          * Fires when the editor is fully initialized (including the iframe)
24131          * @param {Roo.HtmlEditorCore} this
24132          */
24133         initialize: true,
24134         /**
24135          * @event activate
24136          * Fires when the editor is first receives the focus. Any insertion must wait
24137          * until after this event.
24138          * @param {Roo.HtmlEditorCore} this
24139          */
24140         activate: true,
24141          /**
24142          * @event beforesync
24143          * Fires before the textarea is updated with content from the editor iframe. Return false
24144          * to cancel the sync.
24145          * @param {Roo.HtmlEditorCore} this
24146          * @param {String} html
24147          */
24148         beforesync: true,
24149          /**
24150          * @event beforepush
24151          * Fires before the iframe editor is updated with content from the textarea. Return false
24152          * to cancel the push.
24153          * @param {Roo.HtmlEditorCore} this
24154          * @param {String} html
24155          */
24156         beforepush: true,
24157          /**
24158          * @event sync
24159          * Fires when the textarea is updated with content from the editor iframe.
24160          * @param {Roo.HtmlEditorCore} this
24161          * @param {String} html
24162          */
24163         sync: true,
24164          /**
24165          * @event push
24166          * Fires when the iframe editor is updated with content from the textarea.
24167          * @param {Roo.HtmlEditorCore} this
24168          * @param {String} html
24169          */
24170         push: true,
24171         
24172         /**
24173          * @event editorevent
24174          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24175          * @param {Roo.HtmlEditorCore} this
24176          */
24177         editorevent: true
24178         
24179     });
24180     
24181     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24182     
24183     // defaults : white / black...
24184     this.applyBlacklists();
24185     
24186     
24187     
24188 };
24189
24190
24191 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24192
24193
24194      /**
24195      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24196      */
24197     
24198     owner : false,
24199     
24200      /**
24201      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24202      *                        Roo.resizable.
24203      */
24204     resizable : false,
24205      /**
24206      * @cfg {Number} height (in pixels)
24207      */   
24208     height: 300,
24209    /**
24210      * @cfg {Number} width (in pixels)
24211      */   
24212     width: 500,
24213     
24214     /**
24215      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24216      * 
24217      */
24218     stylesheets: false,
24219     
24220     // id of frame..
24221     frameId: false,
24222     
24223     // private properties
24224     validationEvent : false,
24225     deferHeight: true,
24226     initialized : false,
24227     activated : false,
24228     sourceEditMode : false,
24229     onFocus : Roo.emptyFn,
24230     iframePad:3,
24231     hideMode:'offsets',
24232     
24233     clearUp: true,
24234     
24235     // blacklist + whitelisted elements..
24236     black: false,
24237     white: false,
24238      
24239     bodyCls : '',
24240
24241     /**
24242      * Protected method that will not generally be called directly. It
24243      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24244      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24245      */
24246     getDocMarkup : function(){
24247         // body styles..
24248         var st = '';
24249         
24250         // inherit styels from page...?? 
24251         if (this.stylesheets === false) {
24252             
24253             Roo.get(document.head).select('style').each(function(node) {
24254                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24255             });
24256             
24257             Roo.get(document.head).select('link').each(function(node) { 
24258                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24259             });
24260             
24261         } else if (!this.stylesheets.length) {
24262                 // simple..
24263                 st = '<style type="text/css">' +
24264                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24265                    '</style>';
24266         } else {
24267             for (var i in this.stylesheets) { 
24268                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24269             }
24270             
24271         }
24272         
24273         st +=  '<style type="text/css">' +
24274             'IMG { cursor: pointer } ' +
24275         '</style>';
24276
24277         var cls = 'roo-htmleditor-body';
24278         
24279         if(this.bodyCls.length){
24280             cls += ' ' + this.bodyCls;
24281         }
24282         
24283         return '<html><head>' + st  +
24284             //<style type="text/css">' +
24285             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24286             //'</style>' +
24287             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24288     },
24289
24290     // private
24291     onRender : function(ct, position)
24292     {
24293         var _t = this;
24294         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24295         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24296         
24297         
24298         this.el.dom.style.border = '0 none';
24299         this.el.dom.setAttribute('tabIndex', -1);
24300         this.el.addClass('x-hidden hide');
24301         
24302         
24303         
24304         if(Roo.isIE){ // fix IE 1px bogus margin
24305             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24306         }
24307        
24308         
24309         this.frameId = Roo.id();
24310         
24311          
24312         
24313         var iframe = this.owner.wrap.createChild({
24314             tag: 'iframe',
24315             cls: 'form-control', // bootstrap..
24316             id: this.frameId,
24317             name: this.frameId,
24318             frameBorder : 'no',
24319             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24320         }, this.el
24321         );
24322         
24323         
24324         this.iframe = iframe.dom;
24325
24326          this.assignDocWin();
24327         
24328         this.doc.designMode = 'on';
24329        
24330         this.doc.open();
24331         this.doc.write(this.getDocMarkup());
24332         this.doc.close();
24333
24334         
24335         var task = { // must defer to wait for browser to be ready
24336             run : function(){
24337                 //console.log("run task?" + this.doc.readyState);
24338                 this.assignDocWin();
24339                 if(this.doc.body || this.doc.readyState == 'complete'){
24340                     try {
24341                         this.doc.designMode="on";
24342                     } catch (e) {
24343                         return;
24344                     }
24345                     Roo.TaskMgr.stop(task);
24346                     this.initEditor.defer(10, this);
24347                 }
24348             },
24349             interval : 10,
24350             duration: 10000,
24351             scope: this
24352         };
24353         Roo.TaskMgr.start(task);
24354
24355     },
24356
24357     // private
24358     onResize : function(w, h)
24359     {
24360          Roo.log('resize: ' +w + ',' + h );
24361         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24362         if(!this.iframe){
24363             return;
24364         }
24365         if(typeof w == 'number'){
24366             
24367             this.iframe.style.width = w + 'px';
24368         }
24369         if(typeof h == 'number'){
24370             
24371             this.iframe.style.height = h + 'px';
24372             if(this.doc){
24373                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24374             }
24375         }
24376         
24377     },
24378
24379     /**
24380      * Toggles the editor between standard and source edit mode.
24381      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24382      */
24383     toggleSourceEdit : function(sourceEditMode){
24384         
24385         this.sourceEditMode = sourceEditMode === true;
24386         
24387         if(this.sourceEditMode){
24388  
24389             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24390             
24391         }else{
24392             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24393             //this.iframe.className = '';
24394             this.deferFocus();
24395         }
24396         //this.setSize(this.owner.wrap.getSize());
24397         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24398     },
24399
24400     
24401   
24402
24403     /**
24404      * Protected method that will not generally be called directly. If you need/want
24405      * custom HTML cleanup, this is the method you should override.
24406      * @param {String} html The HTML to be cleaned
24407      * return {String} The cleaned HTML
24408      */
24409     cleanHtml : function(html){
24410         html = String(html);
24411         if(html.length > 5){
24412             if(Roo.isSafari){ // strip safari nonsense
24413                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24414             }
24415         }
24416         if(html == '&nbsp;'){
24417             html = '';
24418         }
24419         return html;
24420     },
24421
24422     /**
24423      * HTML Editor -> Textarea
24424      * Protected method that will not generally be called directly. Syncs the contents
24425      * of the editor iframe with the textarea.
24426      */
24427     syncValue : function(){
24428         if(this.initialized){
24429             var bd = (this.doc.body || this.doc.documentElement);
24430             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24431             var html = bd.innerHTML;
24432             if(Roo.isSafari){
24433                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24434                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24435                 if(m && m[1]){
24436                     html = '<div style="'+m[0]+'">' + html + '</div>';
24437                 }
24438             }
24439             html = this.cleanHtml(html);
24440             // fix up the special chars.. normaly like back quotes in word...
24441             // however we do not want to do this with chinese..
24442             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24443                 
24444                 var cc = match.charCodeAt();
24445
24446                 // Get the character value, handling surrogate pairs
24447                 if (match.length == 2) {
24448                     // It's a surrogate pair, calculate the Unicode code point
24449                     var high = match.charCodeAt(0) - 0xD800;
24450                     var low  = match.charCodeAt(1) - 0xDC00;
24451                     cc = (high * 0x400) + low + 0x10000;
24452                 }  else if (
24453                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24454                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24455                     (cc >= 0xf900 && cc < 0xfb00 )
24456                 ) {
24457                         return match;
24458                 }  
24459          
24460                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24461                 return "&#" + cc + ";";
24462                 
24463                 
24464             });
24465             
24466             
24467              
24468             if(this.owner.fireEvent('beforesync', this, html) !== false){
24469                 this.el.dom.value = html;
24470                 this.owner.fireEvent('sync', this, html);
24471             }
24472         }
24473     },
24474
24475     /**
24476      * Protected method that will not generally be called directly. Pushes the value of the textarea
24477      * into the iframe editor.
24478      */
24479     pushValue : function(){
24480         if(this.initialized){
24481             var v = this.el.dom.value.trim();
24482             
24483 //            if(v.length < 1){
24484 //                v = '&#160;';
24485 //            }
24486             
24487             if(this.owner.fireEvent('beforepush', this, v) !== false){
24488                 var d = (this.doc.body || this.doc.documentElement);
24489                 d.innerHTML = v;
24490                 this.cleanUpPaste();
24491                 this.el.dom.value = d.innerHTML;
24492                 this.owner.fireEvent('push', this, v);
24493             }
24494         }
24495     },
24496
24497     // private
24498     deferFocus : function(){
24499         this.focus.defer(10, this);
24500     },
24501
24502     // doc'ed in Field
24503     focus : function(){
24504         if(this.win && !this.sourceEditMode){
24505             this.win.focus();
24506         }else{
24507             this.el.focus();
24508         }
24509     },
24510     
24511     assignDocWin: function()
24512     {
24513         var iframe = this.iframe;
24514         
24515          if(Roo.isIE){
24516             this.doc = iframe.contentWindow.document;
24517             this.win = iframe.contentWindow;
24518         } else {
24519 //            if (!Roo.get(this.frameId)) {
24520 //                return;
24521 //            }
24522 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24523 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24524             
24525             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24526                 return;
24527             }
24528             
24529             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24530             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24531         }
24532     },
24533     
24534     // private
24535     initEditor : function(){
24536         //console.log("INIT EDITOR");
24537         this.assignDocWin();
24538         
24539         
24540         
24541         this.doc.designMode="on";
24542         this.doc.open();
24543         this.doc.write(this.getDocMarkup());
24544         this.doc.close();
24545         
24546         var dbody = (this.doc.body || this.doc.documentElement);
24547         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24548         // this copies styles from the containing element into thsi one..
24549         // not sure why we need all of this..
24550         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24551         
24552         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24553         //ss['background-attachment'] = 'fixed'; // w3c
24554         dbody.bgProperties = 'fixed'; // ie
24555         //Roo.DomHelper.applyStyles(dbody, ss);
24556         Roo.EventManager.on(this.doc, {
24557             //'mousedown': this.onEditorEvent,
24558             'mouseup': this.onEditorEvent,
24559             'dblclick': this.onEditorEvent,
24560             'click': this.onEditorEvent,
24561             'keyup': this.onEditorEvent,
24562             buffer:100,
24563             scope: this
24564         });
24565         if(Roo.isGecko){
24566             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24567         }
24568         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24569             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24570         }
24571         this.initialized = true;
24572
24573         this.owner.fireEvent('initialize', this);
24574         this.pushValue();
24575     },
24576
24577     // private
24578     onDestroy : function(){
24579         
24580         
24581         
24582         if(this.rendered){
24583             
24584             //for (var i =0; i < this.toolbars.length;i++) {
24585             //    // fixme - ask toolbars for heights?
24586             //    this.toolbars[i].onDestroy();
24587            // }
24588             
24589             //this.wrap.dom.innerHTML = '';
24590             //this.wrap.remove();
24591         }
24592     },
24593
24594     // private
24595     onFirstFocus : function(){
24596         
24597         this.assignDocWin();
24598         
24599         
24600         this.activated = true;
24601          
24602     
24603         if(Roo.isGecko){ // prevent silly gecko errors
24604             this.win.focus();
24605             var s = this.win.getSelection();
24606             if(!s.focusNode || s.focusNode.nodeType != 3){
24607                 var r = s.getRangeAt(0);
24608                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24609                 r.collapse(true);
24610                 this.deferFocus();
24611             }
24612             try{
24613                 this.execCmd('useCSS', true);
24614                 this.execCmd('styleWithCSS', false);
24615             }catch(e){}
24616         }
24617         this.owner.fireEvent('activate', this);
24618     },
24619
24620     // private
24621     adjustFont: function(btn){
24622         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24623         //if(Roo.isSafari){ // safari
24624         //    adjust *= 2;
24625        // }
24626         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24627         if(Roo.isSafari){ // safari
24628             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24629             v =  (v < 10) ? 10 : v;
24630             v =  (v > 48) ? 48 : v;
24631             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24632             
24633         }
24634         
24635         
24636         v = Math.max(1, v+adjust);
24637         
24638         this.execCmd('FontSize', v  );
24639     },
24640
24641     onEditorEvent : function(e)
24642     {
24643         this.owner.fireEvent('editorevent', this, e);
24644       //  this.updateToolbar();
24645         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24646     },
24647
24648     insertTag : function(tg)
24649     {
24650         // could be a bit smarter... -> wrap the current selected tRoo..
24651         if (tg.toLowerCase() == 'span' ||
24652             tg.toLowerCase() == 'code' ||
24653             tg.toLowerCase() == 'sup' ||
24654             tg.toLowerCase() == 'sub' 
24655             ) {
24656             
24657             range = this.createRange(this.getSelection());
24658             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24659             wrappingNode.appendChild(range.extractContents());
24660             range.insertNode(wrappingNode);
24661
24662             return;
24663             
24664             
24665             
24666         }
24667         this.execCmd("formatblock",   tg);
24668         
24669     },
24670     
24671     insertText : function(txt)
24672     {
24673         
24674         
24675         var range = this.createRange();
24676         range.deleteContents();
24677                //alert(Sender.getAttribute('label'));
24678                
24679         range.insertNode(this.doc.createTextNode(txt));
24680     } ,
24681     
24682      
24683
24684     /**
24685      * Executes a Midas editor command on the editor document and performs necessary focus and
24686      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24687      * @param {String} cmd The Midas command
24688      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24689      */
24690     relayCmd : function(cmd, value){
24691         this.win.focus();
24692         this.execCmd(cmd, value);
24693         this.owner.fireEvent('editorevent', this);
24694         //this.updateToolbar();
24695         this.owner.deferFocus();
24696     },
24697
24698     /**
24699      * Executes a Midas editor command directly on the editor document.
24700      * For visual commands, you should use {@link #relayCmd} instead.
24701      * <b>This should only be called after the editor is initialized.</b>
24702      * @param {String} cmd The Midas command
24703      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24704      */
24705     execCmd : function(cmd, value){
24706         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24707         this.syncValue();
24708     },
24709  
24710  
24711    
24712     /**
24713      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24714      * to insert tRoo.
24715      * @param {String} text | dom node.. 
24716      */
24717     insertAtCursor : function(text)
24718     {
24719         
24720         if(!this.activated){
24721             return;
24722         }
24723         /*
24724         if(Roo.isIE){
24725             this.win.focus();
24726             var r = this.doc.selection.createRange();
24727             if(r){
24728                 r.collapse(true);
24729                 r.pasteHTML(text);
24730                 this.syncValue();
24731                 this.deferFocus();
24732             
24733             }
24734             return;
24735         }
24736         */
24737         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24738             this.win.focus();
24739             
24740             
24741             // from jquery ui (MIT licenced)
24742             var range, node;
24743             var win = this.win;
24744             
24745             if (win.getSelection && win.getSelection().getRangeAt) {
24746                 range = win.getSelection().getRangeAt(0);
24747                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24748                 range.insertNode(node);
24749             } else if (win.document.selection && win.document.selection.createRange) {
24750                 // no firefox support
24751                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24752                 win.document.selection.createRange().pasteHTML(txt);
24753             } else {
24754                 // no firefox support
24755                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24756                 this.execCmd('InsertHTML', txt);
24757             } 
24758             
24759             this.syncValue();
24760             
24761             this.deferFocus();
24762         }
24763     },
24764  // private
24765     mozKeyPress : function(e){
24766         if(e.ctrlKey){
24767             var c = e.getCharCode(), cmd;
24768           
24769             if(c > 0){
24770                 c = String.fromCharCode(c).toLowerCase();
24771                 switch(c){
24772                     case 'b':
24773                         cmd = 'bold';
24774                         break;
24775                     case 'i':
24776                         cmd = 'italic';
24777                         break;
24778                     
24779                     case 'u':
24780                         cmd = 'underline';
24781                         break;
24782                     
24783                     case 'v':
24784                         this.cleanUpPaste.defer(100, this);
24785                         return;
24786                         
24787                 }
24788                 if(cmd){
24789                     this.win.focus();
24790                     this.execCmd(cmd);
24791                     this.deferFocus();
24792                     e.preventDefault();
24793                 }
24794                 
24795             }
24796         }
24797     },
24798
24799     // private
24800     fixKeys : function(){ // load time branching for fastest keydown performance
24801         if(Roo.isIE){
24802             return function(e){
24803                 var k = e.getKey(), r;
24804                 if(k == e.TAB){
24805                     e.stopEvent();
24806                     r = this.doc.selection.createRange();
24807                     if(r){
24808                         r.collapse(true);
24809                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24810                         this.deferFocus();
24811                     }
24812                     return;
24813                 }
24814                 
24815                 if(k == e.ENTER){
24816                     r = this.doc.selection.createRange();
24817                     if(r){
24818                         var target = r.parentElement();
24819                         if(!target || target.tagName.toLowerCase() != 'li'){
24820                             e.stopEvent();
24821                             r.pasteHTML('<br />');
24822                             r.collapse(false);
24823                             r.select();
24824                         }
24825                     }
24826                 }
24827                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24828                     this.cleanUpPaste.defer(100, this);
24829                     return;
24830                 }
24831                 
24832                 
24833             };
24834         }else if(Roo.isOpera){
24835             return function(e){
24836                 var k = e.getKey();
24837                 if(k == e.TAB){
24838                     e.stopEvent();
24839                     this.win.focus();
24840                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24841                     this.deferFocus();
24842                 }
24843                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24844                     this.cleanUpPaste.defer(100, this);
24845                     return;
24846                 }
24847                 
24848             };
24849         }else if(Roo.isSafari){
24850             return function(e){
24851                 var k = e.getKey();
24852                 
24853                 if(k == e.TAB){
24854                     e.stopEvent();
24855                     this.execCmd('InsertText','\t');
24856                     this.deferFocus();
24857                     return;
24858                 }
24859                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24860                     this.cleanUpPaste.defer(100, this);
24861                     return;
24862                 }
24863                 
24864              };
24865         }
24866     }(),
24867     
24868     getAllAncestors: function()
24869     {
24870         var p = this.getSelectedNode();
24871         var a = [];
24872         if (!p) {
24873             a.push(p); // push blank onto stack..
24874             p = this.getParentElement();
24875         }
24876         
24877         
24878         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24879             a.push(p);
24880             p = p.parentNode;
24881         }
24882         a.push(this.doc.body);
24883         return a;
24884     },
24885     lastSel : false,
24886     lastSelNode : false,
24887     
24888     
24889     getSelection : function() 
24890     {
24891         this.assignDocWin();
24892         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24893     },
24894     
24895     getSelectedNode: function() 
24896     {
24897         // this may only work on Gecko!!!
24898         
24899         // should we cache this!!!!
24900         
24901         
24902         
24903          
24904         var range = this.createRange(this.getSelection()).cloneRange();
24905         
24906         if (Roo.isIE) {
24907             var parent = range.parentElement();
24908             while (true) {
24909                 var testRange = range.duplicate();
24910                 testRange.moveToElementText(parent);
24911                 if (testRange.inRange(range)) {
24912                     break;
24913                 }
24914                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24915                     break;
24916                 }
24917                 parent = parent.parentElement;
24918             }
24919             return parent;
24920         }
24921         
24922         // is ancestor a text element.
24923         var ac =  range.commonAncestorContainer;
24924         if (ac.nodeType == 3) {
24925             ac = ac.parentNode;
24926         }
24927         
24928         var ar = ac.childNodes;
24929          
24930         var nodes = [];
24931         var other_nodes = [];
24932         var has_other_nodes = false;
24933         for (var i=0;i<ar.length;i++) {
24934             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24935                 continue;
24936             }
24937             // fullly contained node.
24938             
24939             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24940                 nodes.push(ar[i]);
24941                 continue;
24942             }
24943             
24944             // probably selected..
24945             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24946                 other_nodes.push(ar[i]);
24947                 continue;
24948             }
24949             // outer..
24950             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24951                 continue;
24952             }
24953             
24954             
24955             has_other_nodes = true;
24956         }
24957         if (!nodes.length && other_nodes.length) {
24958             nodes= other_nodes;
24959         }
24960         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24961             return false;
24962         }
24963         
24964         return nodes[0];
24965     },
24966     createRange: function(sel)
24967     {
24968         // this has strange effects when using with 
24969         // top toolbar - not sure if it's a great idea.
24970         //this.editor.contentWindow.focus();
24971         if (typeof sel != "undefined") {
24972             try {
24973                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24974             } catch(e) {
24975                 return this.doc.createRange();
24976             }
24977         } else {
24978             return this.doc.createRange();
24979         }
24980     },
24981     getParentElement: function()
24982     {
24983         
24984         this.assignDocWin();
24985         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24986         
24987         var range = this.createRange(sel);
24988          
24989         try {
24990             var p = range.commonAncestorContainer;
24991             while (p.nodeType == 3) { // text node
24992                 p = p.parentNode;
24993             }
24994             return p;
24995         } catch (e) {
24996             return null;
24997         }
24998     
24999     },
25000     /***
25001      *
25002      * Range intersection.. the hard stuff...
25003      *  '-1' = before
25004      *  '0' = hits..
25005      *  '1' = after.
25006      *         [ -- selected range --- ]
25007      *   [fail]                        [fail]
25008      *
25009      *    basically..
25010      *      if end is before start or  hits it. fail.
25011      *      if start is after end or hits it fail.
25012      *
25013      *   if either hits (but other is outside. - then it's not 
25014      *   
25015      *    
25016      **/
25017     
25018     
25019     // @see http://www.thismuchiknow.co.uk/?p=64.
25020     rangeIntersectsNode : function(range, node)
25021     {
25022         var nodeRange = node.ownerDocument.createRange();
25023         try {
25024             nodeRange.selectNode(node);
25025         } catch (e) {
25026             nodeRange.selectNodeContents(node);
25027         }
25028     
25029         var rangeStartRange = range.cloneRange();
25030         rangeStartRange.collapse(true);
25031     
25032         var rangeEndRange = range.cloneRange();
25033         rangeEndRange.collapse(false);
25034     
25035         var nodeStartRange = nodeRange.cloneRange();
25036         nodeStartRange.collapse(true);
25037     
25038         var nodeEndRange = nodeRange.cloneRange();
25039         nodeEndRange.collapse(false);
25040     
25041         return rangeStartRange.compareBoundaryPoints(
25042                  Range.START_TO_START, nodeEndRange) == -1 &&
25043                rangeEndRange.compareBoundaryPoints(
25044                  Range.START_TO_START, nodeStartRange) == 1;
25045         
25046          
25047     },
25048     rangeCompareNode : function(range, node)
25049     {
25050         var nodeRange = node.ownerDocument.createRange();
25051         try {
25052             nodeRange.selectNode(node);
25053         } catch (e) {
25054             nodeRange.selectNodeContents(node);
25055         }
25056         
25057         
25058         range.collapse(true);
25059     
25060         nodeRange.collapse(true);
25061      
25062         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25063         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25064          
25065         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25066         
25067         var nodeIsBefore   =  ss == 1;
25068         var nodeIsAfter    = ee == -1;
25069         
25070         if (nodeIsBefore && nodeIsAfter) {
25071             return 0; // outer
25072         }
25073         if (!nodeIsBefore && nodeIsAfter) {
25074             return 1; //right trailed.
25075         }
25076         
25077         if (nodeIsBefore && !nodeIsAfter) {
25078             return 2;  // left trailed.
25079         }
25080         // fully contined.
25081         return 3;
25082     },
25083
25084     // private? - in a new class?
25085     cleanUpPaste :  function()
25086     {
25087         // cleans up the whole document..
25088         Roo.log('cleanuppaste');
25089         
25090         this.cleanUpChildren(this.doc.body);
25091         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25092         if (clean != this.doc.body.innerHTML) {
25093             this.doc.body.innerHTML = clean;
25094         }
25095         
25096     },
25097     
25098     cleanWordChars : function(input) {// change the chars to hex code
25099         var he = Roo.HtmlEditorCore;
25100         
25101         var output = input;
25102         Roo.each(he.swapCodes, function(sw) { 
25103             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25104             
25105             output = output.replace(swapper, sw[1]);
25106         });
25107         
25108         return output;
25109     },
25110     
25111     
25112     cleanUpChildren : function (n)
25113     {
25114         if (!n.childNodes.length) {
25115             return;
25116         }
25117         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25118            this.cleanUpChild(n.childNodes[i]);
25119         }
25120     },
25121     
25122     
25123         
25124     
25125     cleanUpChild : function (node)
25126     {
25127         var ed = this;
25128         //console.log(node);
25129         if (node.nodeName == "#text") {
25130             // clean up silly Windows -- stuff?
25131             return; 
25132         }
25133         if (node.nodeName == "#comment") {
25134             node.parentNode.removeChild(node);
25135             // clean up silly Windows -- stuff?
25136             return; 
25137         }
25138         var lcname = node.tagName.toLowerCase();
25139         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25140         // whitelist of tags..
25141         
25142         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25143             // remove node.
25144             node.parentNode.removeChild(node);
25145             return;
25146             
25147         }
25148         
25149         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25150         
25151         // spans with no attributes - just remove them..
25152         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25153             remove_keep_children = true;
25154         }
25155         
25156         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25157         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25158         
25159         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25160         //    remove_keep_children = true;
25161         //}
25162         
25163         if (remove_keep_children) {
25164             this.cleanUpChildren(node);
25165             // inserts everything just before this node...
25166             while (node.childNodes.length) {
25167                 var cn = node.childNodes[0];
25168                 node.removeChild(cn);
25169                 node.parentNode.insertBefore(cn, node);
25170             }
25171             node.parentNode.removeChild(node);
25172             return;
25173         }
25174         
25175         if (!node.attributes || !node.attributes.length) {
25176             
25177           
25178             
25179             
25180             this.cleanUpChildren(node);
25181             return;
25182         }
25183         
25184         function cleanAttr(n,v)
25185         {
25186             
25187             if (v.match(/^\./) || v.match(/^\//)) {
25188                 return;
25189             }
25190             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25191                 return;
25192             }
25193             if (v.match(/^#/)) {
25194                 return;
25195             }
25196             if (v.match(/^\{/)) { // allow template editing.
25197                 return;
25198             }
25199 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25200             node.removeAttribute(n);
25201             
25202         }
25203         
25204         var cwhite = this.cwhite;
25205         var cblack = this.cblack;
25206             
25207         function cleanStyle(n,v)
25208         {
25209             if (v.match(/expression/)) { //XSS?? should we even bother..
25210                 node.removeAttribute(n);
25211                 return;
25212             }
25213             
25214             var parts = v.split(/;/);
25215             var clean = [];
25216             
25217             Roo.each(parts, function(p) {
25218                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25219                 if (!p.length) {
25220                     return true;
25221                 }
25222                 var l = p.split(':').shift().replace(/\s+/g,'');
25223                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25224                 
25225                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25226 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25227                     //node.removeAttribute(n);
25228                     return true;
25229                 }
25230                 //Roo.log()
25231                 // only allow 'c whitelisted system attributes'
25232                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25233 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25234                     //node.removeAttribute(n);
25235                     return true;
25236                 }
25237                 
25238                 
25239                  
25240                 
25241                 clean.push(p);
25242                 return true;
25243             });
25244             if (clean.length) { 
25245                 node.setAttribute(n, clean.join(';'));
25246             } else {
25247                 node.removeAttribute(n);
25248             }
25249             
25250         }
25251         
25252         
25253         for (var i = node.attributes.length-1; i > -1 ; i--) {
25254             var a = node.attributes[i];
25255             //console.log(a);
25256             
25257             if (a.name.toLowerCase().substr(0,2)=='on')  {
25258                 node.removeAttribute(a.name);
25259                 continue;
25260             }
25261             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25262                 node.removeAttribute(a.name);
25263                 continue;
25264             }
25265             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25266                 cleanAttr(a.name,a.value); // fixme..
25267                 continue;
25268             }
25269             if (a.name == 'style') {
25270                 cleanStyle(a.name,a.value);
25271                 continue;
25272             }
25273             /// clean up MS crap..
25274             // tecnically this should be a list of valid class'es..
25275             
25276             
25277             if (a.name == 'class') {
25278                 if (a.value.match(/^Mso/)) {
25279                     node.removeAttribute('class');
25280                 }
25281                 
25282                 if (a.value.match(/^body$/)) {
25283                     node.removeAttribute('class');
25284                 }
25285                 continue;
25286             }
25287             
25288             // style cleanup!?
25289             // class cleanup?
25290             
25291         }
25292         
25293         
25294         this.cleanUpChildren(node);
25295         
25296         
25297     },
25298     
25299     /**
25300      * Clean up MS wordisms...
25301      */
25302     cleanWord : function(node)
25303     {
25304         if (!node) {
25305             this.cleanWord(this.doc.body);
25306             return;
25307         }
25308         
25309         if(
25310                 node.nodeName == 'SPAN' &&
25311                 !node.hasAttributes() &&
25312                 node.childNodes.length == 1 &&
25313                 node.firstChild.nodeName == "#text"  
25314         ) {
25315             var textNode = node.firstChild;
25316             node.removeChild(textNode);
25317             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25318                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25319             }
25320             node.parentNode.insertBefore(textNode, node);
25321             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25322                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25323             }
25324             node.parentNode.removeChild(node);
25325         }
25326         
25327         if (node.nodeName == "#text") {
25328             // clean up silly Windows -- stuff?
25329             return; 
25330         }
25331         if (node.nodeName == "#comment") {
25332             node.parentNode.removeChild(node);
25333             // clean up silly Windows -- stuff?
25334             return; 
25335         }
25336         
25337         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25338             node.parentNode.removeChild(node);
25339             return;
25340         }
25341         //Roo.log(node.tagName);
25342         // remove - but keep children..
25343         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25344             //Roo.log('-- removed');
25345             while (node.childNodes.length) {
25346                 var cn = node.childNodes[0];
25347                 node.removeChild(cn);
25348                 node.parentNode.insertBefore(cn, node);
25349                 // move node to parent - and clean it..
25350                 this.cleanWord(cn);
25351             }
25352             node.parentNode.removeChild(node);
25353             /// no need to iterate chidlren = it's got none..
25354             //this.iterateChildren(node, this.cleanWord);
25355             return;
25356         }
25357         // clean styles
25358         if (node.className.length) {
25359             
25360             var cn = node.className.split(/\W+/);
25361             var cna = [];
25362             Roo.each(cn, function(cls) {
25363                 if (cls.match(/Mso[a-zA-Z]+/)) {
25364                     return;
25365                 }
25366                 cna.push(cls);
25367             });
25368             node.className = cna.length ? cna.join(' ') : '';
25369             if (!cna.length) {
25370                 node.removeAttribute("class");
25371             }
25372         }
25373         
25374         if (node.hasAttribute("lang")) {
25375             node.removeAttribute("lang");
25376         }
25377         
25378         if (node.hasAttribute("style")) {
25379             
25380             var styles = node.getAttribute("style").split(";");
25381             var nstyle = [];
25382             Roo.each(styles, function(s) {
25383                 if (!s.match(/:/)) {
25384                     return;
25385                 }
25386                 var kv = s.split(":");
25387                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25388                     return;
25389                 }
25390                 // what ever is left... we allow.
25391                 nstyle.push(s);
25392             });
25393             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25394             if (!nstyle.length) {
25395                 node.removeAttribute('style');
25396             }
25397         }
25398         this.iterateChildren(node, this.cleanWord);
25399         
25400         
25401         
25402     },
25403     /**
25404      * iterateChildren of a Node, calling fn each time, using this as the scole..
25405      * @param {DomNode} node node to iterate children of.
25406      * @param {Function} fn method of this class to call on each item.
25407      */
25408     iterateChildren : function(node, fn)
25409     {
25410         if (!node.childNodes.length) {
25411                 return;
25412         }
25413         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25414            fn.call(this, node.childNodes[i])
25415         }
25416     },
25417     
25418     
25419     /**
25420      * cleanTableWidths.
25421      *
25422      * Quite often pasting from word etc.. results in tables with column and widths.
25423      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25424      *
25425      */
25426     cleanTableWidths : function(node)
25427     {
25428          
25429          
25430         if (!node) {
25431             this.cleanTableWidths(this.doc.body);
25432             return;
25433         }
25434         
25435         // ignore list...
25436         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25437             return; 
25438         }
25439         Roo.log(node.tagName);
25440         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25441             this.iterateChildren(node, this.cleanTableWidths);
25442             return;
25443         }
25444         if (node.hasAttribute('width')) {
25445             node.removeAttribute('width');
25446         }
25447         
25448          
25449         if (node.hasAttribute("style")) {
25450             // pretty basic...
25451             
25452             var styles = node.getAttribute("style").split(";");
25453             var nstyle = [];
25454             Roo.each(styles, function(s) {
25455                 if (!s.match(/:/)) {
25456                     return;
25457                 }
25458                 var kv = s.split(":");
25459                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25460                     return;
25461                 }
25462                 // what ever is left... we allow.
25463                 nstyle.push(s);
25464             });
25465             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25466             if (!nstyle.length) {
25467                 node.removeAttribute('style');
25468             }
25469         }
25470         
25471         this.iterateChildren(node, this.cleanTableWidths);
25472         
25473         
25474     },
25475     
25476     
25477     
25478     
25479     domToHTML : function(currentElement, depth, nopadtext) {
25480         
25481         depth = depth || 0;
25482         nopadtext = nopadtext || false;
25483     
25484         if (!currentElement) {
25485             return this.domToHTML(this.doc.body);
25486         }
25487         
25488         //Roo.log(currentElement);
25489         var j;
25490         var allText = false;
25491         var nodeName = currentElement.nodeName;
25492         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25493         
25494         if  (nodeName == '#text') {
25495             
25496             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25497         }
25498         
25499         
25500         var ret = '';
25501         if (nodeName != 'BODY') {
25502              
25503             var i = 0;
25504             // Prints the node tagName, such as <A>, <IMG>, etc
25505             if (tagName) {
25506                 var attr = [];
25507                 for(i = 0; i < currentElement.attributes.length;i++) {
25508                     // quoting?
25509                     var aname = currentElement.attributes.item(i).name;
25510                     if (!currentElement.attributes.item(i).value.length) {
25511                         continue;
25512                     }
25513                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25514                 }
25515                 
25516                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25517             } 
25518             else {
25519                 
25520                 // eack
25521             }
25522         } else {
25523             tagName = false;
25524         }
25525         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25526             return ret;
25527         }
25528         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25529             nopadtext = true;
25530         }
25531         
25532         
25533         // Traverse the tree
25534         i = 0;
25535         var currentElementChild = currentElement.childNodes.item(i);
25536         var allText = true;
25537         var innerHTML  = '';
25538         lastnode = '';
25539         while (currentElementChild) {
25540             // Formatting code (indent the tree so it looks nice on the screen)
25541             var nopad = nopadtext;
25542             if (lastnode == 'SPAN') {
25543                 nopad  = true;
25544             }
25545             // text
25546             if  (currentElementChild.nodeName == '#text') {
25547                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25548                 toadd = nopadtext ? toadd : toadd.trim();
25549                 if (!nopad && toadd.length > 80) {
25550                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25551                 }
25552                 innerHTML  += toadd;
25553                 
25554                 i++;
25555                 currentElementChild = currentElement.childNodes.item(i);
25556                 lastNode = '';
25557                 continue;
25558             }
25559             allText = false;
25560             
25561             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25562                 
25563             // Recursively traverse the tree structure of the child node
25564             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25565             lastnode = currentElementChild.nodeName;
25566             i++;
25567             currentElementChild=currentElement.childNodes.item(i);
25568         }
25569         
25570         ret += innerHTML;
25571         
25572         if (!allText) {
25573                 // The remaining code is mostly for formatting the tree
25574             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25575         }
25576         
25577         
25578         if (tagName) {
25579             ret+= "</"+tagName+">";
25580         }
25581         return ret;
25582         
25583     },
25584         
25585     applyBlacklists : function()
25586     {
25587         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25588         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25589         
25590         this.white = [];
25591         this.black = [];
25592         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25593             if (b.indexOf(tag) > -1) {
25594                 return;
25595             }
25596             this.white.push(tag);
25597             
25598         }, this);
25599         
25600         Roo.each(w, function(tag) {
25601             if (b.indexOf(tag) > -1) {
25602                 return;
25603             }
25604             if (this.white.indexOf(tag) > -1) {
25605                 return;
25606             }
25607             this.white.push(tag);
25608             
25609         }, this);
25610         
25611         
25612         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25613             if (w.indexOf(tag) > -1) {
25614                 return;
25615             }
25616             this.black.push(tag);
25617             
25618         }, this);
25619         
25620         Roo.each(b, function(tag) {
25621             if (w.indexOf(tag) > -1) {
25622                 return;
25623             }
25624             if (this.black.indexOf(tag) > -1) {
25625                 return;
25626             }
25627             this.black.push(tag);
25628             
25629         }, this);
25630         
25631         
25632         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25633         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25634         
25635         this.cwhite = [];
25636         this.cblack = [];
25637         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25638             if (b.indexOf(tag) > -1) {
25639                 return;
25640             }
25641             this.cwhite.push(tag);
25642             
25643         }, this);
25644         
25645         Roo.each(w, function(tag) {
25646             if (b.indexOf(tag) > -1) {
25647                 return;
25648             }
25649             if (this.cwhite.indexOf(tag) > -1) {
25650                 return;
25651             }
25652             this.cwhite.push(tag);
25653             
25654         }, this);
25655         
25656         
25657         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25658             if (w.indexOf(tag) > -1) {
25659                 return;
25660             }
25661             this.cblack.push(tag);
25662             
25663         }, this);
25664         
25665         Roo.each(b, function(tag) {
25666             if (w.indexOf(tag) > -1) {
25667                 return;
25668             }
25669             if (this.cblack.indexOf(tag) > -1) {
25670                 return;
25671             }
25672             this.cblack.push(tag);
25673             
25674         }, this);
25675     },
25676     
25677     setStylesheets : function(stylesheets)
25678     {
25679         if(typeof(stylesheets) == 'string'){
25680             Roo.get(this.iframe.contentDocument.head).createChild({
25681                 tag : 'link',
25682                 rel : 'stylesheet',
25683                 type : 'text/css',
25684                 href : stylesheets
25685             });
25686             
25687             return;
25688         }
25689         var _this = this;
25690      
25691         Roo.each(stylesheets, function(s) {
25692             if(!s.length){
25693                 return;
25694             }
25695             
25696             Roo.get(_this.iframe.contentDocument.head).createChild({
25697                 tag : 'link',
25698                 rel : 'stylesheet',
25699                 type : 'text/css',
25700                 href : s
25701             });
25702         });
25703
25704         
25705     },
25706     
25707     removeStylesheets : function()
25708     {
25709         var _this = this;
25710         
25711         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25712             s.remove();
25713         });
25714     },
25715     
25716     setStyle : function(style)
25717     {
25718         Roo.get(this.iframe.contentDocument.head).createChild({
25719             tag : 'style',
25720             type : 'text/css',
25721             html : style
25722         });
25723
25724         return;
25725     }
25726     
25727     // hide stuff that is not compatible
25728     /**
25729      * @event blur
25730      * @hide
25731      */
25732     /**
25733      * @event change
25734      * @hide
25735      */
25736     /**
25737      * @event focus
25738      * @hide
25739      */
25740     /**
25741      * @event specialkey
25742      * @hide
25743      */
25744     /**
25745      * @cfg {String} fieldClass @hide
25746      */
25747     /**
25748      * @cfg {String} focusClass @hide
25749      */
25750     /**
25751      * @cfg {String} autoCreate @hide
25752      */
25753     /**
25754      * @cfg {String} inputType @hide
25755      */
25756     /**
25757      * @cfg {String} invalidClass @hide
25758      */
25759     /**
25760      * @cfg {String} invalidText @hide
25761      */
25762     /**
25763      * @cfg {String} msgFx @hide
25764      */
25765     /**
25766      * @cfg {String} validateOnBlur @hide
25767      */
25768 });
25769
25770 Roo.HtmlEditorCore.white = [
25771         'area', 'br', 'img', 'input', 'hr', 'wbr',
25772         
25773        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25774        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25775        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25776        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25777        'table',   'ul',         'xmp', 
25778        
25779        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25780       'thead',   'tr', 
25781      
25782       'dir', 'menu', 'ol', 'ul', 'dl',
25783        
25784       'embed',  'object'
25785 ];
25786
25787
25788 Roo.HtmlEditorCore.black = [
25789     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25790         'applet', // 
25791         'base',   'basefont', 'bgsound', 'blink',  'body', 
25792         'frame',  'frameset', 'head',    'html',   'ilayer', 
25793         'iframe', 'layer',  'link',     'meta',    'object',   
25794         'script', 'style' ,'title',  'xml' // clean later..
25795 ];
25796 Roo.HtmlEditorCore.clean = [
25797     'script', 'style', 'title', 'xml'
25798 ];
25799 Roo.HtmlEditorCore.remove = [
25800     'font'
25801 ];
25802 // attributes..
25803
25804 Roo.HtmlEditorCore.ablack = [
25805     'on'
25806 ];
25807     
25808 Roo.HtmlEditorCore.aclean = [ 
25809     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25810 ];
25811
25812 // protocols..
25813 Roo.HtmlEditorCore.pwhite= [
25814         'http',  'https',  'mailto'
25815 ];
25816
25817 // white listed style attributes.
25818 Roo.HtmlEditorCore.cwhite= [
25819       //  'text-align', /// default is to allow most things..
25820       
25821          
25822 //        'font-size'//??
25823 ];
25824
25825 // black listed style attributes.
25826 Roo.HtmlEditorCore.cblack= [
25827       //  'font-size' -- this can be set by the project 
25828 ];
25829
25830
25831 Roo.HtmlEditorCore.swapCodes   =[ 
25832     [    8211, "--" ], 
25833     [    8212, "--" ], 
25834     [    8216,  "'" ],  
25835     [    8217, "'" ],  
25836     [    8220, '"' ],  
25837     [    8221, '"' ],  
25838     [    8226, "*" ],  
25839     [    8230, "..." ]
25840 ]; 
25841
25842     /*
25843  * - LGPL
25844  *
25845  * HtmlEditor
25846  * 
25847  */
25848
25849 /**
25850  * @class Roo.bootstrap.HtmlEditor
25851  * @extends Roo.bootstrap.TextArea
25852  * Bootstrap HtmlEditor class
25853
25854  * @constructor
25855  * Create a new HtmlEditor
25856  * @param {Object} config The config object
25857  */
25858
25859 Roo.bootstrap.HtmlEditor = function(config){
25860     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25861     if (!this.toolbars) {
25862         this.toolbars = [];
25863     }
25864     
25865     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25866     this.addEvents({
25867             /**
25868              * @event initialize
25869              * Fires when the editor is fully initialized (including the iframe)
25870              * @param {HtmlEditor} this
25871              */
25872             initialize: true,
25873             /**
25874              * @event activate
25875              * Fires when the editor is first receives the focus. Any insertion must wait
25876              * until after this event.
25877              * @param {HtmlEditor} this
25878              */
25879             activate: true,
25880              /**
25881              * @event beforesync
25882              * Fires before the textarea is updated with content from the editor iframe. Return false
25883              * to cancel the sync.
25884              * @param {HtmlEditor} this
25885              * @param {String} html
25886              */
25887             beforesync: true,
25888              /**
25889              * @event beforepush
25890              * Fires before the iframe editor is updated with content from the textarea. Return false
25891              * to cancel the push.
25892              * @param {HtmlEditor} this
25893              * @param {String} html
25894              */
25895             beforepush: true,
25896              /**
25897              * @event sync
25898              * Fires when the textarea is updated with content from the editor iframe.
25899              * @param {HtmlEditor} this
25900              * @param {String} html
25901              */
25902             sync: true,
25903              /**
25904              * @event push
25905              * Fires when the iframe editor is updated with content from the textarea.
25906              * @param {HtmlEditor} this
25907              * @param {String} html
25908              */
25909             push: true,
25910              /**
25911              * @event editmodechange
25912              * Fires when the editor switches edit modes
25913              * @param {HtmlEditor} this
25914              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25915              */
25916             editmodechange: true,
25917             /**
25918              * @event editorevent
25919              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25920              * @param {HtmlEditor} this
25921              */
25922             editorevent: true,
25923             /**
25924              * @event firstfocus
25925              * Fires when on first focus - needed by toolbars..
25926              * @param {HtmlEditor} this
25927              */
25928             firstfocus: true,
25929             /**
25930              * @event autosave
25931              * Auto save the htmlEditor value as a file into Events
25932              * @param {HtmlEditor} this
25933              */
25934             autosave: true,
25935             /**
25936              * @event savedpreview
25937              * preview the saved version of htmlEditor
25938              * @param {HtmlEditor} this
25939              */
25940             savedpreview: true
25941         });
25942 };
25943
25944
25945 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25946     
25947     
25948       /**
25949      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25950      */
25951     toolbars : false,
25952     
25953      /**
25954     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25955     */
25956     btns : [],
25957    
25958      /**
25959      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25960      *                        Roo.resizable.
25961      */
25962     resizable : false,
25963      /**
25964      * @cfg {Number} height (in pixels)
25965      */   
25966     height: 300,
25967    /**
25968      * @cfg {Number} width (in pixels)
25969      */   
25970     width: false,
25971     
25972     /**
25973      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25974      * 
25975      */
25976     stylesheets: false,
25977     
25978     // id of frame..
25979     frameId: false,
25980     
25981     // private properties
25982     validationEvent : false,
25983     deferHeight: true,
25984     initialized : false,
25985     activated : false,
25986     
25987     onFocus : Roo.emptyFn,
25988     iframePad:3,
25989     hideMode:'offsets',
25990     
25991     tbContainer : false,
25992     
25993     bodyCls : '',
25994     
25995     toolbarContainer :function() {
25996         return this.wrap.select('.x-html-editor-tb',true).first();
25997     },
25998
25999     /**
26000      * Protected method that will not generally be called directly. It
26001      * is called when the editor creates its toolbar. Override this method if you need to
26002      * add custom toolbar buttons.
26003      * @param {HtmlEditor} editor
26004      */
26005     createToolbar : function(){
26006         Roo.log('renewing');
26007         Roo.log("create toolbars");
26008         
26009         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26010         this.toolbars[0].render(this.toolbarContainer());
26011         
26012         return;
26013         
26014 //        if (!editor.toolbars || !editor.toolbars.length) {
26015 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26016 //        }
26017 //        
26018 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26019 //            editor.toolbars[i] = Roo.factory(
26020 //                    typeof(editor.toolbars[i]) == 'string' ?
26021 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26022 //                Roo.bootstrap.HtmlEditor);
26023 //            editor.toolbars[i].init(editor);
26024 //        }
26025     },
26026
26027      
26028     // private
26029     onRender : function(ct, position)
26030     {
26031        // Roo.log("Call onRender: " + this.xtype);
26032         var _t = this;
26033         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26034       
26035         this.wrap = this.inputEl().wrap({
26036             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26037         });
26038         
26039         this.editorcore.onRender(ct, position);
26040          
26041         if (this.resizable) {
26042             this.resizeEl = new Roo.Resizable(this.wrap, {
26043                 pinned : true,
26044                 wrap: true,
26045                 dynamic : true,
26046                 minHeight : this.height,
26047                 height: this.height,
26048                 handles : this.resizable,
26049                 width: this.width,
26050                 listeners : {
26051                     resize : function(r, w, h) {
26052                         _t.onResize(w,h); // -something
26053                     }
26054                 }
26055             });
26056             
26057         }
26058         this.createToolbar(this);
26059        
26060         
26061         if(!this.width && this.resizable){
26062             this.setSize(this.wrap.getSize());
26063         }
26064         if (this.resizeEl) {
26065             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26066             // should trigger onReize..
26067         }
26068         
26069     },
26070
26071     // private
26072     onResize : function(w, h)
26073     {
26074         Roo.log('resize: ' +w + ',' + h );
26075         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26076         var ew = false;
26077         var eh = false;
26078         
26079         if(this.inputEl() ){
26080             if(typeof w == 'number'){
26081                 var aw = w - this.wrap.getFrameWidth('lr');
26082                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26083                 ew = aw;
26084             }
26085             if(typeof h == 'number'){
26086                  var tbh = -11;  // fixme it needs to tool bar size!
26087                 for (var i =0; i < this.toolbars.length;i++) {
26088                     // fixme - ask toolbars for heights?
26089                     tbh += this.toolbars[i].el.getHeight();
26090                     //if (this.toolbars[i].footer) {
26091                     //    tbh += this.toolbars[i].footer.el.getHeight();
26092                     //}
26093                 }
26094               
26095                 
26096                 
26097                 
26098                 
26099                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26100                 ah -= 5; // knock a few pixes off for look..
26101                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26102                 var eh = ah;
26103             }
26104         }
26105         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26106         this.editorcore.onResize(ew,eh);
26107         
26108     },
26109
26110     /**
26111      * Toggles the editor between standard and source edit mode.
26112      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26113      */
26114     toggleSourceEdit : function(sourceEditMode)
26115     {
26116         this.editorcore.toggleSourceEdit(sourceEditMode);
26117         
26118         if(this.editorcore.sourceEditMode){
26119             Roo.log('editor - showing textarea');
26120             
26121 //            Roo.log('in');
26122 //            Roo.log(this.syncValue());
26123             this.syncValue();
26124             this.inputEl().removeClass(['hide', 'x-hidden']);
26125             this.inputEl().dom.removeAttribute('tabIndex');
26126             this.inputEl().focus();
26127         }else{
26128             Roo.log('editor - hiding textarea');
26129 //            Roo.log('out')
26130 //            Roo.log(this.pushValue()); 
26131             this.pushValue();
26132             
26133             this.inputEl().addClass(['hide', 'x-hidden']);
26134             this.inputEl().dom.setAttribute('tabIndex', -1);
26135             //this.deferFocus();
26136         }
26137          
26138         if(this.resizable){
26139             this.setSize(this.wrap.getSize());
26140         }
26141         
26142         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26143     },
26144  
26145     // private (for BoxComponent)
26146     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26147
26148     // private (for BoxComponent)
26149     getResizeEl : function(){
26150         return this.wrap;
26151     },
26152
26153     // private (for BoxComponent)
26154     getPositionEl : function(){
26155         return this.wrap;
26156     },
26157
26158     // private
26159     initEvents : function(){
26160         this.originalValue = this.getValue();
26161     },
26162
26163 //    /**
26164 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26165 //     * @method
26166 //     */
26167 //    markInvalid : Roo.emptyFn,
26168 //    /**
26169 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26170 //     * @method
26171 //     */
26172 //    clearInvalid : Roo.emptyFn,
26173
26174     setValue : function(v){
26175         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26176         this.editorcore.pushValue();
26177     },
26178
26179      
26180     // private
26181     deferFocus : function(){
26182         this.focus.defer(10, this);
26183     },
26184
26185     // doc'ed in Field
26186     focus : function(){
26187         this.editorcore.focus();
26188         
26189     },
26190       
26191
26192     // private
26193     onDestroy : function(){
26194         
26195         
26196         
26197         if(this.rendered){
26198             
26199             for (var i =0; i < this.toolbars.length;i++) {
26200                 // fixme - ask toolbars for heights?
26201                 this.toolbars[i].onDestroy();
26202             }
26203             
26204             this.wrap.dom.innerHTML = '';
26205             this.wrap.remove();
26206         }
26207     },
26208
26209     // private
26210     onFirstFocus : function(){
26211         //Roo.log("onFirstFocus");
26212         this.editorcore.onFirstFocus();
26213          for (var i =0; i < this.toolbars.length;i++) {
26214             this.toolbars[i].onFirstFocus();
26215         }
26216         
26217     },
26218     
26219     // private
26220     syncValue : function()
26221     {   
26222         this.editorcore.syncValue();
26223     },
26224     
26225     pushValue : function()
26226     {   
26227         this.editorcore.pushValue();
26228     }
26229      
26230     
26231     // hide stuff that is not compatible
26232     /**
26233      * @event blur
26234      * @hide
26235      */
26236     /**
26237      * @event change
26238      * @hide
26239      */
26240     /**
26241      * @event focus
26242      * @hide
26243      */
26244     /**
26245      * @event specialkey
26246      * @hide
26247      */
26248     /**
26249      * @cfg {String} fieldClass @hide
26250      */
26251     /**
26252      * @cfg {String} focusClass @hide
26253      */
26254     /**
26255      * @cfg {String} autoCreate @hide
26256      */
26257     /**
26258      * @cfg {String} inputType @hide
26259      */
26260      
26261     /**
26262      * @cfg {String} invalidText @hide
26263      */
26264     /**
26265      * @cfg {String} msgFx @hide
26266      */
26267     /**
26268      * @cfg {String} validateOnBlur @hide
26269      */
26270 });
26271  
26272     
26273    
26274    
26275    
26276       
26277 Roo.namespace('Roo.bootstrap.htmleditor');
26278 /**
26279  * @class Roo.bootstrap.HtmlEditorToolbar1
26280  * Basic Toolbar
26281  * 
26282  * @example
26283  * Usage:
26284  *
26285  new Roo.bootstrap.HtmlEditor({
26286     ....
26287     toolbars : [
26288         new Roo.bootstrap.HtmlEditorToolbar1({
26289             disable : { fonts: 1 , format: 1, ..., ... , ...],
26290             btns : [ .... ]
26291         })
26292     }
26293      
26294  * 
26295  * @cfg {Object} disable List of elements to disable..
26296  * @cfg {Array} btns List of additional buttons.
26297  * 
26298  * 
26299  * NEEDS Extra CSS? 
26300  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26301  */
26302  
26303 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26304 {
26305     
26306     Roo.apply(this, config);
26307     
26308     // default disabled, based on 'good practice'..
26309     this.disable = this.disable || {};
26310     Roo.applyIf(this.disable, {
26311         fontSize : true,
26312         colors : true,
26313         specialElements : true
26314     });
26315     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26316     
26317     this.editor = config.editor;
26318     this.editorcore = config.editor.editorcore;
26319     
26320     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26321     
26322     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26323     // dont call parent... till later.
26324 }
26325 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26326      
26327     bar : true,
26328     
26329     editor : false,
26330     editorcore : false,
26331     
26332     
26333     formats : [
26334         "p" ,  
26335         "h1","h2","h3","h4","h5","h6", 
26336         "pre", "code", 
26337         "abbr", "acronym", "address", "cite", "samp", "var",
26338         'div','span'
26339     ],
26340     
26341     onRender : function(ct, position)
26342     {
26343        // Roo.log("Call onRender: " + this.xtype);
26344         
26345        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26346        Roo.log(this.el);
26347        this.el.dom.style.marginBottom = '0';
26348        var _this = this;
26349        var editorcore = this.editorcore;
26350        var editor= this.editor;
26351        
26352        var children = [];
26353        var btn = function(id,cmd , toggle, handler, html){
26354        
26355             var  event = toggle ? 'toggle' : 'click';
26356        
26357             var a = {
26358                 size : 'sm',
26359                 xtype: 'Button',
26360                 xns: Roo.bootstrap,
26361                 //glyphicon : id,
26362                 fa: id,
26363                 cmd : id || cmd,
26364                 enableToggle:toggle !== false,
26365                 html : html || '',
26366                 pressed : toggle ? false : null,
26367                 listeners : {}
26368             };
26369             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26370                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26371             };
26372             children.push(a);
26373             return a;
26374        }
26375        
26376     //    var cb_box = function...
26377         
26378         var style = {
26379                 xtype: 'Button',
26380                 size : 'sm',
26381                 xns: Roo.bootstrap,
26382                 fa : 'font',
26383                 //html : 'submit'
26384                 menu : {
26385                     xtype: 'Menu',
26386                     xns: Roo.bootstrap,
26387                     items:  []
26388                 }
26389         };
26390         Roo.each(this.formats, function(f) {
26391             style.menu.items.push({
26392                 xtype :'MenuItem',
26393                 xns: Roo.bootstrap,
26394                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26395                 tagname : f,
26396                 listeners : {
26397                     click : function()
26398                     {
26399                         editorcore.insertTag(this.tagname);
26400                         editor.focus();
26401                     }
26402                 }
26403                 
26404             });
26405         });
26406         children.push(style);   
26407         
26408         btn('bold',false,true);
26409         btn('italic',false,true);
26410         btn('align-left', 'justifyleft',true);
26411         btn('align-center', 'justifycenter',true);
26412         btn('align-right' , 'justifyright',true);
26413         btn('link', false, false, function(btn) {
26414             //Roo.log("create link?");
26415             var url = prompt(this.createLinkText, this.defaultLinkValue);
26416             if(url && url != 'http:/'+'/'){
26417                 this.editorcore.relayCmd('createlink', url);
26418             }
26419         }),
26420         btn('list','insertunorderedlist',true);
26421         btn('pencil', false,true, function(btn){
26422                 Roo.log(this);
26423                 this.toggleSourceEdit(btn.pressed);
26424         });
26425         
26426         if (this.editor.btns.length > 0) {
26427             for (var i = 0; i<this.editor.btns.length; i++) {
26428                 children.push(this.editor.btns[i]);
26429             }
26430         }
26431         
26432         /*
26433         var cog = {
26434                 xtype: 'Button',
26435                 size : 'sm',
26436                 xns: Roo.bootstrap,
26437                 glyphicon : 'cog',
26438                 //html : 'submit'
26439                 menu : {
26440                     xtype: 'Menu',
26441                     xns: Roo.bootstrap,
26442                     items:  []
26443                 }
26444         };
26445         
26446         cog.menu.items.push({
26447             xtype :'MenuItem',
26448             xns: Roo.bootstrap,
26449             html : Clean styles,
26450             tagname : f,
26451             listeners : {
26452                 click : function()
26453                 {
26454                     editorcore.insertTag(this.tagname);
26455                     editor.focus();
26456                 }
26457             }
26458             
26459         });
26460        */
26461         
26462          
26463        this.xtype = 'NavSimplebar';
26464         
26465         for(var i=0;i< children.length;i++) {
26466             
26467             this.buttons.add(this.addxtypeChild(children[i]));
26468             
26469         }
26470         
26471         editor.on('editorevent', this.updateToolbar, this);
26472     },
26473     onBtnClick : function(id)
26474     {
26475        this.editorcore.relayCmd(id);
26476        this.editorcore.focus();
26477     },
26478     
26479     /**
26480      * Protected method that will not generally be called directly. It triggers
26481      * a toolbar update by reading the markup state of the current selection in the editor.
26482      */
26483     updateToolbar: function(){
26484
26485         if(!this.editorcore.activated){
26486             this.editor.onFirstFocus(); // is this neeed?
26487             return;
26488         }
26489
26490         var btns = this.buttons; 
26491         var doc = this.editorcore.doc;
26492         btns.get('bold').setActive(doc.queryCommandState('bold'));
26493         btns.get('italic').setActive(doc.queryCommandState('italic'));
26494         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26495         
26496         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26497         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26498         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26499         
26500         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26501         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26502          /*
26503         
26504         var ans = this.editorcore.getAllAncestors();
26505         if (this.formatCombo) {
26506             
26507             
26508             var store = this.formatCombo.store;
26509             this.formatCombo.setValue("");
26510             for (var i =0; i < ans.length;i++) {
26511                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26512                     // select it..
26513                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26514                     break;
26515                 }
26516             }
26517         }
26518         
26519         
26520         
26521         // hides menus... - so this cant be on a menu...
26522         Roo.bootstrap.MenuMgr.hideAll();
26523         */
26524         Roo.bootstrap.MenuMgr.hideAll();
26525         //this.editorsyncValue();
26526     },
26527     onFirstFocus: function() {
26528         this.buttons.each(function(item){
26529            item.enable();
26530         });
26531     },
26532     toggleSourceEdit : function(sourceEditMode){
26533         
26534           
26535         if(sourceEditMode){
26536             Roo.log("disabling buttons");
26537            this.buttons.each( function(item){
26538                 if(item.cmd != 'pencil'){
26539                     item.disable();
26540                 }
26541             });
26542           
26543         }else{
26544             Roo.log("enabling buttons");
26545             if(this.editorcore.initialized){
26546                 this.buttons.each( function(item){
26547                     item.enable();
26548                 });
26549             }
26550             
26551         }
26552         Roo.log("calling toggole on editor");
26553         // tell the editor that it's been pressed..
26554         this.editor.toggleSourceEdit(sourceEditMode);
26555        
26556     }
26557 });
26558
26559
26560
26561
26562  
26563 /*
26564  * - LGPL
26565  */
26566
26567 /**
26568  * @class Roo.bootstrap.Markdown
26569  * @extends Roo.bootstrap.TextArea
26570  * Bootstrap Showdown editable area
26571  * @cfg {string} content
26572  * 
26573  * @constructor
26574  * Create a new Showdown
26575  */
26576
26577 Roo.bootstrap.Markdown = function(config){
26578     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26579    
26580 };
26581
26582 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26583     
26584     editing :false,
26585     
26586     initEvents : function()
26587     {
26588         
26589         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26590         this.markdownEl = this.el.createChild({
26591             cls : 'roo-markdown-area'
26592         });
26593         this.inputEl().addClass('d-none');
26594         if (this.getValue() == '') {
26595             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26596             
26597         } else {
26598             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26599         }
26600         this.markdownEl.on('click', this.toggleTextEdit, this);
26601         this.on('blur', this.toggleTextEdit, this);
26602         this.on('specialkey', this.resizeTextArea, this);
26603     },
26604     
26605     toggleTextEdit : function()
26606     {
26607         var sh = this.markdownEl.getHeight();
26608         this.inputEl().addClass('d-none');
26609         this.markdownEl.addClass('d-none');
26610         if (!this.editing) {
26611             // show editor?
26612             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26613             this.inputEl().removeClass('d-none');
26614             this.inputEl().focus();
26615             this.editing = true;
26616             return;
26617         }
26618         // show showdown...
26619         this.updateMarkdown();
26620         this.markdownEl.removeClass('d-none');
26621         this.editing = false;
26622         return;
26623     },
26624     updateMarkdown : function()
26625     {
26626         if (this.getValue() == '') {
26627             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26628             return;
26629         }
26630  
26631         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26632     },
26633     
26634     resizeTextArea: function () {
26635         
26636         var sh = 100;
26637         Roo.log([sh, this.getValue().split("\n").length * 30]);
26638         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26639     },
26640     setValue : function(val)
26641     {
26642         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26643         if (!this.editing) {
26644             this.updateMarkdown();
26645         }
26646         
26647     },
26648     focus : function()
26649     {
26650         if (!this.editing) {
26651             this.toggleTextEdit();
26652         }
26653         
26654     }
26655
26656
26657 });
26658 /**
26659  * @class Roo.bootstrap.Table.AbstractSelectionModel
26660  * @extends Roo.util.Observable
26661  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26662  * implemented by descendant classes.  This class should not be directly instantiated.
26663  * @constructor
26664  */
26665 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26666     this.locked = false;
26667     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26668 };
26669
26670
26671 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26672     /** @ignore Called by the grid automatically. Do not call directly. */
26673     init : function(grid){
26674         this.grid = grid;
26675         this.initEvents();
26676     },
26677
26678     /**
26679      * Locks the selections.
26680      */
26681     lock : function(){
26682         this.locked = true;
26683     },
26684
26685     /**
26686      * Unlocks the selections.
26687      */
26688     unlock : function(){
26689         this.locked = false;
26690     },
26691
26692     /**
26693      * Returns true if the selections are locked.
26694      * @return {Boolean}
26695      */
26696     isLocked : function(){
26697         return this.locked;
26698     },
26699     
26700     
26701     initEvents : function ()
26702     {
26703         
26704     }
26705 });
26706 /**
26707  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26708  * @class Roo.bootstrap.Table.RowSelectionModel
26709  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26710  * It supports multiple selections and keyboard selection/navigation. 
26711  * @constructor
26712  * @param {Object} config
26713  */
26714
26715 Roo.bootstrap.Table.RowSelectionModel = function(config){
26716     Roo.apply(this, config);
26717     this.selections = new Roo.util.MixedCollection(false, function(o){
26718         return o.id;
26719     });
26720
26721     this.last = false;
26722     this.lastActive = false;
26723
26724     this.addEvents({
26725         /**
26726              * @event selectionchange
26727              * Fires when the selection changes
26728              * @param {SelectionModel} this
26729              */
26730             "selectionchange" : true,
26731         /**
26732              * @event afterselectionchange
26733              * Fires after the selection changes (eg. by key press or clicking)
26734              * @param {SelectionModel} this
26735              */
26736             "afterselectionchange" : true,
26737         /**
26738              * @event beforerowselect
26739              * Fires when a row is selected being selected, return false to cancel.
26740              * @param {SelectionModel} this
26741              * @param {Number} rowIndex The selected index
26742              * @param {Boolean} keepExisting False if other selections will be cleared
26743              */
26744             "beforerowselect" : true,
26745         /**
26746              * @event rowselect
26747              * Fires when a row is selected.
26748              * @param {SelectionModel} this
26749              * @param {Number} rowIndex The selected index
26750              * @param {Roo.data.Record} r The record
26751              */
26752             "rowselect" : true,
26753         /**
26754              * @event rowdeselect
26755              * Fires when a row is deselected.
26756              * @param {SelectionModel} this
26757              * @param {Number} rowIndex The selected index
26758              */
26759         "rowdeselect" : true
26760     });
26761     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26762     this.locked = false;
26763  };
26764
26765 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26766     /**
26767      * @cfg {Boolean} singleSelect
26768      * True to allow selection of only one row at a time (defaults to false)
26769      */
26770     singleSelect : false,
26771
26772     // private
26773     initEvents : function()
26774     {
26775
26776         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26777         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26778         //}else{ // allow click to work like normal
26779          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26780         //}
26781         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26782         this.grid.on("rowclick", this.handleMouseDown, this);
26783         
26784         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26785             "up" : function(e){
26786                 if(!e.shiftKey){
26787                     this.selectPrevious(e.shiftKey);
26788                 }else if(this.last !== false && this.lastActive !== false){
26789                     var last = this.last;
26790                     this.selectRange(this.last,  this.lastActive-1);
26791                     this.grid.getView().focusRow(this.lastActive);
26792                     if(last !== false){
26793                         this.last = last;
26794                     }
26795                 }else{
26796                     this.selectFirstRow();
26797                 }
26798                 this.fireEvent("afterselectionchange", this);
26799             },
26800             "down" : function(e){
26801                 if(!e.shiftKey){
26802                     this.selectNext(e.shiftKey);
26803                 }else if(this.last !== false && this.lastActive !== false){
26804                     var last = this.last;
26805                     this.selectRange(this.last,  this.lastActive+1);
26806                     this.grid.getView().focusRow(this.lastActive);
26807                     if(last !== false){
26808                         this.last = last;
26809                     }
26810                 }else{
26811                     this.selectFirstRow();
26812                 }
26813                 this.fireEvent("afterselectionchange", this);
26814             },
26815             scope: this
26816         });
26817         this.grid.store.on('load', function(){
26818             this.selections.clear();
26819         },this);
26820         /*
26821         var view = this.grid.view;
26822         view.on("refresh", this.onRefresh, this);
26823         view.on("rowupdated", this.onRowUpdated, this);
26824         view.on("rowremoved", this.onRemove, this);
26825         */
26826     },
26827
26828     // private
26829     onRefresh : function()
26830     {
26831         var ds = this.grid.store, i, v = this.grid.view;
26832         var s = this.selections;
26833         s.each(function(r){
26834             if((i = ds.indexOfId(r.id)) != -1){
26835                 v.onRowSelect(i);
26836             }else{
26837                 s.remove(r);
26838             }
26839         });
26840     },
26841
26842     // private
26843     onRemove : function(v, index, r){
26844         this.selections.remove(r);
26845     },
26846
26847     // private
26848     onRowUpdated : function(v, index, r){
26849         if(this.isSelected(r)){
26850             v.onRowSelect(index);
26851         }
26852     },
26853
26854     /**
26855      * Select records.
26856      * @param {Array} records The records to select
26857      * @param {Boolean} keepExisting (optional) True to keep existing selections
26858      */
26859     selectRecords : function(records, keepExisting)
26860     {
26861         if(!keepExisting){
26862             this.clearSelections();
26863         }
26864             var ds = this.grid.store;
26865         for(var i = 0, len = records.length; i < len; i++){
26866             this.selectRow(ds.indexOf(records[i]), true);
26867         }
26868     },
26869
26870     /**
26871      * Gets the number of selected rows.
26872      * @return {Number}
26873      */
26874     getCount : function(){
26875         return this.selections.length;
26876     },
26877
26878     /**
26879      * Selects the first row in the grid.
26880      */
26881     selectFirstRow : function(){
26882         this.selectRow(0);
26883     },
26884
26885     /**
26886      * Select the last row.
26887      * @param {Boolean} keepExisting (optional) True to keep existing selections
26888      */
26889     selectLastRow : function(keepExisting){
26890         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26891         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26892     },
26893
26894     /**
26895      * Selects the row immediately following the last selected row.
26896      * @param {Boolean} keepExisting (optional) True to keep existing selections
26897      */
26898     selectNext : function(keepExisting)
26899     {
26900             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26901             this.selectRow(this.last+1, keepExisting);
26902             this.grid.getView().focusRow(this.last);
26903         }
26904     },
26905
26906     /**
26907      * Selects the row that precedes the last selected row.
26908      * @param {Boolean} keepExisting (optional) True to keep existing selections
26909      */
26910     selectPrevious : function(keepExisting){
26911         if(this.last){
26912             this.selectRow(this.last-1, keepExisting);
26913             this.grid.getView().focusRow(this.last);
26914         }
26915     },
26916
26917     /**
26918      * Returns the selected records
26919      * @return {Array} Array of selected records
26920      */
26921     getSelections : function(){
26922         return [].concat(this.selections.items);
26923     },
26924
26925     /**
26926      * Returns the first selected record.
26927      * @return {Record}
26928      */
26929     getSelected : function(){
26930         return this.selections.itemAt(0);
26931     },
26932
26933
26934     /**
26935      * Clears all selections.
26936      */
26937     clearSelections : function(fast)
26938     {
26939         if(this.locked) {
26940             return;
26941         }
26942         if(fast !== true){
26943                 var ds = this.grid.store;
26944             var s = this.selections;
26945             s.each(function(r){
26946                 this.deselectRow(ds.indexOfId(r.id));
26947             }, this);
26948             s.clear();
26949         }else{
26950             this.selections.clear();
26951         }
26952         this.last = false;
26953     },
26954
26955
26956     /**
26957      * Selects all rows.
26958      */
26959     selectAll : function(){
26960         if(this.locked) {
26961             return;
26962         }
26963         this.selections.clear();
26964         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26965             this.selectRow(i, true);
26966         }
26967     },
26968
26969     /**
26970      * Returns True if there is a selection.
26971      * @return {Boolean}
26972      */
26973     hasSelection : function(){
26974         return this.selections.length > 0;
26975     },
26976
26977     /**
26978      * Returns True if the specified row is selected.
26979      * @param {Number/Record} record The record or index of the record to check
26980      * @return {Boolean}
26981      */
26982     isSelected : function(index){
26983             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26984         return (r && this.selections.key(r.id) ? true : false);
26985     },
26986
26987     /**
26988      * Returns True if the specified record id is selected.
26989      * @param {String} id The id of record to check
26990      * @return {Boolean}
26991      */
26992     isIdSelected : function(id){
26993         return (this.selections.key(id) ? true : false);
26994     },
26995
26996
26997     // private
26998     handleMouseDBClick : function(e, t){
26999         
27000     },
27001     // private
27002     handleMouseDown : function(e, t)
27003     {
27004             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27005         if(this.isLocked() || rowIndex < 0 ){
27006             return;
27007         };
27008         if(e.shiftKey && this.last !== false){
27009             var last = this.last;
27010             this.selectRange(last, rowIndex, e.ctrlKey);
27011             this.last = last; // reset the last
27012             t.focus();
27013     
27014         }else{
27015             var isSelected = this.isSelected(rowIndex);
27016             //Roo.log("select row:" + rowIndex);
27017             if(isSelected){
27018                 this.deselectRow(rowIndex);
27019             } else {
27020                         this.selectRow(rowIndex, true);
27021             }
27022     
27023             /*
27024                 if(e.button !== 0 && isSelected){
27025                 alert('rowIndex 2: ' + rowIndex);
27026                     view.focusRow(rowIndex);
27027                 }else if(e.ctrlKey && isSelected){
27028                     this.deselectRow(rowIndex);
27029                 }else if(!isSelected){
27030                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27031                     view.focusRow(rowIndex);
27032                 }
27033             */
27034         }
27035         this.fireEvent("afterselectionchange", this);
27036     },
27037     // private
27038     handleDragableRowClick :  function(grid, rowIndex, e) 
27039     {
27040         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27041             this.selectRow(rowIndex, false);
27042             grid.view.focusRow(rowIndex);
27043              this.fireEvent("afterselectionchange", this);
27044         }
27045     },
27046     
27047     /**
27048      * Selects multiple rows.
27049      * @param {Array} rows Array of the indexes of the row to select
27050      * @param {Boolean} keepExisting (optional) True to keep existing selections
27051      */
27052     selectRows : function(rows, keepExisting){
27053         if(!keepExisting){
27054             this.clearSelections();
27055         }
27056         for(var i = 0, len = rows.length; i < len; i++){
27057             this.selectRow(rows[i], true);
27058         }
27059     },
27060
27061     /**
27062      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27063      * @param {Number} startRow The index of the first row in the range
27064      * @param {Number} endRow The index of the last row in the range
27065      * @param {Boolean} keepExisting (optional) True to retain existing selections
27066      */
27067     selectRange : function(startRow, endRow, keepExisting){
27068         if(this.locked) {
27069             return;
27070         }
27071         if(!keepExisting){
27072             this.clearSelections();
27073         }
27074         if(startRow <= endRow){
27075             for(var i = startRow; i <= endRow; i++){
27076                 this.selectRow(i, true);
27077             }
27078         }else{
27079             for(var i = startRow; i >= endRow; i--){
27080                 this.selectRow(i, true);
27081             }
27082         }
27083     },
27084
27085     /**
27086      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27087      * @param {Number} startRow The index of the first row in the range
27088      * @param {Number} endRow The index of the last row in the range
27089      */
27090     deselectRange : function(startRow, endRow, preventViewNotify){
27091         if(this.locked) {
27092             return;
27093         }
27094         for(var i = startRow; i <= endRow; i++){
27095             this.deselectRow(i, preventViewNotify);
27096         }
27097     },
27098
27099     /**
27100      * Selects a row.
27101      * @param {Number} row The index of the row to select
27102      * @param {Boolean} keepExisting (optional) True to keep existing selections
27103      */
27104     selectRow : function(index, keepExisting, preventViewNotify)
27105     {
27106             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27107             return;
27108         }
27109         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27110             if(!keepExisting || this.singleSelect){
27111                 this.clearSelections();
27112             }
27113             
27114             var r = this.grid.store.getAt(index);
27115             //console.log('selectRow - record id :' + r.id);
27116             
27117             this.selections.add(r);
27118             this.last = this.lastActive = index;
27119             if(!preventViewNotify){
27120                 var proxy = new Roo.Element(
27121                                 this.grid.getRowDom(index)
27122                 );
27123                 proxy.addClass('bg-info info');
27124             }
27125             this.fireEvent("rowselect", this, index, r);
27126             this.fireEvent("selectionchange", this);
27127         }
27128     },
27129
27130     /**
27131      * Deselects a row.
27132      * @param {Number} row The index of the row to deselect
27133      */
27134     deselectRow : function(index, preventViewNotify)
27135     {
27136         if(this.locked) {
27137             return;
27138         }
27139         if(this.last == index){
27140             this.last = false;
27141         }
27142         if(this.lastActive == index){
27143             this.lastActive = false;
27144         }
27145         
27146         var r = this.grid.store.getAt(index);
27147         if (!r) {
27148             return;
27149         }
27150         
27151         this.selections.remove(r);
27152         //.console.log('deselectRow - record id :' + r.id);
27153         if(!preventViewNotify){
27154         
27155             var proxy = new Roo.Element(
27156                 this.grid.getRowDom(index)
27157             );
27158             proxy.removeClass('bg-info info');
27159         }
27160         this.fireEvent("rowdeselect", this, index);
27161         this.fireEvent("selectionchange", this);
27162     },
27163
27164     // private
27165     restoreLast : function(){
27166         if(this._last){
27167             this.last = this._last;
27168         }
27169     },
27170
27171     // private
27172     acceptsNav : function(row, col, cm){
27173         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27174     },
27175
27176     // private
27177     onEditorKey : function(field, e){
27178         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27179         if(k == e.TAB){
27180             e.stopEvent();
27181             ed.completeEdit();
27182             if(e.shiftKey){
27183                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27184             }else{
27185                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27186             }
27187         }else if(k == e.ENTER && !e.ctrlKey){
27188             e.stopEvent();
27189             ed.completeEdit();
27190             if(e.shiftKey){
27191                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27192             }else{
27193                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27194             }
27195         }else if(k == e.ESC){
27196             ed.cancelEdit();
27197         }
27198         if(newCell){
27199             g.startEditing(newCell[0], newCell[1]);
27200         }
27201     }
27202 });
27203 /*
27204  * Based on:
27205  * Ext JS Library 1.1.1
27206  * Copyright(c) 2006-2007, Ext JS, LLC.
27207  *
27208  * Originally Released Under LGPL - original licence link has changed is not relivant.
27209  *
27210  * Fork - LGPL
27211  * <script type="text/javascript">
27212  */
27213  
27214 /**
27215  * @class Roo.bootstrap.PagingToolbar
27216  * @extends Roo.bootstrap.NavSimplebar
27217  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27218  * @constructor
27219  * Create a new PagingToolbar
27220  * @param {Object} config The config object
27221  * @param {Roo.data.Store} store
27222  */
27223 Roo.bootstrap.PagingToolbar = function(config)
27224 {
27225     // old args format still supported... - xtype is prefered..
27226         // created from xtype...
27227     
27228     this.ds = config.dataSource;
27229     
27230     if (config.store && !this.ds) {
27231         this.store= Roo.factory(config.store, Roo.data);
27232         this.ds = this.store;
27233         this.ds.xmodule = this.xmodule || false;
27234     }
27235     
27236     this.toolbarItems = [];
27237     if (config.items) {
27238         this.toolbarItems = config.items;
27239     }
27240     
27241     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27242     
27243     this.cursor = 0;
27244     
27245     if (this.ds) { 
27246         this.bind(this.ds);
27247     }
27248     
27249     if (Roo.bootstrap.version == 4) {
27250         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27251     } else {
27252         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27253     }
27254     
27255 };
27256
27257 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27258     /**
27259      * @cfg {Roo.data.Store} dataSource
27260      * The underlying data store providing the paged data
27261      */
27262     /**
27263      * @cfg {String/HTMLElement/Element} container
27264      * container The id or element that will contain the toolbar
27265      */
27266     /**
27267      * @cfg {Boolean} displayInfo
27268      * True to display the displayMsg (defaults to false)
27269      */
27270     /**
27271      * @cfg {Number} pageSize
27272      * The number of records to display per page (defaults to 20)
27273      */
27274     pageSize: 20,
27275     /**
27276      * @cfg {String} displayMsg
27277      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27278      */
27279     displayMsg : 'Displaying {0} - {1} of {2}',
27280     /**
27281      * @cfg {String} emptyMsg
27282      * The message to display when no records are found (defaults to "No data to display")
27283      */
27284     emptyMsg : 'No data to display',
27285     /**
27286      * Customizable piece of the default paging text (defaults to "Page")
27287      * @type String
27288      */
27289     beforePageText : "Page",
27290     /**
27291      * Customizable piece of the default paging text (defaults to "of %0")
27292      * @type String
27293      */
27294     afterPageText : "of {0}",
27295     /**
27296      * Customizable piece of the default paging text (defaults to "First Page")
27297      * @type String
27298      */
27299     firstText : "First Page",
27300     /**
27301      * Customizable piece of the default paging text (defaults to "Previous Page")
27302      * @type String
27303      */
27304     prevText : "Previous Page",
27305     /**
27306      * Customizable piece of the default paging text (defaults to "Next Page")
27307      * @type String
27308      */
27309     nextText : "Next Page",
27310     /**
27311      * Customizable piece of the default paging text (defaults to "Last Page")
27312      * @type String
27313      */
27314     lastText : "Last Page",
27315     /**
27316      * Customizable piece of the default paging text (defaults to "Refresh")
27317      * @type String
27318      */
27319     refreshText : "Refresh",
27320
27321     buttons : false,
27322     // private
27323     onRender : function(ct, position) 
27324     {
27325         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27326         this.navgroup.parentId = this.id;
27327         this.navgroup.onRender(this.el, null);
27328         // add the buttons to the navgroup
27329         
27330         if(this.displayInfo){
27331             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27332             this.displayEl = this.el.select('.x-paging-info', true).first();
27333 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27334 //            this.displayEl = navel.el.select('span',true).first();
27335         }
27336         
27337         var _this = this;
27338         
27339         if(this.buttons){
27340             Roo.each(_this.buttons, function(e){ // this might need to use render????
27341                Roo.factory(e).render(_this.el);
27342             });
27343         }
27344             
27345         Roo.each(_this.toolbarItems, function(e) {
27346             _this.navgroup.addItem(e);
27347         });
27348         
27349         
27350         this.first = this.navgroup.addItem({
27351             tooltip: this.firstText,
27352             cls: "prev btn-outline-secondary",
27353             html : ' <i class="fa fa-step-backward"></i>',
27354             disabled: true,
27355             preventDefault: true,
27356             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27357         });
27358         
27359         this.prev =  this.navgroup.addItem({
27360             tooltip: this.prevText,
27361             cls: "prev btn-outline-secondary",
27362             html : ' <i class="fa fa-backward"></i>',
27363             disabled: true,
27364             preventDefault: true,
27365             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27366         });
27367     //this.addSeparator();
27368         
27369         
27370         var field = this.navgroup.addItem( {
27371             tagtype : 'span',
27372             cls : 'x-paging-position  btn-outline-secondary',
27373              disabled: true,
27374             html : this.beforePageText  +
27375                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27376                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27377          } ); //?? escaped?
27378         
27379         this.field = field.el.select('input', true).first();
27380         this.field.on("keydown", this.onPagingKeydown, this);
27381         this.field.on("focus", function(){this.dom.select();});
27382     
27383     
27384         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27385         //this.field.setHeight(18);
27386         //this.addSeparator();
27387         this.next = this.navgroup.addItem({
27388             tooltip: this.nextText,
27389             cls: "next btn-outline-secondary",
27390             html : ' <i class="fa fa-forward"></i>',
27391             disabled: true,
27392             preventDefault: true,
27393             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27394         });
27395         this.last = this.navgroup.addItem({
27396             tooltip: this.lastText,
27397             html : ' <i class="fa fa-step-forward"></i>',
27398             cls: "next btn-outline-secondary",
27399             disabled: true,
27400             preventDefault: true,
27401             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27402         });
27403     //this.addSeparator();
27404         this.loading = this.navgroup.addItem({
27405             tooltip: this.refreshText,
27406             cls: "btn-outline-secondary",
27407             html : ' <i class="fa fa-refresh"></i>',
27408             preventDefault: true,
27409             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27410         });
27411         
27412     },
27413
27414     // private
27415     updateInfo : function(){
27416         if(this.displayEl){
27417             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27418             var msg = count == 0 ?
27419                 this.emptyMsg :
27420                 String.format(
27421                     this.displayMsg,
27422                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27423                 );
27424             this.displayEl.update(msg);
27425         }
27426     },
27427
27428     // private
27429     onLoad : function(ds, r, o)
27430     {
27431         this.cursor = o.params && o.params.start ? o.params.start : 0;
27432         
27433         var d = this.getPageData(),
27434             ap = d.activePage,
27435             ps = d.pages;
27436         
27437         
27438         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27439         this.field.dom.value = ap;
27440         this.first.setDisabled(ap == 1);
27441         this.prev.setDisabled(ap == 1);
27442         this.next.setDisabled(ap == ps);
27443         this.last.setDisabled(ap == ps);
27444         this.loading.enable();
27445         this.updateInfo();
27446     },
27447
27448     // private
27449     getPageData : function(){
27450         var total = this.ds.getTotalCount();
27451         return {
27452             total : total,
27453             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27454             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27455         };
27456     },
27457
27458     // private
27459     onLoadError : function(){
27460         this.loading.enable();
27461     },
27462
27463     // private
27464     onPagingKeydown : function(e){
27465         var k = e.getKey();
27466         var d = this.getPageData();
27467         if(k == e.RETURN){
27468             var v = this.field.dom.value, pageNum;
27469             if(!v || isNaN(pageNum = parseInt(v, 10))){
27470                 this.field.dom.value = d.activePage;
27471                 return;
27472             }
27473             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27474             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27475             e.stopEvent();
27476         }
27477         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))
27478         {
27479           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27480           this.field.dom.value = pageNum;
27481           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27482           e.stopEvent();
27483         }
27484         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27485         {
27486           var v = this.field.dom.value, pageNum; 
27487           var increment = (e.shiftKey) ? 10 : 1;
27488           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27489                 increment *= -1;
27490           }
27491           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27492             this.field.dom.value = d.activePage;
27493             return;
27494           }
27495           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27496           {
27497             this.field.dom.value = parseInt(v, 10) + increment;
27498             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27499             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27500           }
27501           e.stopEvent();
27502         }
27503     },
27504
27505     // private
27506     beforeLoad : function(){
27507         if(this.loading){
27508             this.loading.disable();
27509         }
27510     },
27511
27512     // private
27513     onClick : function(which){
27514         
27515         var ds = this.ds;
27516         if (!ds) {
27517             return;
27518         }
27519         
27520         switch(which){
27521             case "first":
27522                 ds.load({params:{start: 0, limit: this.pageSize}});
27523             break;
27524             case "prev":
27525                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27526             break;
27527             case "next":
27528                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27529             break;
27530             case "last":
27531                 var total = ds.getTotalCount();
27532                 var extra = total % this.pageSize;
27533                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27534                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27535             break;
27536             case "refresh":
27537                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27538             break;
27539         }
27540     },
27541
27542     /**
27543      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27544      * @param {Roo.data.Store} store The data store to unbind
27545      */
27546     unbind : function(ds){
27547         ds.un("beforeload", this.beforeLoad, this);
27548         ds.un("load", this.onLoad, this);
27549         ds.un("loadexception", this.onLoadError, this);
27550         ds.un("remove", this.updateInfo, this);
27551         ds.un("add", this.updateInfo, this);
27552         this.ds = undefined;
27553     },
27554
27555     /**
27556      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27557      * @param {Roo.data.Store} store The data store to bind
27558      */
27559     bind : function(ds){
27560         ds.on("beforeload", this.beforeLoad, this);
27561         ds.on("load", this.onLoad, this);
27562         ds.on("loadexception", this.onLoadError, this);
27563         ds.on("remove", this.updateInfo, this);
27564         ds.on("add", this.updateInfo, this);
27565         this.ds = ds;
27566     }
27567 });/*
27568  * - LGPL
27569  *
27570  * element
27571  * 
27572  */
27573
27574 /**
27575  * @class Roo.bootstrap.MessageBar
27576  * @extends Roo.bootstrap.Component
27577  * Bootstrap MessageBar class
27578  * @cfg {String} html contents of the MessageBar
27579  * @cfg {String} weight (info | success | warning | danger) default info
27580  * @cfg {String} beforeClass insert the bar before the given class
27581  * @cfg {Boolean} closable (true | false) default false
27582  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27583  * 
27584  * @constructor
27585  * Create a new Element
27586  * @param {Object} config The config object
27587  */
27588
27589 Roo.bootstrap.MessageBar = function(config){
27590     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27591 };
27592
27593 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27594     
27595     html: '',
27596     weight: 'info',
27597     closable: false,
27598     fixed: false,
27599     beforeClass: 'bootstrap-sticky-wrap',
27600     
27601     getAutoCreate : function(){
27602         
27603         var cfg = {
27604             tag: 'div',
27605             cls: 'alert alert-dismissable alert-' + this.weight,
27606             cn: [
27607                 {
27608                     tag: 'span',
27609                     cls: 'message',
27610                     html: this.html || ''
27611                 }
27612             ]
27613         };
27614         
27615         if(this.fixed){
27616             cfg.cls += ' alert-messages-fixed';
27617         }
27618         
27619         if(this.closable){
27620             cfg.cn.push({
27621                 tag: 'button',
27622                 cls: 'close',
27623                 html: 'x'
27624             });
27625         }
27626         
27627         return cfg;
27628     },
27629     
27630     onRender : function(ct, position)
27631     {
27632         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27633         
27634         if(!this.el){
27635             var cfg = Roo.apply({},  this.getAutoCreate());
27636             cfg.id = Roo.id();
27637             
27638             if (this.cls) {
27639                 cfg.cls += ' ' + this.cls;
27640             }
27641             if (this.style) {
27642                 cfg.style = this.style;
27643             }
27644             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27645             
27646             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27647         }
27648         
27649         this.el.select('>button.close').on('click', this.hide, this);
27650         
27651     },
27652     
27653     show : function()
27654     {
27655         if (!this.rendered) {
27656             this.render();
27657         }
27658         
27659         this.el.show();
27660         
27661         this.fireEvent('show', this);
27662         
27663     },
27664     
27665     hide : function()
27666     {
27667         if (!this.rendered) {
27668             this.render();
27669         }
27670         
27671         this.el.hide();
27672         
27673         this.fireEvent('hide', this);
27674     },
27675     
27676     update : function()
27677     {
27678 //        var e = this.el.dom.firstChild;
27679 //        
27680 //        if(this.closable){
27681 //            e = e.nextSibling;
27682 //        }
27683 //        
27684 //        e.data = this.html || '';
27685
27686         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27687     }
27688    
27689 });
27690
27691  
27692
27693      /*
27694  * - LGPL
27695  *
27696  * Graph
27697  * 
27698  */
27699
27700
27701 /**
27702  * @class Roo.bootstrap.Graph
27703  * @extends Roo.bootstrap.Component
27704  * Bootstrap Graph class
27705 > Prameters
27706  -sm {number} sm 4
27707  -md {number} md 5
27708  @cfg {String} graphtype  bar | vbar | pie
27709  @cfg {number} g_x coodinator | centre x (pie)
27710  @cfg {number} g_y coodinator | centre y (pie)
27711  @cfg {number} g_r radius (pie)
27712  @cfg {number} g_height height of the chart (respected by all elements in the set)
27713  @cfg {number} g_width width of the chart (respected by all elements in the set)
27714  @cfg {Object} title The title of the chart
27715     
27716  -{Array}  values
27717  -opts (object) options for the chart 
27718      o {
27719      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27720      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27721      o vgutter (number)
27722      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.
27723      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27724      o to
27725      o stretch (boolean)
27726      o }
27727  -opts (object) options for the pie
27728      o{
27729      o cut
27730      o startAngle (number)
27731      o endAngle (number)
27732      } 
27733  *
27734  * @constructor
27735  * Create a new Input
27736  * @param {Object} config The config object
27737  */
27738
27739 Roo.bootstrap.Graph = function(config){
27740     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27741     
27742     this.addEvents({
27743         // img events
27744         /**
27745          * @event click
27746          * The img click event for the img.
27747          * @param {Roo.EventObject} e
27748          */
27749         "click" : true
27750     });
27751 };
27752
27753 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27754     
27755     sm: 4,
27756     md: 5,
27757     graphtype: 'bar',
27758     g_height: 250,
27759     g_width: 400,
27760     g_x: 50,
27761     g_y: 50,
27762     g_r: 30,
27763     opts:{
27764         //g_colors: this.colors,
27765         g_type: 'soft',
27766         g_gutter: '20%'
27767
27768     },
27769     title : false,
27770
27771     getAutoCreate : function(){
27772         
27773         var cfg = {
27774             tag: 'div',
27775             html : null
27776         };
27777         
27778         
27779         return  cfg;
27780     },
27781
27782     onRender : function(ct,position){
27783         
27784         
27785         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27786         
27787         if (typeof(Raphael) == 'undefined') {
27788             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27789             return;
27790         }
27791         
27792         this.raphael = Raphael(this.el.dom);
27793         
27794                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27795                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27796                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27797                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27798                 /*
27799                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27800                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27801                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27802                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27803                 
27804                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27805                 r.barchart(330, 10, 300, 220, data1);
27806                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27807                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27808                 */
27809                 
27810                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27811                 // r.barchart(30, 30, 560, 250,  xdata, {
27812                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27813                 //     axis : "0 0 1 1",
27814                 //     axisxlabels :  xdata
27815                 //     //yvalues : cols,
27816                    
27817                 // });
27818 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27819 //        
27820 //        this.load(null,xdata,{
27821 //                axis : "0 0 1 1",
27822 //                axisxlabels :  xdata
27823 //                });
27824
27825     },
27826
27827     load : function(graphtype,xdata,opts)
27828     {
27829         this.raphael.clear();
27830         if(!graphtype) {
27831             graphtype = this.graphtype;
27832         }
27833         if(!opts){
27834             opts = this.opts;
27835         }
27836         var r = this.raphael,
27837             fin = function () {
27838                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27839             },
27840             fout = function () {
27841                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27842             },
27843             pfin = function() {
27844                 this.sector.stop();
27845                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27846
27847                 if (this.label) {
27848                     this.label[0].stop();
27849                     this.label[0].attr({ r: 7.5 });
27850                     this.label[1].attr({ "font-weight": 800 });
27851                 }
27852             },
27853             pfout = function() {
27854                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27855
27856                 if (this.label) {
27857                     this.label[0].animate({ r: 5 }, 500, "bounce");
27858                     this.label[1].attr({ "font-weight": 400 });
27859                 }
27860             };
27861
27862         switch(graphtype){
27863             case 'bar':
27864                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27865                 break;
27866             case 'hbar':
27867                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27868                 break;
27869             case 'pie':
27870 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27871 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27872 //            
27873                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27874                 
27875                 break;
27876
27877         }
27878         
27879         if(this.title){
27880             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27881         }
27882         
27883     },
27884     
27885     setTitle: function(o)
27886     {
27887         this.title = o;
27888     },
27889     
27890     initEvents: function() {
27891         
27892         if(!this.href){
27893             this.el.on('click', this.onClick, this);
27894         }
27895     },
27896     
27897     onClick : function(e)
27898     {
27899         Roo.log('img onclick');
27900         this.fireEvent('click', this, e);
27901     }
27902    
27903 });
27904
27905  
27906 /*
27907  * - LGPL
27908  *
27909  * numberBox
27910  * 
27911  */
27912 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27913
27914 /**
27915  * @class Roo.bootstrap.dash.NumberBox
27916  * @extends Roo.bootstrap.Component
27917  * Bootstrap NumberBox class
27918  * @cfg {String} headline Box headline
27919  * @cfg {String} content Box content
27920  * @cfg {String} icon Box icon
27921  * @cfg {String} footer Footer text
27922  * @cfg {String} fhref Footer href
27923  * 
27924  * @constructor
27925  * Create a new NumberBox
27926  * @param {Object} config The config object
27927  */
27928
27929
27930 Roo.bootstrap.dash.NumberBox = function(config){
27931     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27932     
27933 };
27934
27935 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27936     
27937     headline : '',
27938     content : '',
27939     icon : '',
27940     footer : '',
27941     fhref : '',
27942     ficon : '',
27943     
27944     getAutoCreate : function(){
27945         
27946         var cfg = {
27947             tag : 'div',
27948             cls : 'small-box ',
27949             cn : [
27950                 {
27951                     tag : 'div',
27952                     cls : 'inner',
27953                     cn :[
27954                         {
27955                             tag : 'h3',
27956                             cls : 'roo-headline',
27957                             html : this.headline
27958                         },
27959                         {
27960                             tag : 'p',
27961                             cls : 'roo-content',
27962                             html : this.content
27963                         }
27964                     ]
27965                 }
27966             ]
27967         };
27968         
27969         if(this.icon){
27970             cfg.cn.push({
27971                 tag : 'div',
27972                 cls : 'icon',
27973                 cn :[
27974                     {
27975                         tag : 'i',
27976                         cls : 'ion ' + this.icon
27977                     }
27978                 ]
27979             });
27980         }
27981         
27982         if(this.footer){
27983             var footer = {
27984                 tag : 'a',
27985                 cls : 'small-box-footer',
27986                 href : this.fhref || '#',
27987                 html : this.footer
27988             };
27989             
27990             cfg.cn.push(footer);
27991             
27992         }
27993         
27994         return  cfg;
27995     },
27996
27997     onRender : function(ct,position){
27998         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27999
28000
28001        
28002                 
28003     },
28004
28005     setHeadline: function (value)
28006     {
28007         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28008     },
28009     
28010     setFooter: function (value, href)
28011     {
28012         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28013         
28014         if(href){
28015             this.el.select('a.small-box-footer',true).first().attr('href', href);
28016         }
28017         
28018     },
28019
28020     setContent: function (value)
28021     {
28022         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28023     },
28024
28025     initEvents: function() 
28026     {   
28027         
28028     }
28029     
28030 });
28031
28032  
28033 /*
28034  * - LGPL
28035  *
28036  * TabBox
28037  * 
28038  */
28039 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28040
28041 /**
28042  * @class Roo.bootstrap.dash.TabBox
28043  * @extends Roo.bootstrap.Component
28044  * Bootstrap TabBox class
28045  * @cfg {String} title Title of the TabBox
28046  * @cfg {String} icon Icon of the TabBox
28047  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28048  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28049  * 
28050  * @constructor
28051  * Create a new TabBox
28052  * @param {Object} config The config object
28053  */
28054
28055
28056 Roo.bootstrap.dash.TabBox = function(config){
28057     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28058     this.addEvents({
28059         // raw events
28060         /**
28061          * @event addpane
28062          * When a pane is added
28063          * @param {Roo.bootstrap.dash.TabPane} pane
28064          */
28065         "addpane" : true,
28066         /**
28067          * @event activatepane
28068          * When a pane is activated
28069          * @param {Roo.bootstrap.dash.TabPane} pane
28070          */
28071         "activatepane" : true
28072         
28073          
28074     });
28075     
28076     this.panes = [];
28077 };
28078
28079 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28080
28081     title : '',
28082     icon : false,
28083     showtabs : true,
28084     tabScrollable : false,
28085     
28086     getChildContainer : function()
28087     {
28088         return this.el.select('.tab-content', true).first();
28089     },
28090     
28091     getAutoCreate : function(){
28092         
28093         var header = {
28094             tag: 'li',
28095             cls: 'pull-left header',
28096             html: this.title,
28097             cn : []
28098         };
28099         
28100         if(this.icon){
28101             header.cn.push({
28102                 tag: 'i',
28103                 cls: 'fa ' + this.icon
28104             });
28105         }
28106         
28107         var h = {
28108             tag: 'ul',
28109             cls: 'nav nav-tabs pull-right',
28110             cn: [
28111                 header
28112             ]
28113         };
28114         
28115         if(this.tabScrollable){
28116             h = {
28117                 tag: 'div',
28118                 cls: 'tab-header',
28119                 cn: [
28120                     {
28121                         tag: 'ul',
28122                         cls: 'nav nav-tabs pull-right',
28123                         cn: [
28124                             header
28125                         ]
28126                     }
28127                 ]
28128             };
28129         }
28130         
28131         var cfg = {
28132             tag: 'div',
28133             cls: 'nav-tabs-custom',
28134             cn: [
28135                 h,
28136                 {
28137                     tag: 'div',
28138                     cls: 'tab-content no-padding',
28139                     cn: []
28140                 }
28141             ]
28142         };
28143
28144         return  cfg;
28145     },
28146     initEvents : function()
28147     {
28148         //Roo.log('add add pane handler');
28149         this.on('addpane', this.onAddPane, this);
28150     },
28151      /**
28152      * Updates the box title
28153      * @param {String} html to set the title to.
28154      */
28155     setTitle : function(value)
28156     {
28157         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28158     },
28159     onAddPane : function(pane)
28160     {
28161         this.panes.push(pane);
28162         //Roo.log('addpane');
28163         //Roo.log(pane);
28164         // tabs are rendere left to right..
28165         if(!this.showtabs){
28166             return;
28167         }
28168         
28169         var ctr = this.el.select('.nav-tabs', true).first();
28170          
28171          
28172         var existing = ctr.select('.nav-tab',true);
28173         var qty = existing.getCount();;
28174         
28175         
28176         var tab = ctr.createChild({
28177             tag : 'li',
28178             cls : 'nav-tab' + (qty ? '' : ' active'),
28179             cn : [
28180                 {
28181                     tag : 'a',
28182                     href:'#',
28183                     html : pane.title
28184                 }
28185             ]
28186         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28187         pane.tab = tab;
28188         
28189         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28190         if (!qty) {
28191             pane.el.addClass('active');
28192         }
28193         
28194                 
28195     },
28196     onTabClick : function(ev,un,ob,pane)
28197     {
28198         //Roo.log('tab - prev default');
28199         ev.preventDefault();
28200         
28201         
28202         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28203         pane.tab.addClass('active');
28204         //Roo.log(pane.title);
28205         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28206         // technically we should have a deactivate event.. but maybe add later.
28207         // and it should not de-activate the selected tab...
28208         this.fireEvent('activatepane', pane);
28209         pane.el.addClass('active');
28210         pane.fireEvent('activate');
28211         
28212         
28213     },
28214     
28215     getActivePane : function()
28216     {
28217         var r = false;
28218         Roo.each(this.panes, function(p) {
28219             if(p.el.hasClass('active')){
28220                 r = p;
28221                 return false;
28222             }
28223             
28224             return;
28225         });
28226         
28227         return r;
28228     }
28229     
28230     
28231 });
28232
28233  
28234 /*
28235  * - LGPL
28236  *
28237  * Tab pane
28238  * 
28239  */
28240 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28241 /**
28242  * @class Roo.bootstrap.TabPane
28243  * @extends Roo.bootstrap.Component
28244  * Bootstrap TabPane class
28245  * @cfg {Boolean} active (false | true) Default false
28246  * @cfg {String} title title of panel
28247
28248  * 
28249  * @constructor
28250  * Create a new TabPane
28251  * @param {Object} config The config object
28252  */
28253
28254 Roo.bootstrap.dash.TabPane = function(config){
28255     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28256     
28257     this.addEvents({
28258         // raw events
28259         /**
28260          * @event activate
28261          * When a pane is activated
28262          * @param {Roo.bootstrap.dash.TabPane} pane
28263          */
28264         "activate" : true
28265          
28266     });
28267 };
28268
28269 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28270     
28271     active : false,
28272     title : '',
28273     
28274     // the tabBox that this is attached to.
28275     tab : false,
28276      
28277     getAutoCreate : function() 
28278     {
28279         var cfg = {
28280             tag: 'div',
28281             cls: 'tab-pane'
28282         };
28283         
28284         if(this.active){
28285             cfg.cls += ' active';
28286         }
28287         
28288         return cfg;
28289     },
28290     initEvents  : function()
28291     {
28292         //Roo.log('trigger add pane handler');
28293         this.parent().fireEvent('addpane', this)
28294     },
28295     
28296      /**
28297      * Updates the tab title 
28298      * @param {String} html to set the title to.
28299      */
28300     setTitle: function(str)
28301     {
28302         if (!this.tab) {
28303             return;
28304         }
28305         this.title = str;
28306         this.tab.select('a', true).first().dom.innerHTML = str;
28307         
28308     }
28309     
28310     
28311     
28312 });
28313
28314  
28315
28316
28317  /*
28318  * - LGPL
28319  *
28320  * menu
28321  * 
28322  */
28323 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28324
28325 /**
28326  * @class Roo.bootstrap.menu.Menu
28327  * @extends Roo.bootstrap.Component
28328  * Bootstrap Menu class - container for Menu
28329  * @cfg {String} html Text of the menu
28330  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28331  * @cfg {String} icon Font awesome icon
28332  * @cfg {String} pos Menu align to (top | bottom) default bottom
28333  * 
28334  * 
28335  * @constructor
28336  * Create a new Menu
28337  * @param {Object} config The config object
28338  */
28339
28340
28341 Roo.bootstrap.menu.Menu = function(config){
28342     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28343     
28344     this.addEvents({
28345         /**
28346          * @event beforeshow
28347          * Fires before this menu is displayed
28348          * @param {Roo.bootstrap.menu.Menu} this
28349          */
28350         beforeshow : true,
28351         /**
28352          * @event beforehide
28353          * Fires before this menu is hidden
28354          * @param {Roo.bootstrap.menu.Menu} this
28355          */
28356         beforehide : true,
28357         /**
28358          * @event show
28359          * Fires after this menu is displayed
28360          * @param {Roo.bootstrap.menu.Menu} this
28361          */
28362         show : true,
28363         /**
28364          * @event hide
28365          * Fires after this menu is hidden
28366          * @param {Roo.bootstrap.menu.Menu} this
28367          */
28368         hide : true,
28369         /**
28370          * @event click
28371          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28372          * @param {Roo.bootstrap.menu.Menu} this
28373          * @param {Roo.EventObject} e
28374          */
28375         click : true
28376     });
28377     
28378 };
28379
28380 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28381     
28382     submenu : false,
28383     html : '',
28384     weight : 'default',
28385     icon : false,
28386     pos : 'bottom',
28387     
28388     
28389     getChildContainer : function() {
28390         if(this.isSubMenu){
28391             return this.el;
28392         }
28393         
28394         return this.el.select('ul.dropdown-menu', true).first();  
28395     },
28396     
28397     getAutoCreate : function()
28398     {
28399         var text = [
28400             {
28401                 tag : 'span',
28402                 cls : 'roo-menu-text',
28403                 html : this.html
28404             }
28405         ];
28406         
28407         if(this.icon){
28408             text.unshift({
28409                 tag : 'i',
28410                 cls : 'fa ' + this.icon
28411             })
28412         }
28413         
28414         
28415         var cfg = {
28416             tag : 'div',
28417             cls : 'btn-group',
28418             cn : [
28419                 {
28420                     tag : 'button',
28421                     cls : 'dropdown-button btn btn-' + this.weight,
28422                     cn : text
28423                 },
28424                 {
28425                     tag : 'button',
28426                     cls : 'dropdown-toggle btn btn-' + this.weight,
28427                     cn : [
28428                         {
28429                             tag : 'span',
28430                             cls : 'caret'
28431                         }
28432                     ]
28433                 },
28434                 {
28435                     tag : 'ul',
28436                     cls : 'dropdown-menu'
28437                 }
28438             ]
28439             
28440         };
28441         
28442         if(this.pos == 'top'){
28443             cfg.cls += ' dropup';
28444         }
28445         
28446         if(this.isSubMenu){
28447             cfg = {
28448                 tag : 'ul',
28449                 cls : 'dropdown-menu'
28450             }
28451         }
28452         
28453         return cfg;
28454     },
28455     
28456     onRender : function(ct, position)
28457     {
28458         this.isSubMenu = ct.hasClass('dropdown-submenu');
28459         
28460         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28461     },
28462     
28463     initEvents : function() 
28464     {
28465         if(this.isSubMenu){
28466             return;
28467         }
28468         
28469         this.hidden = true;
28470         
28471         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28472         this.triggerEl.on('click', this.onTriggerPress, this);
28473         
28474         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28475         this.buttonEl.on('click', this.onClick, this);
28476         
28477     },
28478     
28479     list : function()
28480     {
28481         if(this.isSubMenu){
28482             return this.el;
28483         }
28484         
28485         return this.el.select('ul.dropdown-menu', true).first();
28486     },
28487     
28488     onClick : function(e)
28489     {
28490         this.fireEvent("click", this, e);
28491     },
28492     
28493     onTriggerPress  : function(e)
28494     {   
28495         if (this.isVisible()) {
28496             this.hide();
28497         } else {
28498             this.show();
28499         }
28500     },
28501     
28502     isVisible : function(){
28503         return !this.hidden;
28504     },
28505     
28506     show : function()
28507     {
28508         this.fireEvent("beforeshow", this);
28509         
28510         this.hidden = false;
28511         this.el.addClass('open');
28512         
28513         Roo.get(document).on("mouseup", this.onMouseUp, this);
28514         
28515         this.fireEvent("show", this);
28516         
28517         
28518     },
28519     
28520     hide : function()
28521     {
28522         this.fireEvent("beforehide", this);
28523         
28524         this.hidden = true;
28525         this.el.removeClass('open');
28526         
28527         Roo.get(document).un("mouseup", this.onMouseUp);
28528         
28529         this.fireEvent("hide", this);
28530     },
28531     
28532     onMouseUp : function()
28533     {
28534         this.hide();
28535     }
28536     
28537 });
28538
28539  
28540  /*
28541  * - LGPL
28542  *
28543  * menu item
28544  * 
28545  */
28546 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28547
28548 /**
28549  * @class Roo.bootstrap.menu.Item
28550  * @extends Roo.bootstrap.Component
28551  * Bootstrap MenuItem class
28552  * @cfg {Boolean} submenu (true | false) default false
28553  * @cfg {String} html text of the item
28554  * @cfg {String} href the link
28555  * @cfg {Boolean} disable (true | false) default false
28556  * @cfg {Boolean} preventDefault (true | false) default true
28557  * @cfg {String} icon Font awesome icon
28558  * @cfg {String} pos Submenu align to (left | right) default right 
28559  * 
28560  * 
28561  * @constructor
28562  * Create a new Item
28563  * @param {Object} config The config object
28564  */
28565
28566
28567 Roo.bootstrap.menu.Item = function(config){
28568     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28569     this.addEvents({
28570         /**
28571          * @event mouseover
28572          * Fires when the mouse is hovering over this menu
28573          * @param {Roo.bootstrap.menu.Item} this
28574          * @param {Roo.EventObject} e
28575          */
28576         mouseover : true,
28577         /**
28578          * @event mouseout
28579          * Fires when the mouse exits this menu
28580          * @param {Roo.bootstrap.menu.Item} this
28581          * @param {Roo.EventObject} e
28582          */
28583         mouseout : true,
28584         // raw events
28585         /**
28586          * @event click
28587          * The raw click event for the entire grid.
28588          * @param {Roo.EventObject} e
28589          */
28590         click : true
28591     });
28592 };
28593
28594 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28595     
28596     submenu : false,
28597     href : '',
28598     html : '',
28599     preventDefault: true,
28600     disable : false,
28601     icon : false,
28602     pos : 'right',
28603     
28604     getAutoCreate : function()
28605     {
28606         var text = [
28607             {
28608                 tag : 'span',
28609                 cls : 'roo-menu-item-text',
28610                 html : this.html
28611             }
28612         ];
28613         
28614         if(this.icon){
28615             text.unshift({
28616                 tag : 'i',
28617                 cls : 'fa ' + this.icon
28618             })
28619         }
28620         
28621         var cfg = {
28622             tag : 'li',
28623             cn : [
28624                 {
28625                     tag : 'a',
28626                     href : this.href || '#',
28627                     cn : text
28628                 }
28629             ]
28630         };
28631         
28632         if(this.disable){
28633             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28634         }
28635         
28636         if(this.submenu){
28637             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28638             
28639             if(this.pos == 'left'){
28640                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28641             }
28642         }
28643         
28644         return cfg;
28645     },
28646     
28647     initEvents : function() 
28648     {
28649         this.el.on('mouseover', this.onMouseOver, this);
28650         this.el.on('mouseout', this.onMouseOut, this);
28651         
28652         this.el.select('a', true).first().on('click', this.onClick, this);
28653         
28654     },
28655     
28656     onClick : function(e)
28657     {
28658         if(this.preventDefault){
28659             e.preventDefault();
28660         }
28661         
28662         this.fireEvent("click", this, e);
28663     },
28664     
28665     onMouseOver : function(e)
28666     {
28667         if(this.submenu && this.pos == 'left'){
28668             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28669         }
28670         
28671         this.fireEvent("mouseover", this, e);
28672     },
28673     
28674     onMouseOut : function(e)
28675     {
28676         this.fireEvent("mouseout", this, e);
28677     }
28678 });
28679
28680  
28681
28682  /*
28683  * - LGPL
28684  *
28685  * menu separator
28686  * 
28687  */
28688 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28689
28690 /**
28691  * @class Roo.bootstrap.menu.Separator
28692  * @extends Roo.bootstrap.Component
28693  * Bootstrap Separator class
28694  * 
28695  * @constructor
28696  * Create a new Separator
28697  * @param {Object} config The config object
28698  */
28699
28700
28701 Roo.bootstrap.menu.Separator = function(config){
28702     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28703 };
28704
28705 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28706     
28707     getAutoCreate : function(){
28708         var cfg = {
28709             tag : 'li',
28710             cls: 'divider'
28711         };
28712         
28713         return cfg;
28714     }
28715    
28716 });
28717
28718  
28719
28720  /*
28721  * - LGPL
28722  *
28723  * Tooltip
28724  * 
28725  */
28726
28727 /**
28728  * @class Roo.bootstrap.Tooltip
28729  * Bootstrap Tooltip class
28730  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28731  * to determine which dom element triggers the tooltip.
28732  * 
28733  * It needs to add support for additional attributes like tooltip-position
28734  * 
28735  * @constructor
28736  * Create a new Toolti
28737  * @param {Object} config The config object
28738  */
28739
28740 Roo.bootstrap.Tooltip = function(config){
28741     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28742     
28743     this.alignment = Roo.bootstrap.Tooltip.alignment;
28744     
28745     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28746         this.alignment = config.alignment;
28747     }
28748     
28749 };
28750
28751 Roo.apply(Roo.bootstrap.Tooltip, {
28752     /**
28753      * @function init initialize tooltip monitoring.
28754      * @static
28755      */
28756     currentEl : false,
28757     currentTip : false,
28758     currentRegion : false,
28759     
28760     //  init : delay?
28761     
28762     init : function()
28763     {
28764         Roo.get(document).on('mouseover', this.enter ,this);
28765         Roo.get(document).on('mouseout', this.leave, this);
28766          
28767         
28768         this.currentTip = new Roo.bootstrap.Tooltip();
28769     },
28770     
28771     enter : function(ev)
28772     {
28773         var dom = ev.getTarget();
28774         
28775         //Roo.log(['enter',dom]);
28776         var el = Roo.fly(dom);
28777         if (this.currentEl) {
28778             //Roo.log(dom);
28779             //Roo.log(this.currentEl);
28780             //Roo.log(this.currentEl.contains(dom));
28781             if (this.currentEl == el) {
28782                 return;
28783             }
28784             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28785                 return;
28786             }
28787
28788         }
28789         
28790         if (this.currentTip.el) {
28791             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28792         }    
28793         //Roo.log(ev);
28794         
28795         if(!el || el.dom == document){
28796             return;
28797         }
28798         
28799         var bindEl = el;
28800         
28801         // you can not look for children, as if el is the body.. then everythign is the child..
28802         if (!el.attr('tooltip')) { //
28803             if (!el.select("[tooltip]").elements.length) {
28804                 return;
28805             }
28806             // is the mouse over this child...?
28807             bindEl = el.select("[tooltip]").first();
28808             var xy = ev.getXY();
28809             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28810                 //Roo.log("not in region.");
28811                 return;
28812             }
28813             //Roo.log("child element over..");
28814             
28815         }
28816         this.currentEl = bindEl;
28817         this.currentTip.bind(bindEl);
28818         this.currentRegion = Roo.lib.Region.getRegion(dom);
28819         this.currentTip.enter();
28820         
28821     },
28822     leave : function(ev)
28823     {
28824         var dom = ev.getTarget();
28825         //Roo.log(['leave',dom]);
28826         if (!this.currentEl) {
28827             return;
28828         }
28829         
28830         
28831         if (dom != this.currentEl.dom) {
28832             return;
28833         }
28834         var xy = ev.getXY();
28835         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28836             return;
28837         }
28838         // only activate leave if mouse cursor is outside... bounding box..
28839         
28840         
28841         
28842         
28843         if (this.currentTip) {
28844             this.currentTip.leave();
28845         }
28846         //Roo.log('clear currentEl');
28847         this.currentEl = false;
28848         
28849         
28850     },
28851     alignment : {
28852         'left' : ['r-l', [-2,0], 'right'],
28853         'right' : ['l-r', [2,0], 'left'],
28854         'bottom' : ['t-b', [0,2], 'top'],
28855         'top' : [ 'b-t', [0,-2], 'bottom']
28856     }
28857     
28858 });
28859
28860
28861 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28862     
28863     
28864     bindEl : false,
28865     
28866     delay : null, // can be { show : 300 , hide: 500}
28867     
28868     timeout : null,
28869     
28870     hoverState : null, //???
28871     
28872     placement : 'bottom', 
28873     
28874     alignment : false,
28875     
28876     getAutoCreate : function(){
28877     
28878         var cfg = {
28879            cls : 'tooltip',   
28880            role : 'tooltip',
28881            cn : [
28882                 {
28883                     cls : 'tooltip-arrow arrow'
28884                 },
28885                 {
28886                     cls : 'tooltip-inner'
28887                 }
28888            ]
28889         };
28890         
28891         return cfg;
28892     },
28893     bind : function(el)
28894     {
28895         this.bindEl = el;
28896     },
28897     
28898     initEvents : function()
28899     {
28900         this.arrowEl = this.el.select('.arrow', true).first();
28901         this.innerEl = this.el.select('.tooltip-inner', true).first();
28902     },
28903     
28904     enter : function () {
28905        
28906         if (this.timeout != null) {
28907             clearTimeout(this.timeout);
28908         }
28909         
28910         this.hoverState = 'in';
28911          //Roo.log("enter - show");
28912         if (!this.delay || !this.delay.show) {
28913             this.show();
28914             return;
28915         }
28916         var _t = this;
28917         this.timeout = setTimeout(function () {
28918             if (_t.hoverState == 'in') {
28919                 _t.show();
28920             }
28921         }, this.delay.show);
28922     },
28923     leave : function()
28924     {
28925         clearTimeout(this.timeout);
28926     
28927         this.hoverState = 'out';
28928          if (!this.delay || !this.delay.hide) {
28929             this.hide();
28930             return;
28931         }
28932        
28933         var _t = this;
28934         this.timeout = setTimeout(function () {
28935             //Roo.log("leave - timeout");
28936             
28937             if (_t.hoverState == 'out') {
28938                 _t.hide();
28939                 Roo.bootstrap.Tooltip.currentEl = false;
28940             }
28941         }, delay);
28942     },
28943     
28944     show : function (msg)
28945     {
28946         if (!this.el) {
28947             this.render(document.body);
28948         }
28949         // set content.
28950         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28951         
28952         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28953         
28954         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28955         
28956         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28957                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28958         
28959         var placement = typeof this.placement == 'function' ?
28960             this.placement.call(this, this.el, on_el) :
28961             this.placement;
28962             
28963         var autoToken = /\s?auto?\s?/i;
28964         var autoPlace = autoToken.test(placement);
28965         if (autoPlace) {
28966             placement = placement.replace(autoToken, '') || 'top';
28967         }
28968         
28969         //this.el.detach()
28970         //this.el.setXY([0,0]);
28971         this.el.show();
28972         //this.el.dom.style.display='block';
28973         
28974         //this.el.appendTo(on_el);
28975         
28976         var p = this.getPosition();
28977         var box = this.el.getBox();
28978         
28979         if (autoPlace) {
28980             // fixme..
28981         }
28982         
28983         var align = this.alignment[placement];
28984         
28985         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28986         
28987         if(placement == 'top' || placement == 'bottom'){
28988             if(xy[0] < 0){
28989                 placement = 'right';
28990             }
28991             
28992             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28993                 placement = 'left';
28994             }
28995             
28996             var scroll = Roo.select('body', true).first().getScroll();
28997             
28998             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28999                 placement = 'top';
29000             }
29001             
29002             align = this.alignment[placement];
29003             
29004             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29005             
29006         }
29007         
29008         this.el.alignTo(this.bindEl, align[0],align[1]);
29009         //var arrow = this.el.select('.arrow',true).first();
29010         //arrow.set(align[2], 
29011         
29012         this.el.addClass(placement);
29013         this.el.addClass("bs-tooltip-"+ placement);
29014         
29015         this.el.addClass('in fade show');
29016         
29017         this.hoverState = null;
29018         
29019         if (this.el.hasClass('fade')) {
29020             // fade it?
29021         }
29022         
29023         
29024         
29025         
29026         
29027     },
29028     hide : function()
29029     {
29030          
29031         if (!this.el) {
29032             return;
29033         }
29034         //this.el.setXY([0,0]);
29035         this.el.removeClass(['show', 'in']);
29036         //this.el.hide();
29037         
29038     }
29039     
29040 });
29041  
29042
29043  /*
29044  * - LGPL
29045  *
29046  * Location Picker
29047  * 
29048  */
29049
29050 /**
29051  * @class Roo.bootstrap.LocationPicker
29052  * @extends Roo.bootstrap.Component
29053  * Bootstrap LocationPicker class
29054  * @cfg {Number} latitude Position when init default 0
29055  * @cfg {Number} longitude Position when init default 0
29056  * @cfg {Number} zoom default 15
29057  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29058  * @cfg {Boolean} mapTypeControl default false
29059  * @cfg {Boolean} disableDoubleClickZoom default false
29060  * @cfg {Boolean} scrollwheel default true
29061  * @cfg {Boolean} streetViewControl default false
29062  * @cfg {Number} radius default 0
29063  * @cfg {String} locationName
29064  * @cfg {Boolean} draggable default true
29065  * @cfg {Boolean} enableAutocomplete default false
29066  * @cfg {Boolean} enableReverseGeocode default true
29067  * @cfg {String} markerTitle
29068  * 
29069  * @constructor
29070  * Create a new LocationPicker
29071  * @param {Object} config The config object
29072  */
29073
29074
29075 Roo.bootstrap.LocationPicker = function(config){
29076     
29077     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29078     
29079     this.addEvents({
29080         /**
29081          * @event initial
29082          * Fires when the picker initialized.
29083          * @param {Roo.bootstrap.LocationPicker} this
29084          * @param {Google Location} location
29085          */
29086         initial : true,
29087         /**
29088          * @event positionchanged
29089          * Fires when the picker position changed.
29090          * @param {Roo.bootstrap.LocationPicker} this
29091          * @param {Google Location} location
29092          */
29093         positionchanged : true,
29094         /**
29095          * @event resize
29096          * Fires when the map resize.
29097          * @param {Roo.bootstrap.LocationPicker} this
29098          */
29099         resize : true,
29100         /**
29101          * @event show
29102          * Fires when the map show.
29103          * @param {Roo.bootstrap.LocationPicker} this
29104          */
29105         show : true,
29106         /**
29107          * @event hide
29108          * Fires when the map hide.
29109          * @param {Roo.bootstrap.LocationPicker} this
29110          */
29111         hide : true,
29112         /**
29113          * @event mapClick
29114          * Fires when click the map.
29115          * @param {Roo.bootstrap.LocationPicker} this
29116          * @param {Map event} e
29117          */
29118         mapClick : true,
29119         /**
29120          * @event mapRightClick
29121          * Fires when right click the map.
29122          * @param {Roo.bootstrap.LocationPicker} this
29123          * @param {Map event} e
29124          */
29125         mapRightClick : true,
29126         /**
29127          * @event markerClick
29128          * Fires when click the marker.
29129          * @param {Roo.bootstrap.LocationPicker} this
29130          * @param {Map event} e
29131          */
29132         markerClick : true,
29133         /**
29134          * @event markerRightClick
29135          * Fires when right click the marker.
29136          * @param {Roo.bootstrap.LocationPicker} this
29137          * @param {Map event} e
29138          */
29139         markerRightClick : true,
29140         /**
29141          * @event OverlayViewDraw
29142          * Fires when OverlayView Draw
29143          * @param {Roo.bootstrap.LocationPicker} this
29144          */
29145         OverlayViewDraw : true,
29146         /**
29147          * @event OverlayViewOnAdd
29148          * Fires when OverlayView Draw
29149          * @param {Roo.bootstrap.LocationPicker} this
29150          */
29151         OverlayViewOnAdd : true,
29152         /**
29153          * @event OverlayViewOnRemove
29154          * Fires when OverlayView Draw
29155          * @param {Roo.bootstrap.LocationPicker} this
29156          */
29157         OverlayViewOnRemove : true,
29158         /**
29159          * @event OverlayViewShow
29160          * Fires when OverlayView Draw
29161          * @param {Roo.bootstrap.LocationPicker} this
29162          * @param {Pixel} cpx
29163          */
29164         OverlayViewShow : true,
29165         /**
29166          * @event OverlayViewHide
29167          * Fires when OverlayView Draw
29168          * @param {Roo.bootstrap.LocationPicker} this
29169          */
29170         OverlayViewHide : true,
29171         /**
29172          * @event loadexception
29173          * Fires when load google lib failed.
29174          * @param {Roo.bootstrap.LocationPicker} this
29175          */
29176         loadexception : true
29177     });
29178         
29179 };
29180
29181 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29182     
29183     gMapContext: false,
29184     
29185     latitude: 0,
29186     longitude: 0,
29187     zoom: 15,
29188     mapTypeId: false,
29189     mapTypeControl: false,
29190     disableDoubleClickZoom: false,
29191     scrollwheel: true,
29192     streetViewControl: false,
29193     radius: 0,
29194     locationName: '',
29195     draggable: true,
29196     enableAutocomplete: false,
29197     enableReverseGeocode: true,
29198     markerTitle: '',
29199     
29200     getAutoCreate: function()
29201     {
29202
29203         var cfg = {
29204             tag: 'div',
29205             cls: 'roo-location-picker'
29206         };
29207         
29208         return cfg
29209     },
29210     
29211     initEvents: function(ct, position)
29212     {       
29213         if(!this.el.getWidth() || this.isApplied()){
29214             return;
29215         }
29216         
29217         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29218         
29219         this.initial();
29220     },
29221     
29222     initial: function()
29223     {
29224         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29225             this.fireEvent('loadexception', this);
29226             return;
29227         }
29228         
29229         if(!this.mapTypeId){
29230             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29231         }
29232         
29233         this.gMapContext = this.GMapContext();
29234         
29235         this.initOverlayView();
29236         
29237         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29238         
29239         var _this = this;
29240                 
29241         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29242             _this.setPosition(_this.gMapContext.marker.position);
29243         });
29244         
29245         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29246             _this.fireEvent('mapClick', this, event);
29247             
29248         });
29249
29250         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29251             _this.fireEvent('mapRightClick', this, event);
29252             
29253         });
29254         
29255         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29256             _this.fireEvent('markerClick', this, event);
29257             
29258         });
29259
29260         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29261             _this.fireEvent('markerRightClick', this, event);
29262             
29263         });
29264         
29265         this.setPosition(this.gMapContext.location);
29266         
29267         this.fireEvent('initial', this, this.gMapContext.location);
29268     },
29269     
29270     initOverlayView: function()
29271     {
29272         var _this = this;
29273         
29274         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29275             
29276             draw: function()
29277             {
29278                 _this.fireEvent('OverlayViewDraw', _this);
29279             },
29280             
29281             onAdd: function()
29282             {
29283                 _this.fireEvent('OverlayViewOnAdd', _this);
29284             },
29285             
29286             onRemove: function()
29287             {
29288                 _this.fireEvent('OverlayViewOnRemove', _this);
29289             },
29290             
29291             show: function(cpx)
29292             {
29293                 _this.fireEvent('OverlayViewShow', _this, cpx);
29294             },
29295             
29296             hide: function()
29297             {
29298                 _this.fireEvent('OverlayViewHide', _this);
29299             }
29300             
29301         });
29302     },
29303     
29304     fromLatLngToContainerPixel: function(event)
29305     {
29306         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29307     },
29308     
29309     isApplied: function() 
29310     {
29311         return this.getGmapContext() == false ? false : true;
29312     },
29313     
29314     getGmapContext: function() 
29315     {
29316         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29317     },
29318     
29319     GMapContext: function() 
29320     {
29321         var position = new google.maps.LatLng(this.latitude, this.longitude);
29322         
29323         var _map = new google.maps.Map(this.el.dom, {
29324             center: position,
29325             zoom: this.zoom,
29326             mapTypeId: this.mapTypeId,
29327             mapTypeControl: this.mapTypeControl,
29328             disableDoubleClickZoom: this.disableDoubleClickZoom,
29329             scrollwheel: this.scrollwheel,
29330             streetViewControl: this.streetViewControl,
29331             locationName: this.locationName,
29332             draggable: this.draggable,
29333             enableAutocomplete: this.enableAutocomplete,
29334             enableReverseGeocode: this.enableReverseGeocode
29335         });
29336         
29337         var _marker = new google.maps.Marker({
29338             position: position,
29339             map: _map,
29340             title: this.markerTitle,
29341             draggable: this.draggable
29342         });
29343         
29344         return {
29345             map: _map,
29346             marker: _marker,
29347             circle: null,
29348             location: position,
29349             radius: this.radius,
29350             locationName: this.locationName,
29351             addressComponents: {
29352                 formatted_address: null,
29353                 addressLine1: null,
29354                 addressLine2: null,
29355                 streetName: null,
29356                 streetNumber: null,
29357                 city: null,
29358                 district: null,
29359                 state: null,
29360                 stateOrProvince: null
29361             },
29362             settings: this,
29363             domContainer: this.el.dom,
29364             geodecoder: new google.maps.Geocoder()
29365         };
29366     },
29367     
29368     drawCircle: function(center, radius, options) 
29369     {
29370         if (this.gMapContext.circle != null) {
29371             this.gMapContext.circle.setMap(null);
29372         }
29373         if (radius > 0) {
29374             radius *= 1;
29375             options = Roo.apply({}, options, {
29376                 strokeColor: "#0000FF",
29377                 strokeOpacity: .35,
29378                 strokeWeight: 2,
29379                 fillColor: "#0000FF",
29380                 fillOpacity: .2
29381             });
29382             
29383             options.map = this.gMapContext.map;
29384             options.radius = radius;
29385             options.center = center;
29386             this.gMapContext.circle = new google.maps.Circle(options);
29387             return this.gMapContext.circle;
29388         }
29389         
29390         return null;
29391     },
29392     
29393     setPosition: function(location) 
29394     {
29395         this.gMapContext.location = location;
29396         this.gMapContext.marker.setPosition(location);
29397         this.gMapContext.map.panTo(location);
29398         this.drawCircle(location, this.gMapContext.radius, {});
29399         
29400         var _this = this;
29401         
29402         if (this.gMapContext.settings.enableReverseGeocode) {
29403             this.gMapContext.geodecoder.geocode({
29404                 latLng: this.gMapContext.location
29405             }, function(results, status) {
29406                 
29407                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29408                     _this.gMapContext.locationName = results[0].formatted_address;
29409                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29410                     
29411                     _this.fireEvent('positionchanged', this, location);
29412                 }
29413             });
29414             
29415             return;
29416         }
29417         
29418         this.fireEvent('positionchanged', this, location);
29419     },
29420     
29421     resize: function()
29422     {
29423         google.maps.event.trigger(this.gMapContext.map, "resize");
29424         
29425         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29426         
29427         this.fireEvent('resize', this);
29428     },
29429     
29430     setPositionByLatLng: function(latitude, longitude)
29431     {
29432         this.setPosition(new google.maps.LatLng(latitude, longitude));
29433     },
29434     
29435     getCurrentPosition: function() 
29436     {
29437         return {
29438             latitude: this.gMapContext.location.lat(),
29439             longitude: this.gMapContext.location.lng()
29440         };
29441     },
29442     
29443     getAddressName: function() 
29444     {
29445         return this.gMapContext.locationName;
29446     },
29447     
29448     getAddressComponents: function() 
29449     {
29450         return this.gMapContext.addressComponents;
29451     },
29452     
29453     address_component_from_google_geocode: function(address_components) 
29454     {
29455         var result = {};
29456         
29457         for (var i = 0; i < address_components.length; i++) {
29458             var component = address_components[i];
29459             if (component.types.indexOf("postal_code") >= 0) {
29460                 result.postalCode = component.short_name;
29461             } else if (component.types.indexOf("street_number") >= 0) {
29462                 result.streetNumber = component.short_name;
29463             } else if (component.types.indexOf("route") >= 0) {
29464                 result.streetName = component.short_name;
29465             } else if (component.types.indexOf("neighborhood") >= 0) {
29466                 result.city = component.short_name;
29467             } else if (component.types.indexOf("locality") >= 0) {
29468                 result.city = component.short_name;
29469             } else if (component.types.indexOf("sublocality") >= 0) {
29470                 result.district = component.short_name;
29471             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29472                 result.stateOrProvince = component.short_name;
29473             } else if (component.types.indexOf("country") >= 0) {
29474                 result.country = component.short_name;
29475             }
29476         }
29477         
29478         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29479         result.addressLine2 = "";
29480         return result;
29481     },
29482     
29483     setZoomLevel: function(zoom)
29484     {
29485         this.gMapContext.map.setZoom(zoom);
29486     },
29487     
29488     show: function()
29489     {
29490         if(!this.el){
29491             return;
29492         }
29493         
29494         this.el.show();
29495         
29496         this.resize();
29497         
29498         this.fireEvent('show', this);
29499     },
29500     
29501     hide: function()
29502     {
29503         if(!this.el){
29504             return;
29505         }
29506         
29507         this.el.hide();
29508         
29509         this.fireEvent('hide', this);
29510     }
29511     
29512 });
29513
29514 Roo.apply(Roo.bootstrap.LocationPicker, {
29515     
29516     OverlayView : function(map, options)
29517     {
29518         options = options || {};
29519         
29520         this.setMap(map);
29521     }
29522     
29523     
29524 });/**
29525  * @class Roo.bootstrap.Alert
29526  * @extends Roo.bootstrap.Component
29527  * Bootstrap Alert class - shows an alert area box
29528  * eg
29529  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29530   Enter a valid email address
29531 </div>
29532  * @licence LGPL
29533  * @cfg {String} title The title of alert
29534  * @cfg {String} html The content of alert
29535  * @cfg {String} weight (  success | info | warning | danger )
29536  * @cfg {String} faicon font-awesomeicon
29537  * 
29538  * @constructor
29539  * Create a new alert
29540  * @param {Object} config The config object
29541  */
29542
29543
29544 Roo.bootstrap.Alert = function(config){
29545     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29546     
29547 };
29548
29549 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29550     
29551     title: '',
29552     html: '',
29553     weight: false,
29554     faicon: false,
29555     
29556     getAutoCreate : function()
29557     {
29558         
29559         var cfg = {
29560             tag : 'div',
29561             cls : 'alert',
29562             cn : [
29563                 {
29564                     tag : 'i',
29565                     cls : 'roo-alert-icon'
29566                     
29567                 },
29568                 {
29569                     tag : 'b',
29570                     cls : 'roo-alert-title',
29571                     html : this.title
29572                 },
29573                 {
29574                     tag : 'span',
29575                     cls : 'roo-alert-text',
29576                     html : this.html
29577                 }
29578             ]
29579         };
29580         
29581         if(this.faicon){
29582             cfg.cn[0].cls += ' fa ' + this.faicon;
29583         }
29584         
29585         if(this.weight){
29586             cfg.cls += ' alert-' + this.weight;
29587         }
29588         
29589         return cfg;
29590     },
29591     
29592     initEvents: function() 
29593     {
29594         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29595     },
29596     
29597     setTitle : function(str)
29598     {
29599         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29600     },
29601     
29602     setText : function(str)
29603     {
29604         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29605     },
29606     
29607     setWeight : function(weight)
29608     {
29609         if(this.weight){
29610             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29611         }
29612         
29613         this.weight = weight;
29614         
29615         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29616     },
29617     
29618     setIcon : function(icon)
29619     {
29620         if(this.faicon){
29621             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29622         }
29623         
29624         this.faicon = icon;
29625         
29626         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29627     },
29628     
29629     hide: function() 
29630     {
29631         this.el.hide();   
29632     },
29633     
29634     show: function() 
29635     {  
29636         this.el.show();   
29637     }
29638     
29639 });
29640
29641  
29642 /*
29643 * Licence: LGPL
29644 */
29645
29646 /**
29647  * @class Roo.bootstrap.UploadCropbox
29648  * @extends Roo.bootstrap.Component
29649  * Bootstrap UploadCropbox class
29650  * @cfg {String} emptyText show when image has been loaded
29651  * @cfg {String} rotateNotify show when image too small to rotate
29652  * @cfg {Number} errorTimeout default 3000
29653  * @cfg {Number} minWidth default 300
29654  * @cfg {Number} minHeight default 300
29655  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29656  * @cfg {Boolean} isDocument (true|false) default false
29657  * @cfg {String} url action url
29658  * @cfg {String} paramName default 'imageUpload'
29659  * @cfg {String} method default POST
29660  * @cfg {Boolean} loadMask (true|false) default true
29661  * @cfg {Boolean} loadingText default 'Loading...'
29662  * 
29663  * @constructor
29664  * Create a new UploadCropbox
29665  * @param {Object} config The config object
29666  */
29667
29668 Roo.bootstrap.UploadCropbox = function(config){
29669     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29670     
29671     this.addEvents({
29672         /**
29673          * @event beforeselectfile
29674          * Fire before select file
29675          * @param {Roo.bootstrap.UploadCropbox} this
29676          */
29677         "beforeselectfile" : true,
29678         /**
29679          * @event initial
29680          * Fire after initEvent
29681          * @param {Roo.bootstrap.UploadCropbox} this
29682          */
29683         "initial" : true,
29684         /**
29685          * @event crop
29686          * Fire after initEvent
29687          * @param {Roo.bootstrap.UploadCropbox} this
29688          * @param {String} data
29689          */
29690         "crop" : true,
29691         /**
29692          * @event prepare
29693          * Fire when preparing the file data
29694          * @param {Roo.bootstrap.UploadCropbox} this
29695          * @param {Object} file
29696          */
29697         "prepare" : true,
29698         /**
29699          * @event exception
29700          * Fire when get exception
29701          * @param {Roo.bootstrap.UploadCropbox} this
29702          * @param {XMLHttpRequest} xhr
29703          */
29704         "exception" : true,
29705         /**
29706          * @event beforeloadcanvas
29707          * Fire before load the canvas
29708          * @param {Roo.bootstrap.UploadCropbox} this
29709          * @param {String} src
29710          */
29711         "beforeloadcanvas" : true,
29712         /**
29713          * @event trash
29714          * Fire when trash image
29715          * @param {Roo.bootstrap.UploadCropbox} this
29716          */
29717         "trash" : true,
29718         /**
29719          * @event download
29720          * Fire when download the image
29721          * @param {Roo.bootstrap.UploadCropbox} this
29722          */
29723         "download" : true,
29724         /**
29725          * @event footerbuttonclick
29726          * Fire when footerbuttonclick
29727          * @param {Roo.bootstrap.UploadCropbox} this
29728          * @param {String} type
29729          */
29730         "footerbuttonclick" : true,
29731         /**
29732          * @event resize
29733          * Fire when resize
29734          * @param {Roo.bootstrap.UploadCropbox} this
29735          */
29736         "resize" : true,
29737         /**
29738          * @event rotate
29739          * Fire when rotate the image
29740          * @param {Roo.bootstrap.UploadCropbox} this
29741          * @param {String} pos
29742          */
29743         "rotate" : true,
29744         /**
29745          * @event inspect
29746          * Fire when inspect the file
29747          * @param {Roo.bootstrap.UploadCropbox} this
29748          * @param {Object} file
29749          */
29750         "inspect" : true,
29751         /**
29752          * @event upload
29753          * Fire when xhr upload the file
29754          * @param {Roo.bootstrap.UploadCropbox} this
29755          * @param {Object} data
29756          */
29757         "upload" : true,
29758         /**
29759          * @event arrange
29760          * Fire when arrange the file data
29761          * @param {Roo.bootstrap.UploadCropbox} this
29762          * @param {Object} formData
29763          */
29764         "arrange" : true
29765     });
29766     
29767     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29768 };
29769
29770 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29771     
29772     emptyText : 'Click to upload image',
29773     rotateNotify : 'Image is too small to rotate',
29774     errorTimeout : 3000,
29775     scale : 0,
29776     baseScale : 1,
29777     rotate : 0,
29778     dragable : false,
29779     pinching : false,
29780     mouseX : 0,
29781     mouseY : 0,
29782     cropData : false,
29783     minWidth : 300,
29784     minHeight : 300,
29785     file : false,
29786     exif : {},
29787     baseRotate : 1,
29788     cropType : 'image/jpeg',
29789     buttons : false,
29790     canvasLoaded : false,
29791     isDocument : false,
29792     method : 'POST',
29793     paramName : 'imageUpload',
29794     loadMask : true,
29795     loadingText : 'Loading...',
29796     maskEl : false,
29797     
29798     getAutoCreate : function()
29799     {
29800         var cfg = {
29801             tag : 'div',
29802             cls : 'roo-upload-cropbox',
29803             cn : [
29804                 {
29805                     tag : 'input',
29806                     cls : 'roo-upload-cropbox-selector',
29807                     type : 'file'
29808                 },
29809                 {
29810                     tag : 'div',
29811                     cls : 'roo-upload-cropbox-body',
29812                     style : 'cursor:pointer',
29813                     cn : [
29814                         {
29815                             tag : 'div',
29816                             cls : 'roo-upload-cropbox-preview'
29817                         },
29818                         {
29819                             tag : 'div',
29820                             cls : 'roo-upload-cropbox-thumb'
29821                         },
29822                         {
29823                             tag : 'div',
29824                             cls : 'roo-upload-cropbox-empty-notify',
29825                             html : this.emptyText
29826                         },
29827                         {
29828                             tag : 'div',
29829                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29830                             html : this.rotateNotify
29831                         }
29832                     ]
29833                 },
29834                 {
29835                     tag : 'div',
29836                     cls : 'roo-upload-cropbox-footer',
29837                     cn : {
29838                         tag : 'div',
29839                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29840                         cn : []
29841                     }
29842                 }
29843             ]
29844         };
29845         
29846         return cfg;
29847     },
29848     
29849     onRender : function(ct, position)
29850     {
29851         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29852         
29853         if (this.buttons.length) {
29854             
29855             Roo.each(this.buttons, function(bb) {
29856                 
29857                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29858                 
29859                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29860                 
29861             }, this);
29862         }
29863         
29864         if(this.loadMask){
29865             this.maskEl = this.el;
29866         }
29867     },
29868     
29869     initEvents : function()
29870     {
29871         this.urlAPI = (window.createObjectURL && window) || 
29872                                 (window.URL && URL.revokeObjectURL && URL) || 
29873                                 (window.webkitURL && webkitURL);
29874                         
29875         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29876         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29877         
29878         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29879         this.selectorEl.hide();
29880         
29881         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29882         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29883         
29884         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29885         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29886         this.thumbEl.hide();
29887         
29888         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29889         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29890         
29891         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29892         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29893         this.errorEl.hide();
29894         
29895         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29896         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29897         this.footerEl.hide();
29898         
29899         this.setThumbBoxSize();
29900         
29901         this.bind();
29902         
29903         this.resize();
29904         
29905         this.fireEvent('initial', this);
29906     },
29907
29908     bind : function()
29909     {
29910         var _this = this;
29911         
29912         window.addEventListener("resize", function() { _this.resize(); } );
29913         
29914         this.bodyEl.on('click', this.beforeSelectFile, this);
29915         
29916         if(Roo.isTouch){
29917             this.bodyEl.on('touchstart', this.onTouchStart, this);
29918             this.bodyEl.on('touchmove', this.onTouchMove, this);
29919             this.bodyEl.on('touchend', this.onTouchEnd, this);
29920         }
29921         
29922         if(!Roo.isTouch){
29923             this.bodyEl.on('mousedown', this.onMouseDown, this);
29924             this.bodyEl.on('mousemove', this.onMouseMove, this);
29925             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29926             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29927             Roo.get(document).on('mouseup', this.onMouseUp, this);
29928         }
29929         
29930         this.selectorEl.on('change', this.onFileSelected, this);
29931     },
29932     
29933     reset : function()
29934     {    
29935         this.scale = 0;
29936         this.baseScale = 1;
29937         this.rotate = 0;
29938         this.baseRotate = 1;
29939         this.dragable = false;
29940         this.pinching = false;
29941         this.mouseX = 0;
29942         this.mouseY = 0;
29943         this.cropData = false;
29944         this.notifyEl.dom.innerHTML = this.emptyText;
29945         
29946         this.selectorEl.dom.value = '';
29947         
29948     },
29949     
29950     resize : function()
29951     {
29952         if(this.fireEvent('resize', this) != false){
29953             this.setThumbBoxPosition();
29954             this.setCanvasPosition();
29955         }
29956     },
29957     
29958     onFooterButtonClick : function(e, el, o, type)
29959     {
29960         switch (type) {
29961             case 'rotate-left' :
29962                 this.onRotateLeft(e);
29963                 break;
29964             case 'rotate-right' :
29965                 this.onRotateRight(e);
29966                 break;
29967             case 'picture' :
29968                 this.beforeSelectFile(e);
29969                 break;
29970             case 'trash' :
29971                 this.trash(e);
29972                 break;
29973             case 'crop' :
29974                 this.crop(e);
29975                 break;
29976             case 'download' :
29977                 this.download(e);
29978                 break;
29979             default :
29980                 break;
29981         }
29982         
29983         this.fireEvent('footerbuttonclick', this, type);
29984     },
29985     
29986     beforeSelectFile : function(e)
29987     {
29988         e.preventDefault();
29989         
29990         if(this.fireEvent('beforeselectfile', this) != false){
29991             this.selectorEl.dom.click();
29992         }
29993     },
29994     
29995     onFileSelected : function(e)
29996     {
29997         e.preventDefault();
29998         
29999         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30000             return;
30001         }
30002         
30003         var file = this.selectorEl.dom.files[0];
30004         
30005         if(this.fireEvent('inspect', this, file) != false){
30006             this.prepare(file);
30007         }
30008         
30009     },
30010     
30011     trash : function(e)
30012     {
30013         this.fireEvent('trash', this);
30014     },
30015     
30016     download : function(e)
30017     {
30018         this.fireEvent('download', this);
30019     },
30020     
30021     loadCanvas : function(src)
30022     {   
30023         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30024             
30025             this.reset();
30026             
30027             this.imageEl = document.createElement('img');
30028             
30029             var _this = this;
30030             
30031             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30032             
30033             this.imageEl.src = src;
30034         }
30035     },
30036     
30037     onLoadCanvas : function()
30038     {   
30039         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30040         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30041         
30042         this.bodyEl.un('click', this.beforeSelectFile, this);
30043         
30044         this.notifyEl.hide();
30045         this.thumbEl.show();
30046         this.footerEl.show();
30047         
30048         this.baseRotateLevel();
30049         
30050         if(this.isDocument){
30051             this.setThumbBoxSize();
30052         }
30053         
30054         this.setThumbBoxPosition();
30055         
30056         this.baseScaleLevel();
30057         
30058         this.draw();
30059         
30060         this.resize();
30061         
30062         this.canvasLoaded = true;
30063         
30064         if(this.loadMask){
30065             this.maskEl.unmask();
30066         }
30067         
30068     },
30069     
30070     setCanvasPosition : function()
30071     {   
30072         if(!this.canvasEl){
30073             return;
30074         }
30075         
30076         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30077         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30078         
30079         this.previewEl.setLeft(pw);
30080         this.previewEl.setTop(ph);
30081         
30082     },
30083     
30084     onMouseDown : function(e)
30085     {   
30086         e.stopEvent();
30087         
30088         this.dragable = true;
30089         this.pinching = false;
30090         
30091         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30092             this.dragable = false;
30093             return;
30094         }
30095         
30096         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30097         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30098         
30099     },
30100     
30101     onMouseMove : function(e)
30102     {   
30103         e.stopEvent();
30104         
30105         if(!this.canvasLoaded){
30106             return;
30107         }
30108         
30109         if (!this.dragable){
30110             return;
30111         }
30112         
30113         var minX = Math.ceil(this.thumbEl.getLeft(true));
30114         var minY = Math.ceil(this.thumbEl.getTop(true));
30115         
30116         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30117         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30118         
30119         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30120         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30121         
30122         x = x - this.mouseX;
30123         y = y - this.mouseY;
30124         
30125         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30126         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30127         
30128         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30129         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30130         
30131         this.previewEl.setLeft(bgX);
30132         this.previewEl.setTop(bgY);
30133         
30134         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30135         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30136     },
30137     
30138     onMouseUp : function(e)
30139     {   
30140         e.stopEvent();
30141         
30142         this.dragable = false;
30143     },
30144     
30145     onMouseWheel : function(e)
30146     {   
30147         e.stopEvent();
30148         
30149         this.startScale = this.scale;
30150         
30151         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30152         
30153         if(!this.zoomable()){
30154             this.scale = this.startScale;
30155             return;
30156         }
30157         
30158         this.draw();
30159         
30160         return;
30161     },
30162     
30163     zoomable : function()
30164     {
30165         var minScale = this.thumbEl.getWidth() / this.minWidth;
30166         
30167         if(this.minWidth < this.minHeight){
30168             minScale = this.thumbEl.getHeight() / this.minHeight;
30169         }
30170         
30171         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30172         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30173         
30174         if(
30175                 this.isDocument &&
30176                 (this.rotate == 0 || this.rotate == 180) && 
30177                 (
30178                     width > this.imageEl.OriginWidth || 
30179                     height > this.imageEl.OriginHeight ||
30180                     (width < this.minWidth && height < this.minHeight)
30181                 )
30182         ){
30183             return false;
30184         }
30185         
30186         if(
30187                 this.isDocument &&
30188                 (this.rotate == 90 || this.rotate == 270) && 
30189                 (
30190                     width > this.imageEl.OriginWidth || 
30191                     height > this.imageEl.OriginHeight ||
30192                     (width < this.minHeight && height < this.minWidth)
30193                 )
30194         ){
30195             return false;
30196         }
30197         
30198         if(
30199                 !this.isDocument &&
30200                 (this.rotate == 0 || this.rotate == 180) && 
30201                 (
30202                     width < this.minWidth || 
30203                     width > this.imageEl.OriginWidth || 
30204                     height < this.minHeight || 
30205                     height > this.imageEl.OriginHeight
30206                 )
30207         ){
30208             return false;
30209         }
30210         
30211         if(
30212                 !this.isDocument &&
30213                 (this.rotate == 90 || this.rotate == 270) && 
30214                 (
30215                     width < this.minHeight || 
30216                     width > this.imageEl.OriginWidth || 
30217                     height < this.minWidth || 
30218                     height > this.imageEl.OriginHeight
30219                 )
30220         ){
30221             return false;
30222         }
30223         
30224         return true;
30225         
30226     },
30227     
30228     onRotateLeft : function(e)
30229     {   
30230         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30231             
30232             var minScale = this.thumbEl.getWidth() / this.minWidth;
30233             
30234             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30235             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30236             
30237             this.startScale = this.scale;
30238             
30239             while (this.getScaleLevel() < minScale){
30240             
30241                 this.scale = this.scale + 1;
30242                 
30243                 if(!this.zoomable()){
30244                     break;
30245                 }
30246                 
30247                 if(
30248                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30249                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30250                 ){
30251                     continue;
30252                 }
30253                 
30254                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30255
30256                 this.draw();
30257                 
30258                 return;
30259             }
30260             
30261             this.scale = this.startScale;
30262             
30263             this.onRotateFail();
30264             
30265             return false;
30266         }
30267         
30268         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30269
30270         if(this.isDocument){
30271             this.setThumbBoxSize();
30272             this.setThumbBoxPosition();
30273             this.setCanvasPosition();
30274         }
30275         
30276         this.draw();
30277         
30278         this.fireEvent('rotate', this, 'left');
30279         
30280     },
30281     
30282     onRotateRight : function(e)
30283     {
30284         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30285             
30286             var minScale = this.thumbEl.getWidth() / this.minWidth;
30287         
30288             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30289             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30290             
30291             this.startScale = this.scale;
30292             
30293             while (this.getScaleLevel() < minScale){
30294             
30295                 this.scale = this.scale + 1;
30296                 
30297                 if(!this.zoomable()){
30298                     break;
30299                 }
30300                 
30301                 if(
30302                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30303                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30304                 ){
30305                     continue;
30306                 }
30307                 
30308                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30309
30310                 this.draw();
30311                 
30312                 return;
30313             }
30314             
30315             this.scale = this.startScale;
30316             
30317             this.onRotateFail();
30318             
30319             return false;
30320         }
30321         
30322         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30323
30324         if(this.isDocument){
30325             this.setThumbBoxSize();
30326             this.setThumbBoxPosition();
30327             this.setCanvasPosition();
30328         }
30329         
30330         this.draw();
30331         
30332         this.fireEvent('rotate', this, 'right');
30333     },
30334     
30335     onRotateFail : function()
30336     {
30337         this.errorEl.show(true);
30338         
30339         var _this = this;
30340         
30341         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30342     },
30343     
30344     draw : function()
30345     {
30346         this.previewEl.dom.innerHTML = '';
30347         
30348         var canvasEl = document.createElement("canvas");
30349         
30350         var contextEl = canvasEl.getContext("2d");
30351         
30352         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30353         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30354         var center = this.imageEl.OriginWidth / 2;
30355         
30356         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30357             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30358             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30359             center = this.imageEl.OriginHeight / 2;
30360         }
30361         
30362         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30363         
30364         contextEl.translate(center, center);
30365         contextEl.rotate(this.rotate * Math.PI / 180);
30366
30367         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30368         
30369         this.canvasEl = document.createElement("canvas");
30370         
30371         this.contextEl = this.canvasEl.getContext("2d");
30372         
30373         switch (this.rotate) {
30374             case 0 :
30375                 
30376                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30377                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30378                 
30379                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30380                 
30381                 break;
30382             case 90 : 
30383                 
30384                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30385                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30386                 
30387                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30388                     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);
30389                     break;
30390                 }
30391                 
30392                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30393                 
30394                 break;
30395             case 180 :
30396                 
30397                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30398                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30399                 
30400                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30401                     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);
30402                     break;
30403                 }
30404                 
30405                 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);
30406                 
30407                 break;
30408             case 270 :
30409                 
30410                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30411                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30412         
30413                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30414                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30415                     break;
30416                 }
30417                 
30418                 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);
30419                 
30420                 break;
30421             default : 
30422                 break;
30423         }
30424         
30425         this.previewEl.appendChild(this.canvasEl);
30426         
30427         this.setCanvasPosition();
30428     },
30429     
30430     crop : function()
30431     {
30432         if(!this.canvasLoaded){
30433             return;
30434         }
30435         
30436         var imageCanvas = document.createElement("canvas");
30437         
30438         var imageContext = imageCanvas.getContext("2d");
30439         
30440         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30441         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30442         
30443         var center = imageCanvas.width / 2;
30444         
30445         imageContext.translate(center, center);
30446         
30447         imageContext.rotate(this.rotate * Math.PI / 180);
30448         
30449         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30450         
30451         var canvas = document.createElement("canvas");
30452         
30453         var context = canvas.getContext("2d");
30454                 
30455         canvas.width = this.minWidth;
30456         canvas.height = this.minHeight;
30457
30458         switch (this.rotate) {
30459             case 0 :
30460                 
30461                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30462                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (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                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30497                 
30498                 break;
30499             case 90 : 
30500                 
30501                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30502                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30503                 
30504                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30505                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30506                 
30507                 var targetWidth = this.minWidth - 2 * x;
30508                 var targetHeight = this.minHeight - 2 * y;
30509                 
30510                 var scale = 1;
30511                 
30512                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30513                     scale = targetWidth / width;
30514                 }
30515                 
30516                 if(x > 0 && y == 0){
30517                     scale = targetHeight / height;
30518                 }
30519                 
30520                 if(x > 0 && y > 0){
30521                     scale = targetWidth / width;
30522                     
30523                     if(width < height){
30524                         scale = targetHeight / height;
30525                     }
30526                 }
30527                 
30528                 context.scale(scale, scale);
30529                 
30530                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30531                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30532
30533                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30534                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30535                 
30536                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30537                 
30538                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30539                 
30540                 break;
30541             case 180 :
30542                 
30543                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30544                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30545                 
30546                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30547                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30548                 
30549                 var targetWidth = this.minWidth - 2 * x;
30550                 var targetHeight = this.minHeight - 2 * y;
30551                 
30552                 var scale = 1;
30553                 
30554                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30555                     scale = targetWidth / width;
30556                 }
30557                 
30558                 if(x > 0 && y == 0){
30559                     scale = targetHeight / height;
30560                 }
30561                 
30562                 if(x > 0 && y > 0){
30563                     scale = targetWidth / width;
30564                     
30565                     if(width < height){
30566                         scale = targetHeight / height;
30567                     }
30568                 }
30569                 
30570                 context.scale(scale, scale);
30571                 
30572                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30573                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30574
30575                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30576                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30577
30578                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30579                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30580                 
30581                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30582                 
30583                 break;
30584             case 270 :
30585                 
30586                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30587                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30588                 
30589                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30590                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30591                 
30592                 var targetWidth = this.minWidth - 2 * x;
30593                 var targetHeight = this.minHeight - 2 * y;
30594                 
30595                 var scale = 1;
30596                 
30597                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30598                     scale = targetWidth / width;
30599                 }
30600                 
30601                 if(x > 0 && y == 0){
30602                     scale = targetHeight / height;
30603                 }
30604                 
30605                 if(x > 0 && y > 0){
30606                     scale = targetWidth / width;
30607                     
30608                     if(width < height){
30609                         scale = targetHeight / height;
30610                     }
30611                 }
30612                 
30613                 context.scale(scale, scale);
30614                 
30615                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30616                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30617
30618                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30619                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30620                 
30621                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30622                 
30623                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30624                 
30625                 break;
30626             default : 
30627                 break;
30628         }
30629         
30630         this.cropData = canvas.toDataURL(this.cropType);
30631         
30632         if(this.fireEvent('crop', this, this.cropData) !== false){
30633             this.process(this.file, this.cropData);
30634         }
30635         
30636         return;
30637         
30638     },
30639     
30640     setThumbBoxSize : function()
30641     {
30642         var width, height;
30643         
30644         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30645             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30646             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30647             
30648             this.minWidth = width;
30649             this.minHeight = height;
30650             
30651             if(this.rotate == 90 || this.rotate == 270){
30652                 this.minWidth = height;
30653                 this.minHeight = width;
30654             }
30655         }
30656         
30657         height = 300;
30658         width = Math.ceil(this.minWidth * height / this.minHeight);
30659         
30660         if(this.minWidth > this.minHeight){
30661             width = 300;
30662             height = Math.ceil(this.minHeight * width / this.minWidth);
30663         }
30664         
30665         this.thumbEl.setStyle({
30666             width : width + 'px',
30667             height : height + 'px'
30668         });
30669
30670         return;
30671             
30672     },
30673     
30674     setThumbBoxPosition : function()
30675     {
30676         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30677         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30678         
30679         this.thumbEl.setLeft(x);
30680         this.thumbEl.setTop(y);
30681         
30682     },
30683     
30684     baseRotateLevel : function()
30685     {
30686         this.baseRotate = 1;
30687         
30688         if(
30689                 typeof(this.exif) != 'undefined' &&
30690                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30691                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30692         ){
30693             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30694         }
30695         
30696         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30697         
30698     },
30699     
30700     baseScaleLevel : function()
30701     {
30702         var width, height;
30703         
30704         if(this.isDocument){
30705             
30706             if(this.baseRotate == 6 || this.baseRotate == 8){
30707             
30708                 height = this.thumbEl.getHeight();
30709                 this.baseScale = height / this.imageEl.OriginWidth;
30710
30711                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30712                     width = this.thumbEl.getWidth();
30713                     this.baseScale = width / this.imageEl.OriginHeight;
30714                 }
30715
30716                 return;
30717             }
30718
30719             height = this.thumbEl.getHeight();
30720             this.baseScale = height / this.imageEl.OriginHeight;
30721
30722             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30723                 width = this.thumbEl.getWidth();
30724                 this.baseScale = width / this.imageEl.OriginWidth;
30725             }
30726
30727             return;
30728         }
30729         
30730         if(this.baseRotate == 6 || this.baseRotate == 8){
30731             
30732             width = this.thumbEl.getHeight();
30733             this.baseScale = width / this.imageEl.OriginHeight;
30734             
30735             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30736                 height = this.thumbEl.getWidth();
30737                 this.baseScale = height / this.imageEl.OriginHeight;
30738             }
30739             
30740             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30741                 height = this.thumbEl.getWidth();
30742                 this.baseScale = height / this.imageEl.OriginHeight;
30743                 
30744                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30745                     width = this.thumbEl.getHeight();
30746                     this.baseScale = width / this.imageEl.OriginWidth;
30747                 }
30748             }
30749             
30750             return;
30751         }
30752         
30753         width = this.thumbEl.getWidth();
30754         this.baseScale = width / this.imageEl.OriginWidth;
30755         
30756         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30757             height = this.thumbEl.getHeight();
30758             this.baseScale = height / this.imageEl.OriginHeight;
30759         }
30760         
30761         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30762             
30763             height = this.thumbEl.getHeight();
30764             this.baseScale = height / this.imageEl.OriginHeight;
30765             
30766             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30767                 width = this.thumbEl.getWidth();
30768                 this.baseScale = width / this.imageEl.OriginWidth;
30769             }
30770             
30771         }
30772         
30773         return;
30774     },
30775     
30776     getScaleLevel : function()
30777     {
30778         return this.baseScale * Math.pow(1.1, this.scale);
30779     },
30780     
30781     onTouchStart : function(e)
30782     {
30783         if(!this.canvasLoaded){
30784             this.beforeSelectFile(e);
30785             return;
30786         }
30787         
30788         var touches = e.browserEvent.touches;
30789         
30790         if(!touches){
30791             return;
30792         }
30793         
30794         if(touches.length == 1){
30795             this.onMouseDown(e);
30796             return;
30797         }
30798         
30799         if(touches.length != 2){
30800             return;
30801         }
30802         
30803         var coords = [];
30804         
30805         for(var i = 0, finger; finger = touches[i]; i++){
30806             coords.push(finger.pageX, finger.pageY);
30807         }
30808         
30809         var x = Math.pow(coords[0] - coords[2], 2);
30810         var y = Math.pow(coords[1] - coords[3], 2);
30811         
30812         this.startDistance = Math.sqrt(x + y);
30813         
30814         this.startScale = this.scale;
30815         
30816         this.pinching = true;
30817         this.dragable = false;
30818         
30819     },
30820     
30821     onTouchMove : function(e)
30822     {
30823         if(!this.pinching && !this.dragable){
30824             return;
30825         }
30826         
30827         var touches = e.browserEvent.touches;
30828         
30829         if(!touches){
30830             return;
30831         }
30832         
30833         if(this.dragable){
30834             this.onMouseMove(e);
30835             return;
30836         }
30837         
30838         var coords = [];
30839         
30840         for(var i = 0, finger; finger = touches[i]; i++){
30841             coords.push(finger.pageX, finger.pageY);
30842         }
30843         
30844         var x = Math.pow(coords[0] - coords[2], 2);
30845         var y = Math.pow(coords[1] - coords[3], 2);
30846         
30847         this.endDistance = Math.sqrt(x + y);
30848         
30849         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30850         
30851         if(!this.zoomable()){
30852             this.scale = this.startScale;
30853             return;
30854         }
30855         
30856         this.draw();
30857         
30858     },
30859     
30860     onTouchEnd : function(e)
30861     {
30862         this.pinching = false;
30863         this.dragable = false;
30864         
30865     },
30866     
30867     process : function(file, crop)
30868     {
30869         if(this.loadMask){
30870             this.maskEl.mask(this.loadingText);
30871         }
30872         
30873         this.xhr = new XMLHttpRequest();
30874         
30875         file.xhr = this.xhr;
30876
30877         this.xhr.open(this.method, this.url, true);
30878         
30879         var headers = {
30880             "Accept": "application/json",
30881             "Cache-Control": "no-cache",
30882             "X-Requested-With": "XMLHttpRequest"
30883         };
30884         
30885         for (var headerName in headers) {
30886             var headerValue = headers[headerName];
30887             if (headerValue) {
30888                 this.xhr.setRequestHeader(headerName, headerValue);
30889             }
30890         }
30891         
30892         var _this = this;
30893         
30894         this.xhr.onload = function()
30895         {
30896             _this.xhrOnLoad(_this.xhr);
30897         }
30898         
30899         this.xhr.onerror = function()
30900         {
30901             _this.xhrOnError(_this.xhr);
30902         }
30903         
30904         var formData = new FormData();
30905
30906         formData.append('returnHTML', 'NO');
30907         
30908         if(crop){
30909             formData.append('crop', crop);
30910         }
30911         
30912         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30913             formData.append(this.paramName, file, file.name);
30914         }
30915         
30916         if(typeof(file.filename) != 'undefined'){
30917             formData.append('filename', file.filename);
30918         }
30919         
30920         if(typeof(file.mimetype) != 'undefined'){
30921             formData.append('mimetype', file.mimetype);
30922         }
30923         
30924         if(this.fireEvent('arrange', this, formData) != false){
30925             this.xhr.send(formData);
30926         };
30927     },
30928     
30929     xhrOnLoad : function(xhr)
30930     {
30931         if(this.loadMask){
30932             this.maskEl.unmask();
30933         }
30934         
30935         if (xhr.readyState !== 4) {
30936             this.fireEvent('exception', this, xhr);
30937             return;
30938         }
30939
30940         var response = Roo.decode(xhr.responseText);
30941         
30942         if(!response.success){
30943             this.fireEvent('exception', this, xhr);
30944             return;
30945         }
30946         
30947         var response = Roo.decode(xhr.responseText);
30948         
30949         this.fireEvent('upload', this, response);
30950         
30951     },
30952     
30953     xhrOnError : function()
30954     {
30955         if(this.loadMask){
30956             this.maskEl.unmask();
30957         }
30958         
30959         Roo.log('xhr on error');
30960         
30961         var response = Roo.decode(xhr.responseText);
30962           
30963         Roo.log(response);
30964         
30965     },
30966     
30967     prepare : function(file)
30968     {   
30969         if(this.loadMask){
30970             this.maskEl.mask(this.loadingText);
30971         }
30972         
30973         this.file = false;
30974         this.exif = {};
30975         
30976         if(typeof(file) === 'string'){
30977             this.loadCanvas(file);
30978             return;
30979         }
30980         
30981         if(!file || !this.urlAPI){
30982             return;
30983         }
30984         
30985         this.file = file;
30986         this.cropType = file.type;
30987         
30988         var _this = this;
30989         
30990         if(this.fireEvent('prepare', this, this.file) != false){
30991             
30992             var reader = new FileReader();
30993             
30994             reader.onload = function (e) {
30995                 if (e.target.error) {
30996                     Roo.log(e.target.error);
30997                     return;
30998                 }
30999                 
31000                 var buffer = e.target.result,
31001                     dataView = new DataView(buffer),
31002                     offset = 2,
31003                     maxOffset = dataView.byteLength - 4,
31004                     markerBytes,
31005                     markerLength;
31006                 
31007                 if (dataView.getUint16(0) === 0xffd8) {
31008                     while (offset < maxOffset) {
31009                         markerBytes = dataView.getUint16(offset);
31010                         
31011                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31012                             markerLength = dataView.getUint16(offset + 2) + 2;
31013                             if (offset + markerLength > dataView.byteLength) {
31014                                 Roo.log('Invalid meta data: Invalid segment size.');
31015                                 break;
31016                             }
31017                             
31018                             if(markerBytes == 0xffe1){
31019                                 _this.parseExifData(
31020                                     dataView,
31021                                     offset,
31022                                     markerLength
31023                                 );
31024                             }
31025                             
31026                             offset += markerLength;
31027                             
31028                             continue;
31029                         }
31030                         
31031                         break;
31032                     }
31033                     
31034                 }
31035                 
31036                 var url = _this.urlAPI.createObjectURL(_this.file);
31037                 
31038                 _this.loadCanvas(url);
31039                 
31040                 return;
31041             }
31042             
31043             reader.readAsArrayBuffer(this.file);
31044             
31045         }
31046         
31047     },
31048     
31049     parseExifData : function(dataView, offset, length)
31050     {
31051         var tiffOffset = offset + 10,
31052             littleEndian,
31053             dirOffset;
31054     
31055         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31056             // No Exif data, might be XMP data instead
31057             return;
31058         }
31059         
31060         // Check for the ASCII code for "Exif" (0x45786966):
31061         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31062             // No Exif data, might be XMP data instead
31063             return;
31064         }
31065         if (tiffOffset + 8 > dataView.byteLength) {
31066             Roo.log('Invalid Exif data: Invalid segment size.');
31067             return;
31068         }
31069         // Check for the two null bytes:
31070         if (dataView.getUint16(offset + 8) !== 0x0000) {
31071             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31072             return;
31073         }
31074         // Check the byte alignment:
31075         switch (dataView.getUint16(tiffOffset)) {
31076         case 0x4949:
31077             littleEndian = true;
31078             break;
31079         case 0x4D4D:
31080             littleEndian = false;
31081             break;
31082         default:
31083             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31084             return;
31085         }
31086         // Check for the TIFF tag marker (0x002A):
31087         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31088             Roo.log('Invalid Exif data: Missing TIFF marker.');
31089             return;
31090         }
31091         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31092         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31093         
31094         this.parseExifTags(
31095             dataView,
31096             tiffOffset,
31097             tiffOffset + dirOffset,
31098             littleEndian
31099         );
31100     },
31101     
31102     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31103     {
31104         var tagsNumber,
31105             dirEndOffset,
31106             i;
31107         if (dirOffset + 6 > dataView.byteLength) {
31108             Roo.log('Invalid Exif data: Invalid directory offset.');
31109             return;
31110         }
31111         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31112         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31113         if (dirEndOffset + 4 > dataView.byteLength) {
31114             Roo.log('Invalid Exif data: Invalid directory size.');
31115             return;
31116         }
31117         for (i = 0; i < tagsNumber; i += 1) {
31118             this.parseExifTag(
31119                 dataView,
31120                 tiffOffset,
31121                 dirOffset + 2 + 12 * i, // tag offset
31122                 littleEndian
31123             );
31124         }
31125         // Return the offset to the next directory:
31126         return dataView.getUint32(dirEndOffset, littleEndian);
31127     },
31128     
31129     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31130     {
31131         var tag = dataView.getUint16(offset, littleEndian);
31132         
31133         this.exif[tag] = this.getExifValue(
31134             dataView,
31135             tiffOffset,
31136             offset,
31137             dataView.getUint16(offset + 2, littleEndian), // tag type
31138             dataView.getUint32(offset + 4, littleEndian), // tag length
31139             littleEndian
31140         );
31141     },
31142     
31143     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31144     {
31145         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31146             tagSize,
31147             dataOffset,
31148             values,
31149             i,
31150             str,
31151             c;
31152     
31153         if (!tagType) {
31154             Roo.log('Invalid Exif data: Invalid tag type.');
31155             return;
31156         }
31157         
31158         tagSize = tagType.size * length;
31159         // Determine if the value is contained in the dataOffset bytes,
31160         // or if the value at the dataOffset is a pointer to the actual data:
31161         dataOffset = tagSize > 4 ?
31162                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31163         if (dataOffset + tagSize > dataView.byteLength) {
31164             Roo.log('Invalid Exif data: Invalid data offset.');
31165             return;
31166         }
31167         if (length === 1) {
31168             return tagType.getValue(dataView, dataOffset, littleEndian);
31169         }
31170         values = [];
31171         for (i = 0; i < length; i += 1) {
31172             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31173         }
31174         
31175         if (tagType.ascii) {
31176             str = '';
31177             // Concatenate the chars:
31178             for (i = 0; i < values.length; i += 1) {
31179                 c = values[i];
31180                 // Ignore the terminating NULL byte(s):
31181                 if (c === '\u0000') {
31182                     break;
31183                 }
31184                 str += c;
31185             }
31186             return str;
31187         }
31188         return values;
31189     }
31190     
31191 });
31192
31193 Roo.apply(Roo.bootstrap.UploadCropbox, {
31194     tags : {
31195         'Orientation': 0x0112
31196     },
31197     
31198     Orientation: {
31199             1: 0, //'top-left',
31200 //            2: 'top-right',
31201             3: 180, //'bottom-right',
31202 //            4: 'bottom-left',
31203 //            5: 'left-top',
31204             6: 90, //'right-top',
31205 //            7: 'right-bottom',
31206             8: 270 //'left-bottom'
31207     },
31208     
31209     exifTagTypes : {
31210         // byte, 8-bit unsigned int:
31211         1: {
31212             getValue: function (dataView, dataOffset) {
31213                 return dataView.getUint8(dataOffset);
31214             },
31215             size: 1
31216         },
31217         // ascii, 8-bit byte:
31218         2: {
31219             getValue: function (dataView, dataOffset) {
31220                 return String.fromCharCode(dataView.getUint8(dataOffset));
31221             },
31222             size: 1,
31223             ascii: true
31224         },
31225         // short, 16 bit int:
31226         3: {
31227             getValue: function (dataView, dataOffset, littleEndian) {
31228                 return dataView.getUint16(dataOffset, littleEndian);
31229             },
31230             size: 2
31231         },
31232         // long, 32 bit int:
31233         4: {
31234             getValue: function (dataView, dataOffset, littleEndian) {
31235                 return dataView.getUint32(dataOffset, littleEndian);
31236             },
31237             size: 4
31238         },
31239         // rational = two long values, first is numerator, second is denominator:
31240         5: {
31241             getValue: function (dataView, dataOffset, littleEndian) {
31242                 return dataView.getUint32(dataOffset, littleEndian) /
31243                     dataView.getUint32(dataOffset + 4, littleEndian);
31244             },
31245             size: 8
31246         },
31247         // slong, 32 bit signed int:
31248         9: {
31249             getValue: function (dataView, dataOffset, littleEndian) {
31250                 return dataView.getInt32(dataOffset, littleEndian);
31251             },
31252             size: 4
31253         },
31254         // srational, two slongs, first is numerator, second is denominator:
31255         10: {
31256             getValue: function (dataView, dataOffset, littleEndian) {
31257                 return dataView.getInt32(dataOffset, littleEndian) /
31258                     dataView.getInt32(dataOffset + 4, littleEndian);
31259             },
31260             size: 8
31261         }
31262     },
31263     
31264     footer : {
31265         STANDARD : [
31266             {
31267                 tag : 'div',
31268                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31269                 action : 'rotate-left',
31270                 cn : [
31271                     {
31272                         tag : 'button',
31273                         cls : 'btn btn-default',
31274                         html : '<i class="fa fa-undo"></i>'
31275                     }
31276                 ]
31277             },
31278             {
31279                 tag : 'div',
31280                 cls : 'btn-group roo-upload-cropbox-picture',
31281                 action : 'picture',
31282                 cn : [
31283                     {
31284                         tag : 'button',
31285                         cls : 'btn btn-default',
31286                         html : '<i class="fa fa-picture-o"></i>'
31287                     }
31288                 ]
31289             },
31290             {
31291                 tag : 'div',
31292                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31293                 action : 'rotate-right',
31294                 cn : [
31295                     {
31296                         tag : 'button',
31297                         cls : 'btn btn-default',
31298                         html : '<i class="fa fa-repeat"></i>'
31299                     }
31300                 ]
31301             }
31302         ],
31303         DOCUMENT : [
31304             {
31305                 tag : 'div',
31306                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31307                 action : 'rotate-left',
31308                 cn : [
31309                     {
31310                         tag : 'button',
31311                         cls : 'btn btn-default',
31312                         html : '<i class="fa fa-undo"></i>'
31313                     }
31314                 ]
31315             },
31316             {
31317                 tag : 'div',
31318                 cls : 'btn-group roo-upload-cropbox-download',
31319                 action : 'download',
31320                 cn : [
31321                     {
31322                         tag : 'button',
31323                         cls : 'btn btn-default',
31324                         html : '<i class="fa fa-download"></i>'
31325                     }
31326                 ]
31327             },
31328             {
31329                 tag : 'div',
31330                 cls : 'btn-group roo-upload-cropbox-crop',
31331                 action : 'crop',
31332                 cn : [
31333                     {
31334                         tag : 'button',
31335                         cls : 'btn btn-default',
31336                         html : '<i class="fa fa-crop"></i>'
31337                     }
31338                 ]
31339             },
31340             {
31341                 tag : 'div',
31342                 cls : 'btn-group roo-upload-cropbox-trash',
31343                 action : 'trash',
31344                 cn : [
31345                     {
31346                         tag : 'button',
31347                         cls : 'btn btn-default',
31348                         html : '<i class="fa fa-trash"></i>'
31349                     }
31350                 ]
31351             },
31352             {
31353                 tag : 'div',
31354                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31355                 action : 'rotate-right',
31356                 cn : [
31357                     {
31358                         tag : 'button',
31359                         cls : 'btn btn-default',
31360                         html : '<i class="fa fa-repeat"></i>'
31361                     }
31362                 ]
31363             }
31364         ],
31365         ROTATOR : [
31366             {
31367                 tag : 'div',
31368                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31369                 action : 'rotate-left',
31370                 cn : [
31371                     {
31372                         tag : 'button',
31373                         cls : 'btn btn-default',
31374                         html : '<i class="fa fa-undo"></i>'
31375                     }
31376                 ]
31377             },
31378             {
31379                 tag : 'div',
31380                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31381                 action : 'rotate-right',
31382                 cn : [
31383                     {
31384                         tag : 'button',
31385                         cls : 'btn btn-default',
31386                         html : '<i class="fa fa-repeat"></i>'
31387                     }
31388                 ]
31389             }
31390         ]
31391     }
31392 });
31393
31394 /*
31395 * Licence: LGPL
31396 */
31397
31398 /**
31399  * @class Roo.bootstrap.DocumentManager
31400  * @extends Roo.bootstrap.Component
31401  * Bootstrap DocumentManager class
31402  * @cfg {String} paramName default 'imageUpload'
31403  * @cfg {String} toolTipName default 'filename'
31404  * @cfg {String} method default POST
31405  * @cfg {String} url action url
31406  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31407  * @cfg {Boolean} multiple multiple upload default true
31408  * @cfg {Number} thumbSize default 300
31409  * @cfg {String} fieldLabel
31410  * @cfg {Number} labelWidth default 4
31411  * @cfg {String} labelAlign (left|top) default left
31412  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31413 * @cfg {Number} labellg set the width of label (1-12)
31414  * @cfg {Number} labelmd set the width of label (1-12)
31415  * @cfg {Number} labelsm set the width of label (1-12)
31416  * @cfg {Number} labelxs set the width of label (1-12)
31417  * 
31418  * @constructor
31419  * Create a new DocumentManager
31420  * @param {Object} config The config object
31421  */
31422
31423 Roo.bootstrap.DocumentManager = function(config){
31424     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31425     
31426     this.files = [];
31427     this.delegates = [];
31428     
31429     this.addEvents({
31430         /**
31431          * @event initial
31432          * Fire when initial the DocumentManager
31433          * @param {Roo.bootstrap.DocumentManager} this
31434          */
31435         "initial" : true,
31436         /**
31437          * @event inspect
31438          * inspect selected file
31439          * @param {Roo.bootstrap.DocumentManager} this
31440          * @param {File} file
31441          */
31442         "inspect" : true,
31443         /**
31444          * @event exception
31445          * Fire when xhr load exception
31446          * @param {Roo.bootstrap.DocumentManager} this
31447          * @param {XMLHttpRequest} xhr
31448          */
31449         "exception" : true,
31450         /**
31451          * @event afterupload
31452          * Fire when xhr load exception
31453          * @param {Roo.bootstrap.DocumentManager} this
31454          * @param {XMLHttpRequest} xhr
31455          */
31456         "afterupload" : true,
31457         /**
31458          * @event prepare
31459          * prepare the form data
31460          * @param {Roo.bootstrap.DocumentManager} this
31461          * @param {Object} formData
31462          */
31463         "prepare" : true,
31464         /**
31465          * @event remove
31466          * Fire when remove the file
31467          * @param {Roo.bootstrap.DocumentManager} this
31468          * @param {Object} file
31469          */
31470         "remove" : true,
31471         /**
31472          * @event refresh
31473          * Fire after refresh the file
31474          * @param {Roo.bootstrap.DocumentManager} this
31475          */
31476         "refresh" : true,
31477         /**
31478          * @event click
31479          * Fire after click the image
31480          * @param {Roo.bootstrap.DocumentManager} this
31481          * @param {Object} file
31482          */
31483         "click" : true,
31484         /**
31485          * @event edit
31486          * Fire when upload a image and editable set to true
31487          * @param {Roo.bootstrap.DocumentManager} this
31488          * @param {Object} file
31489          */
31490         "edit" : true,
31491         /**
31492          * @event beforeselectfile
31493          * Fire before select file
31494          * @param {Roo.bootstrap.DocumentManager} this
31495          */
31496         "beforeselectfile" : true,
31497         /**
31498          * @event process
31499          * Fire before process file
31500          * @param {Roo.bootstrap.DocumentManager} this
31501          * @param {Object} file
31502          */
31503         "process" : true,
31504         /**
31505          * @event previewrendered
31506          * Fire when preview rendered
31507          * @param {Roo.bootstrap.DocumentManager} this
31508          * @param {Object} file
31509          */
31510         "previewrendered" : true,
31511         /**
31512          */
31513         "previewResize" : true
31514         
31515     });
31516 };
31517
31518 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31519     
31520     boxes : 0,
31521     inputName : '',
31522     thumbSize : 300,
31523     multiple : true,
31524     files : false,
31525     method : 'POST',
31526     url : '',
31527     paramName : 'imageUpload',
31528     toolTipName : 'filename',
31529     fieldLabel : '',
31530     labelWidth : 4,
31531     labelAlign : 'left',
31532     editable : true,
31533     delegates : false,
31534     xhr : false, 
31535     
31536     labellg : 0,
31537     labelmd : 0,
31538     labelsm : 0,
31539     labelxs : 0,
31540     
31541     getAutoCreate : function()
31542     {   
31543         var managerWidget = {
31544             tag : 'div',
31545             cls : 'roo-document-manager',
31546             cn : [
31547                 {
31548                     tag : 'input',
31549                     cls : 'roo-document-manager-selector',
31550                     type : 'file'
31551                 },
31552                 {
31553                     tag : 'div',
31554                     cls : 'roo-document-manager-uploader',
31555                     cn : [
31556                         {
31557                             tag : 'div',
31558                             cls : 'roo-document-manager-upload-btn',
31559                             html : '<i class="fa fa-plus"></i>'
31560                         }
31561                     ]
31562                     
31563                 }
31564             ]
31565         };
31566         
31567         var content = [
31568             {
31569                 tag : 'div',
31570                 cls : 'column col-md-12',
31571                 cn : managerWidget
31572             }
31573         ];
31574         
31575         if(this.fieldLabel.length){
31576             
31577             content = [
31578                 {
31579                     tag : 'div',
31580                     cls : 'column col-md-12',
31581                     html : this.fieldLabel
31582                 },
31583                 {
31584                     tag : 'div',
31585                     cls : 'column col-md-12',
31586                     cn : managerWidget
31587                 }
31588             ];
31589
31590             if(this.labelAlign == 'left'){
31591                 content = [
31592                     {
31593                         tag : 'div',
31594                         cls : 'column',
31595                         html : this.fieldLabel
31596                     },
31597                     {
31598                         tag : 'div',
31599                         cls : 'column',
31600                         cn : managerWidget
31601                     }
31602                 ];
31603                 
31604                 if(this.labelWidth > 12){
31605                     content[0].style = "width: " + this.labelWidth + 'px';
31606                 }
31607
31608                 if(this.labelWidth < 13 && this.labelmd == 0){
31609                     this.labelmd = this.labelWidth;
31610                 }
31611
31612                 if(this.labellg > 0){
31613                     content[0].cls += ' col-lg-' + this.labellg;
31614                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31615                 }
31616
31617                 if(this.labelmd > 0){
31618                     content[0].cls += ' col-md-' + this.labelmd;
31619                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31620                 }
31621
31622                 if(this.labelsm > 0){
31623                     content[0].cls += ' col-sm-' + this.labelsm;
31624                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31625                 }
31626
31627                 if(this.labelxs > 0){
31628                     content[0].cls += ' col-xs-' + this.labelxs;
31629                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31630                 }
31631                 
31632             }
31633         }
31634         
31635         var cfg = {
31636             tag : 'div',
31637             cls : 'row clearfix',
31638             cn : content
31639         };
31640         
31641         return cfg;
31642         
31643     },
31644     
31645     initEvents : function()
31646     {
31647         this.managerEl = this.el.select('.roo-document-manager', true).first();
31648         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31649         
31650         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31651         this.selectorEl.hide();
31652         
31653         if(this.multiple){
31654             this.selectorEl.attr('multiple', 'multiple');
31655         }
31656         
31657         this.selectorEl.on('change', this.onFileSelected, this);
31658         
31659         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31660         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31661         
31662         this.uploader.on('click', this.onUploaderClick, this);
31663         
31664         this.renderProgressDialog();
31665         
31666         var _this = this;
31667         
31668         window.addEventListener("resize", function() { _this.refresh(); } );
31669         
31670         this.fireEvent('initial', this);
31671     },
31672     
31673     renderProgressDialog : function()
31674     {
31675         var _this = this;
31676         
31677         this.progressDialog = new Roo.bootstrap.Modal({
31678             cls : 'roo-document-manager-progress-dialog',
31679             allow_close : false,
31680             animate : false,
31681             title : '',
31682             buttons : [
31683                 {
31684                     name  :'cancel',
31685                     weight : 'danger',
31686                     html : 'Cancel'
31687                 }
31688             ], 
31689             listeners : { 
31690                 btnclick : function() {
31691                     _this.uploadCancel();
31692                     this.hide();
31693                 }
31694             }
31695         });
31696          
31697         this.progressDialog.render(Roo.get(document.body));
31698          
31699         this.progress = new Roo.bootstrap.Progress({
31700             cls : 'roo-document-manager-progress',
31701             active : true,
31702             striped : true
31703         });
31704         
31705         this.progress.render(this.progressDialog.getChildContainer());
31706         
31707         this.progressBar = new Roo.bootstrap.ProgressBar({
31708             cls : 'roo-document-manager-progress-bar',
31709             aria_valuenow : 0,
31710             aria_valuemin : 0,
31711             aria_valuemax : 12,
31712             panel : 'success'
31713         });
31714         
31715         this.progressBar.render(this.progress.getChildContainer());
31716     },
31717     
31718     onUploaderClick : function(e)
31719     {
31720         e.preventDefault();
31721      
31722         if(this.fireEvent('beforeselectfile', this) != false){
31723             this.selectorEl.dom.click();
31724         }
31725         
31726     },
31727     
31728     onFileSelected : function(e)
31729     {
31730         e.preventDefault();
31731         
31732         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31733             return;
31734         }
31735         
31736         Roo.each(this.selectorEl.dom.files, function(file){
31737             if(this.fireEvent('inspect', this, file) != false){
31738                 this.files.push(file);
31739             }
31740         }, this);
31741         
31742         this.queue();
31743         
31744     },
31745     
31746     queue : function()
31747     {
31748         this.selectorEl.dom.value = '';
31749         
31750         if(!this.files || !this.files.length){
31751             return;
31752         }
31753         
31754         if(this.boxes > 0 && this.files.length > this.boxes){
31755             this.files = this.files.slice(0, this.boxes);
31756         }
31757         
31758         this.uploader.show();
31759         
31760         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31761             this.uploader.hide();
31762         }
31763         
31764         var _this = this;
31765         
31766         var files = [];
31767         
31768         var docs = [];
31769         
31770         Roo.each(this.files, function(file){
31771             
31772             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31773                 var f = this.renderPreview(file);
31774                 files.push(f);
31775                 return;
31776             }
31777             
31778             if(file.type.indexOf('image') != -1){
31779                 this.delegates.push(
31780                     (function(){
31781                         _this.process(file);
31782                     }).createDelegate(this)
31783                 );
31784         
31785                 return;
31786             }
31787             
31788             docs.push(
31789                 (function(){
31790                     _this.process(file);
31791                 }).createDelegate(this)
31792             );
31793             
31794         }, this);
31795         
31796         this.files = files;
31797         
31798         this.delegates = this.delegates.concat(docs);
31799         
31800         if(!this.delegates.length){
31801             this.refresh();
31802             return;
31803         }
31804         
31805         this.progressBar.aria_valuemax = this.delegates.length;
31806         
31807         this.arrange();
31808         
31809         return;
31810     },
31811     
31812     arrange : function()
31813     {
31814         if(!this.delegates.length){
31815             this.progressDialog.hide();
31816             this.refresh();
31817             return;
31818         }
31819         
31820         var delegate = this.delegates.shift();
31821         
31822         this.progressDialog.show();
31823         
31824         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31825         
31826         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31827         
31828         delegate();
31829     },
31830     
31831     refresh : function()
31832     {
31833         this.uploader.show();
31834         
31835         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31836             this.uploader.hide();
31837         }
31838         
31839         Roo.isTouch ? this.closable(false) : this.closable(true);
31840         
31841         this.fireEvent('refresh', this);
31842     },
31843     
31844     onRemove : function(e, el, o)
31845     {
31846         e.preventDefault();
31847         
31848         this.fireEvent('remove', this, o);
31849         
31850     },
31851     
31852     remove : function(o)
31853     {
31854         var files = [];
31855         
31856         Roo.each(this.files, function(file){
31857             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31858                 files.push(file);
31859                 return;
31860             }
31861
31862             o.target.remove();
31863
31864         }, this);
31865         
31866         this.files = files;
31867         
31868         this.refresh();
31869     },
31870     
31871     clear : function()
31872     {
31873         Roo.each(this.files, function(file){
31874             if(!file.target){
31875                 return;
31876             }
31877             
31878             file.target.remove();
31879
31880         }, this);
31881         
31882         this.files = [];
31883         
31884         this.refresh();
31885     },
31886     
31887     onClick : function(e, el, o)
31888     {
31889         e.preventDefault();
31890         
31891         this.fireEvent('click', this, o);
31892         
31893     },
31894     
31895     closable : function(closable)
31896     {
31897         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31898             
31899             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31900             
31901             if(closable){
31902                 el.show();
31903                 return;
31904             }
31905             
31906             el.hide();
31907             
31908         }, this);
31909     },
31910     
31911     xhrOnLoad : function(xhr)
31912     {
31913         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31914             el.remove();
31915         }, this);
31916         
31917         if (xhr.readyState !== 4) {
31918             this.arrange();
31919             this.fireEvent('exception', this, xhr);
31920             return;
31921         }
31922
31923         var response = Roo.decode(xhr.responseText);
31924         
31925         if(!response.success){
31926             this.arrange();
31927             this.fireEvent('exception', this, xhr);
31928             return;
31929         }
31930         
31931         var file = this.renderPreview(response.data);
31932         
31933         this.files.push(file);
31934         
31935         this.arrange();
31936         
31937         this.fireEvent('afterupload', this, xhr);
31938         
31939     },
31940     
31941     xhrOnError : function(xhr)
31942     {
31943         Roo.log('xhr on error');
31944         
31945         var response = Roo.decode(xhr.responseText);
31946           
31947         Roo.log(response);
31948         
31949         this.arrange();
31950     },
31951     
31952     process : function(file)
31953     {
31954         if(this.fireEvent('process', this, file) !== false){
31955             if(this.editable && file.type.indexOf('image') != -1){
31956                 this.fireEvent('edit', this, file);
31957                 return;
31958             }
31959
31960             this.uploadStart(file, false);
31961
31962             return;
31963         }
31964         
31965     },
31966     
31967     uploadStart : function(file, crop)
31968     {
31969         this.xhr = new XMLHttpRequest();
31970         
31971         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31972             this.arrange();
31973             return;
31974         }
31975         
31976         file.xhr = this.xhr;
31977             
31978         this.managerEl.createChild({
31979             tag : 'div',
31980             cls : 'roo-document-manager-loading',
31981             cn : [
31982                 {
31983                     tag : 'div',
31984                     tooltip : file.name,
31985                     cls : 'roo-document-manager-thumb',
31986                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31987                 }
31988             ]
31989
31990         });
31991
31992         this.xhr.open(this.method, this.url, true);
31993         
31994         var headers = {
31995             "Accept": "application/json",
31996             "Cache-Control": "no-cache",
31997             "X-Requested-With": "XMLHttpRequest"
31998         };
31999         
32000         for (var headerName in headers) {
32001             var headerValue = headers[headerName];
32002             if (headerValue) {
32003                 this.xhr.setRequestHeader(headerName, headerValue);
32004             }
32005         }
32006         
32007         var _this = this;
32008         
32009         this.xhr.onload = function()
32010         {
32011             _this.xhrOnLoad(_this.xhr);
32012         }
32013         
32014         this.xhr.onerror = function()
32015         {
32016             _this.xhrOnError(_this.xhr);
32017         }
32018         
32019         var formData = new FormData();
32020
32021         formData.append('returnHTML', 'NO');
32022         
32023         if(crop){
32024             formData.append('crop', crop);
32025         }
32026         
32027         formData.append(this.paramName, file, file.name);
32028         
32029         var options = {
32030             file : file, 
32031             manually : false
32032         };
32033         
32034         if(this.fireEvent('prepare', this, formData, options) != false){
32035             
32036             if(options.manually){
32037                 return;
32038             }
32039             
32040             this.xhr.send(formData);
32041             return;
32042         };
32043         
32044         this.uploadCancel();
32045     },
32046     
32047     uploadCancel : function()
32048     {
32049         if (this.xhr) {
32050             this.xhr.abort();
32051         }
32052         
32053         this.delegates = [];
32054         
32055         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32056             el.remove();
32057         }, this);
32058         
32059         this.arrange();
32060     },
32061     
32062     renderPreview : function(file)
32063     {
32064         if(typeof(file.target) != 'undefined' && file.target){
32065             return file;
32066         }
32067         
32068         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32069         
32070         var previewEl = this.managerEl.createChild({
32071             tag : 'div',
32072             cls : 'roo-document-manager-preview',
32073             cn : [
32074                 {
32075                     tag : 'div',
32076                     tooltip : file[this.toolTipName],
32077                     cls : 'roo-document-manager-thumb',
32078                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32079                 },
32080                 {
32081                     tag : 'button',
32082                     cls : 'close',
32083                     html : '<i class="fa fa-times-circle"></i>'
32084                 }
32085             ]
32086         });
32087
32088         var close = previewEl.select('button.close', true).first();
32089
32090         close.on('click', this.onRemove, this, file);
32091
32092         file.target = previewEl;
32093
32094         var image = previewEl.select('img', true).first();
32095         
32096         var _this = this;
32097         
32098         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32099         
32100         image.on('click', this.onClick, this, file);
32101         
32102         this.fireEvent('previewrendered', this, file);
32103         
32104         return file;
32105         
32106     },
32107     
32108     onPreviewLoad : function(file, image)
32109     {
32110         if(typeof(file.target) == 'undefined' || !file.target){
32111             return;
32112         }
32113         
32114         var width = image.dom.naturalWidth || image.dom.width;
32115         var height = image.dom.naturalHeight || image.dom.height;
32116         
32117         if(!this.previewResize) {
32118             return;
32119         }
32120         
32121         if(width > height){
32122             file.target.addClass('wide');
32123             return;
32124         }
32125         
32126         file.target.addClass('tall');
32127         return;
32128         
32129     },
32130     
32131     uploadFromSource : function(file, crop)
32132     {
32133         this.xhr = new XMLHttpRequest();
32134         
32135         this.managerEl.createChild({
32136             tag : 'div',
32137             cls : 'roo-document-manager-loading',
32138             cn : [
32139                 {
32140                     tag : 'div',
32141                     tooltip : file.name,
32142                     cls : 'roo-document-manager-thumb',
32143                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32144                 }
32145             ]
32146
32147         });
32148
32149         this.xhr.open(this.method, this.url, true);
32150         
32151         var headers = {
32152             "Accept": "application/json",
32153             "Cache-Control": "no-cache",
32154             "X-Requested-With": "XMLHttpRequest"
32155         };
32156         
32157         for (var headerName in headers) {
32158             var headerValue = headers[headerName];
32159             if (headerValue) {
32160                 this.xhr.setRequestHeader(headerName, headerValue);
32161             }
32162         }
32163         
32164         var _this = this;
32165         
32166         this.xhr.onload = function()
32167         {
32168             _this.xhrOnLoad(_this.xhr);
32169         }
32170         
32171         this.xhr.onerror = function()
32172         {
32173             _this.xhrOnError(_this.xhr);
32174         }
32175         
32176         var formData = new FormData();
32177
32178         formData.append('returnHTML', 'NO');
32179         
32180         formData.append('crop', crop);
32181         
32182         if(typeof(file.filename) != 'undefined'){
32183             formData.append('filename', file.filename);
32184         }
32185         
32186         if(typeof(file.mimetype) != 'undefined'){
32187             formData.append('mimetype', file.mimetype);
32188         }
32189         
32190         Roo.log(formData);
32191         
32192         if(this.fireEvent('prepare', this, formData) != false){
32193             this.xhr.send(formData);
32194         };
32195     }
32196 });
32197
32198 /*
32199 * Licence: LGPL
32200 */
32201
32202 /**
32203  * @class Roo.bootstrap.DocumentViewer
32204  * @extends Roo.bootstrap.Component
32205  * Bootstrap DocumentViewer class
32206  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32207  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32208  * 
32209  * @constructor
32210  * Create a new DocumentViewer
32211  * @param {Object} config The config object
32212  */
32213
32214 Roo.bootstrap.DocumentViewer = function(config){
32215     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32216     
32217     this.addEvents({
32218         /**
32219          * @event initial
32220          * Fire after initEvent
32221          * @param {Roo.bootstrap.DocumentViewer} this
32222          */
32223         "initial" : true,
32224         /**
32225          * @event click
32226          * Fire after click
32227          * @param {Roo.bootstrap.DocumentViewer} this
32228          */
32229         "click" : true,
32230         /**
32231          * @event download
32232          * Fire after download button
32233          * @param {Roo.bootstrap.DocumentViewer} this
32234          */
32235         "download" : true,
32236         /**
32237          * @event trash
32238          * Fire after trash button
32239          * @param {Roo.bootstrap.DocumentViewer} this
32240          */
32241         "trash" : true
32242         
32243     });
32244 };
32245
32246 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32247     
32248     showDownload : true,
32249     
32250     showTrash : true,
32251     
32252     getAutoCreate : function()
32253     {
32254         var cfg = {
32255             tag : 'div',
32256             cls : 'roo-document-viewer',
32257             cn : [
32258                 {
32259                     tag : 'div',
32260                     cls : 'roo-document-viewer-body',
32261                     cn : [
32262                         {
32263                             tag : 'div',
32264                             cls : 'roo-document-viewer-thumb',
32265                             cn : [
32266                                 {
32267                                     tag : 'img',
32268                                     cls : 'roo-document-viewer-image'
32269                                 }
32270                             ]
32271                         }
32272                     ]
32273                 },
32274                 {
32275                     tag : 'div',
32276                     cls : 'roo-document-viewer-footer',
32277                     cn : {
32278                         tag : 'div',
32279                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32280                         cn : [
32281                             {
32282                                 tag : 'div',
32283                                 cls : 'btn-group roo-document-viewer-download',
32284                                 cn : [
32285                                     {
32286                                         tag : 'button',
32287                                         cls : 'btn btn-default',
32288                                         html : '<i class="fa fa-download"></i>'
32289                                     }
32290                                 ]
32291                             },
32292                             {
32293                                 tag : 'div',
32294                                 cls : 'btn-group roo-document-viewer-trash',
32295                                 cn : [
32296                                     {
32297                                         tag : 'button',
32298                                         cls : 'btn btn-default',
32299                                         html : '<i class="fa fa-trash"></i>'
32300                                     }
32301                                 ]
32302                             }
32303                         ]
32304                     }
32305                 }
32306             ]
32307         };
32308         
32309         return cfg;
32310     },
32311     
32312     initEvents : function()
32313     {
32314         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32315         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32316         
32317         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32318         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32319         
32320         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32321         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32322         
32323         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32324         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32325         
32326         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32327         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32328         
32329         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32330         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32331         
32332         this.bodyEl.on('click', this.onClick, this);
32333         this.downloadBtn.on('click', this.onDownload, this);
32334         this.trashBtn.on('click', this.onTrash, this);
32335         
32336         this.downloadBtn.hide();
32337         this.trashBtn.hide();
32338         
32339         if(this.showDownload){
32340             this.downloadBtn.show();
32341         }
32342         
32343         if(this.showTrash){
32344             this.trashBtn.show();
32345         }
32346         
32347         if(!this.showDownload && !this.showTrash) {
32348             this.footerEl.hide();
32349         }
32350         
32351     },
32352     
32353     initial : function()
32354     {
32355         this.fireEvent('initial', this);
32356         
32357     },
32358     
32359     onClick : function(e)
32360     {
32361         e.preventDefault();
32362         
32363         this.fireEvent('click', this);
32364     },
32365     
32366     onDownload : function(e)
32367     {
32368         e.preventDefault();
32369         
32370         this.fireEvent('download', this);
32371     },
32372     
32373     onTrash : function(e)
32374     {
32375         e.preventDefault();
32376         
32377         this.fireEvent('trash', this);
32378     }
32379     
32380 });
32381 /*
32382  * - LGPL
32383  *
32384  * nav progress bar
32385  * 
32386  */
32387
32388 /**
32389  * @class Roo.bootstrap.NavProgressBar
32390  * @extends Roo.bootstrap.Component
32391  * Bootstrap NavProgressBar class
32392  * 
32393  * @constructor
32394  * Create a new nav progress bar
32395  * @param {Object} config The config object
32396  */
32397
32398 Roo.bootstrap.NavProgressBar = function(config){
32399     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32400
32401     this.bullets = this.bullets || [];
32402    
32403 //    Roo.bootstrap.NavProgressBar.register(this);
32404      this.addEvents({
32405         /**
32406              * @event changed
32407              * Fires when the active item changes
32408              * @param {Roo.bootstrap.NavProgressBar} this
32409              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32410              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32411          */
32412         'changed': true
32413      });
32414     
32415 };
32416
32417 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32418     
32419     bullets : [],
32420     barItems : [],
32421     
32422     getAutoCreate : function()
32423     {
32424         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32425         
32426         cfg = {
32427             tag : 'div',
32428             cls : 'roo-navigation-bar-group',
32429             cn : [
32430                 {
32431                     tag : 'div',
32432                     cls : 'roo-navigation-top-bar'
32433                 },
32434                 {
32435                     tag : 'div',
32436                     cls : 'roo-navigation-bullets-bar',
32437                     cn : [
32438                         {
32439                             tag : 'ul',
32440                             cls : 'roo-navigation-bar'
32441                         }
32442                     ]
32443                 },
32444                 
32445                 {
32446                     tag : 'div',
32447                     cls : 'roo-navigation-bottom-bar'
32448                 }
32449             ]
32450             
32451         };
32452         
32453         return cfg;
32454         
32455     },
32456     
32457     initEvents: function() 
32458     {
32459         
32460     },
32461     
32462     onRender : function(ct, position) 
32463     {
32464         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32465         
32466         if(this.bullets.length){
32467             Roo.each(this.bullets, function(b){
32468                this.addItem(b);
32469             }, this);
32470         }
32471         
32472         this.format();
32473         
32474     },
32475     
32476     addItem : function(cfg)
32477     {
32478         var item = new Roo.bootstrap.NavProgressItem(cfg);
32479         
32480         item.parentId = this.id;
32481         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32482         
32483         if(cfg.html){
32484             var top = new Roo.bootstrap.Element({
32485                 tag : 'div',
32486                 cls : 'roo-navigation-bar-text'
32487             });
32488             
32489             var bottom = new Roo.bootstrap.Element({
32490                 tag : 'div',
32491                 cls : 'roo-navigation-bar-text'
32492             });
32493             
32494             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32495             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32496             
32497             var topText = new Roo.bootstrap.Element({
32498                 tag : 'span',
32499                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32500             });
32501             
32502             var bottomText = new Roo.bootstrap.Element({
32503                 tag : 'span',
32504                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32505             });
32506             
32507             topText.onRender(top.el, null);
32508             bottomText.onRender(bottom.el, null);
32509             
32510             item.topEl = top;
32511             item.bottomEl = bottom;
32512         }
32513         
32514         this.barItems.push(item);
32515         
32516         return item;
32517     },
32518     
32519     getActive : function()
32520     {
32521         var active = false;
32522         
32523         Roo.each(this.barItems, function(v){
32524             
32525             if (!v.isActive()) {
32526                 return;
32527             }
32528             
32529             active = v;
32530             return false;
32531             
32532         });
32533         
32534         return active;
32535     },
32536     
32537     setActiveItem : function(item)
32538     {
32539         var prev = false;
32540         
32541         Roo.each(this.barItems, function(v){
32542             if (v.rid == item.rid) {
32543                 return ;
32544             }
32545             
32546             if (v.isActive()) {
32547                 v.setActive(false);
32548                 prev = v;
32549             }
32550         });
32551
32552         item.setActive(true);
32553         
32554         this.fireEvent('changed', this, item, prev);
32555     },
32556     
32557     getBarItem: function(rid)
32558     {
32559         var ret = false;
32560         
32561         Roo.each(this.barItems, function(e) {
32562             if (e.rid != rid) {
32563                 return;
32564             }
32565             
32566             ret =  e;
32567             return false;
32568         });
32569         
32570         return ret;
32571     },
32572     
32573     indexOfItem : function(item)
32574     {
32575         var index = false;
32576         
32577         Roo.each(this.barItems, function(v, i){
32578             
32579             if (v.rid != item.rid) {
32580                 return;
32581             }
32582             
32583             index = i;
32584             return false
32585         });
32586         
32587         return index;
32588     },
32589     
32590     setActiveNext : function()
32591     {
32592         var i = this.indexOfItem(this.getActive());
32593         
32594         if (i > this.barItems.length) {
32595             return;
32596         }
32597         
32598         this.setActiveItem(this.barItems[i+1]);
32599     },
32600     
32601     setActivePrev : function()
32602     {
32603         var i = this.indexOfItem(this.getActive());
32604         
32605         if (i  < 1) {
32606             return;
32607         }
32608         
32609         this.setActiveItem(this.barItems[i-1]);
32610     },
32611     
32612     format : function()
32613     {
32614         if(!this.barItems.length){
32615             return;
32616         }
32617      
32618         var width = 100 / this.barItems.length;
32619         
32620         Roo.each(this.barItems, function(i){
32621             i.el.setStyle('width', width + '%');
32622             i.topEl.el.setStyle('width', width + '%');
32623             i.bottomEl.el.setStyle('width', width + '%');
32624         }, this);
32625         
32626     }
32627     
32628 });
32629 /*
32630  * - LGPL
32631  *
32632  * Nav Progress Item
32633  * 
32634  */
32635
32636 /**
32637  * @class Roo.bootstrap.NavProgressItem
32638  * @extends Roo.bootstrap.Component
32639  * Bootstrap NavProgressItem class
32640  * @cfg {String} rid the reference id
32641  * @cfg {Boolean} active (true|false) Is item active default false
32642  * @cfg {Boolean} disabled (true|false) Is item active default false
32643  * @cfg {String} html
32644  * @cfg {String} position (top|bottom) text position default bottom
32645  * @cfg {String} icon show icon instead of number
32646  * 
32647  * @constructor
32648  * Create a new NavProgressItem
32649  * @param {Object} config The config object
32650  */
32651 Roo.bootstrap.NavProgressItem = function(config){
32652     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32653     this.addEvents({
32654         // raw events
32655         /**
32656          * @event click
32657          * The raw click event for the entire grid.
32658          * @param {Roo.bootstrap.NavProgressItem} this
32659          * @param {Roo.EventObject} e
32660          */
32661         "click" : true
32662     });
32663    
32664 };
32665
32666 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32667     
32668     rid : '',
32669     active : false,
32670     disabled : false,
32671     html : '',
32672     position : 'bottom',
32673     icon : false,
32674     
32675     getAutoCreate : function()
32676     {
32677         var iconCls = 'roo-navigation-bar-item-icon';
32678         
32679         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32680         
32681         var cfg = {
32682             tag: 'li',
32683             cls: 'roo-navigation-bar-item',
32684             cn : [
32685                 {
32686                     tag : 'i',
32687                     cls : iconCls
32688                 }
32689             ]
32690         };
32691         
32692         if(this.active){
32693             cfg.cls += ' active';
32694         }
32695         if(this.disabled){
32696             cfg.cls += ' disabled';
32697         }
32698         
32699         return cfg;
32700     },
32701     
32702     disable : function()
32703     {
32704         this.setDisabled(true);
32705     },
32706     
32707     enable : function()
32708     {
32709         this.setDisabled(false);
32710     },
32711     
32712     initEvents: function() 
32713     {
32714         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32715         
32716         this.iconEl.on('click', this.onClick, this);
32717     },
32718     
32719     onClick : function(e)
32720     {
32721         e.preventDefault();
32722         
32723         if(this.disabled){
32724             return;
32725         }
32726         
32727         if(this.fireEvent('click', this, e) === false){
32728             return;
32729         };
32730         
32731         this.parent().setActiveItem(this);
32732     },
32733     
32734     isActive: function () 
32735     {
32736         return this.active;
32737     },
32738     
32739     setActive : function(state)
32740     {
32741         if(this.active == state){
32742             return;
32743         }
32744         
32745         this.active = state;
32746         
32747         if (state) {
32748             this.el.addClass('active');
32749             return;
32750         }
32751         
32752         this.el.removeClass('active');
32753         
32754         return;
32755     },
32756     
32757     setDisabled : function(state)
32758     {
32759         if(this.disabled == state){
32760             return;
32761         }
32762         
32763         this.disabled = state;
32764         
32765         if (state) {
32766             this.el.addClass('disabled');
32767             return;
32768         }
32769         
32770         this.el.removeClass('disabled');
32771     },
32772     
32773     tooltipEl : function()
32774     {
32775         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32776     }
32777 });
32778  
32779
32780  /*
32781  * - LGPL
32782  *
32783  * FieldLabel
32784  * 
32785  */
32786
32787 /**
32788  * @class Roo.bootstrap.FieldLabel
32789  * @extends Roo.bootstrap.Component
32790  * Bootstrap FieldLabel class
32791  * @cfg {String} html contents of the element
32792  * @cfg {String} tag tag of the element default label
32793  * @cfg {String} cls class of the element
32794  * @cfg {String} target label target 
32795  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32796  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32797  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32798  * @cfg {String} iconTooltip default "This field is required"
32799  * @cfg {String} indicatorpos (left|right) default left
32800  * 
32801  * @constructor
32802  * Create a new FieldLabel
32803  * @param {Object} config The config object
32804  */
32805
32806 Roo.bootstrap.FieldLabel = function(config){
32807     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32808     
32809     this.addEvents({
32810             /**
32811              * @event invalid
32812              * Fires after the field has been marked as invalid.
32813              * @param {Roo.form.FieldLabel} this
32814              * @param {String} msg The validation message
32815              */
32816             invalid : true,
32817             /**
32818              * @event valid
32819              * Fires after the field has been validated with no errors.
32820              * @param {Roo.form.FieldLabel} this
32821              */
32822             valid : true
32823         });
32824 };
32825
32826 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32827     
32828     tag: 'label',
32829     cls: '',
32830     html: '',
32831     target: '',
32832     allowBlank : true,
32833     invalidClass : 'has-warning',
32834     validClass : 'has-success',
32835     iconTooltip : 'This field is required',
32836     indicatorpos : 'left',
32837     
32838     getAutoCreate : function(){
32839         
32840         var cls = "";
32841         if (!this.allowBlank) {
32842             cls  = "visible";
32843         }
32844         
32845         var cfg = {
32846             tag : this.tag,
32847             cls : 'roo-bootstrap-field-label ' + this.cls,
32848             for : this.target,
32849             cn : [
32850                 {
32851                     tag : 'i',
32852                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32853                     tooltip : this.iconTooltip
32854                 },
32855                 {
32856                     tag : 'span',
32857                     html : this.html
32858                 }
32859             ] 
32860         };
32861         
32862         if(this.indicatorpos == 'right'){
32863             var cfg = {
32864                 tag : this.tag,
32865                 cls : 'roo-bootstrap-field-label ' + this.cls,
32866                 for : this.target,
32867                 cn : [
32868                     {
32869                         tag : 'span',
32870                         html : this.html
32871                     },
32872                     {
32873                         tag : 'i',
32874                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32875                         tooltip : this.iconTooltip
32876                     }
32877                 ] 
32878             };
32879         }
32880         
32881         return cfg;
32882     },
32883     
32884     initEvents: function() 
32885     {
32886         Roo.bootstrap.Element.superclass.initEvents.call(this);
32887         
32888         this.indicator = this.indicatorEl();
32889         
32890         if(this.indicator){
32891             this.indicator.removeClass('visible');
32892             this.indicator.addClass('invisible');
32893         }
32894         
32895         Roo.bootstrap.FieldLabel.register(this);
32896     },
32897     
32898     indicatorEl : function()
32899     {
32900         var indicator = this.el.select('i.roo-required-indicator',true).first();
32901         
32902         if(!indicator){
32903             return false;
32904         }
32905         
32906         return indicator;
32907         
32908     },
32909     
32910     /**
32911      * Mark this field as valid
32912      */
32913     markValid : function()
32914     {
32915         if(this.indicator){
32916             this.indicator.removeClass('visible');
32917             this.indicator.addClass('invisible');
32918         }
32919         if (Roo.bootstrap.version == 3) {
32920             this.el.removeClass(this.invalidClass);
32921             this.el.addClass(this.validClass);
32922         } else {
32923             this.el.removeClass('is-invalid');
32924             this.el.addClass('is-valid');
32925         }
32926         
32927         
32928         this.fireEvent('valid', this);
32929     },
32930     
32931     /**
32932      * Mark this field as invalid
32933      * @param {String} msg The validation message
32934      */
32935     markInvalid : function(msg)
32936     {
32937         if(this.indicator){
32938             this.indicator.removeClass('invisible');
32939             this.indicator.addClass('visible');
32940         }
32941           if (Roo.bootstrap.version == 3) {
32942             this.el.removeClass(this.validClass);
32943             this.el.addClass(this.invalidClass);
32944         } else {
32945             this.el.removeClass('is-valid');
32946             this.el.addClass('is-invalid');
32947         }
32948         
32949         
32950         this.fireEvent('invalid', this, msg);
32951     }
32952     
32953    
32954 });
32955
32956 Roo.apply(Roo.bootstrap.FieldLabel, {
32957     
32958     groups: {},
32959     
32960      /**
32961     * register a FieldLabel Group
32962     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32963     */
32964     register : function(label)
32965     {
32966         if(this.groups.hasOwnProperty(label.target)){
32967             return;
32968         }
32969      
32970         this.groups[label.target] = label;
32971         
32972     },
32973     /**
32974     * fetch a FieldLabel Group based on the target
32975     * @param {string} target
32976     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32977     */
32978     get: function(target) {
32979         if (typeof(this.groups[target]) == 'undefined') {
32980             return false;
32981         }
32982         
32983         return this.groups[target] ;
32984     }
32985 });
32986
32987  
32988
32989  /*
32990  * - LGPL
32991  *
32992  * page DateSplitField.
32993  * 
32994  */
32995
32996
32997 /**
32998  * @class Roo.bootstrap.DateSplitField
32999  * @extends Roo.bootstrap.Component
33000  * Bootstrap DateSplitField class
33001  * @cfg {string} fieldLabel - the label associated
33002  * @cfg {Number} labelWidth set the width of label (0-12)
33003  * @cfg {String} labelAlign (top|left)
33004  * @cfg {Boolean} dayAllowBlank (true|false) default false
33005  * @cfg {Boolean} monthAllowBlank (true|false) default false
33006  * @cfg {Boolean} yearAllowBlank (true|false) default false
33007  * @cfg {string} dayPlaceholder 
33008  * @cfg {string} monthPlaceholder
33009  * @cfg {string} yearPlaceholder
33010  * @cfg {string} dayFormat default 'd'
33011  * @cfg {string} monthFormat default 'm'
33012  * @cfg {string} yearFormat default 'Y'
33013  * @cfg {Number} labellg set the width of label (1-12)
33014  * @cfg {Number} labelmd set the width of label (1-12)
33015  * @cfg {Number} labelsm set the width of label (1-12)
33016  * @cfg {Number} labelxs set the width of label (1-12)
33017
33018  *     
33019  * @constructor
33020  * Create a new DateSplitField
33021  * @param {Object} config The config object
33022  */
33023
33024 Roo.bootstrap.DateSplitField = function(config){
33025     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33026     
33027     this.addEvents({
33028         // raw events
33029          /**
33030          * @event years
33031          * getting the data of years
33032          * @param {Roo.bootstrap.DateSplitField} this
33033          * @param {Object} years
33034          */
33035         "years" : true,
33036         /**
33037          * @event days
33038          * getting the data of days
33039          * @param {Roo.bootstrap.DateSplitField} this
33040          * @param {Object} days
33041          */
33042         "days" : true,
33043         /**
33044          * @event invalid
33045          * Fires after the field has been marked as invalid.
33046          * @param {Roo.form.Field} this
33047          * @param {String} msg The validation message
33048          */
33049         invalid : true,
33050        /**
33051          * @event valid
33052          * Fires after the field has been validated with no errors.
33053          * @param {Roo.form.Field} this
33054          */
33055         valid : true
33056     });
33057 };
33058
33059 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33060     
33061     fieldLabel : '',
33062     labelAlign : 'top',
33063     labelWidth : 3,
33064     dayAllowBlank : false,
33065     monthAllowBlank : false,
33066     yearAllowBlank : false,
33067     dayPlaceholder : '',
33068     monthPlaceholder : '',
33069     yearPlaceholder : '',
33070     dayFormat : 'd',
33071     monthFormat : 'm',
33072     yearFormat : 'Y',
33073     isFormField : true,
33074     labellg : 0,
33075     labelmd : 0,
33076     labelsm : 0,
33077     labelxs : 0,
33078     
33079     getAutoCreate : function()
33080     {
33081         var cfg = {
33082             tag : 'div',
33083             cls : 'row roo-date-split-field-group',
33084             cn : [
33085                 {
33086                     tag : 'input',
33087                     type : 'hidden',
33088                     cls : 'form-hidden-field roo-date-split-field-group-value',
33089                     name : this.name
33090                 }
33091             ]
33092         };
33093         
33094         var labelCls = 'col-md-12';
33095         var contentCls = 'col-md-4';
33096         
33097         if(this.fieldLabel){
33098             
33099             var label = {
33100                 tag : 'div',
33101                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33102                 cn : [
33103                     {
33104                         tag : 'label',
33105                         html : this.fieldLabel
33106                     }
33107                 ]
33108             };
33109             
33110             if(this.labelAlign == 'left'){
33111             
33112                 if(this.labelWidth > 12){
33113                     label.style = "width: " + this.labelWidth + 'px';
33114                 }
33115
33116                 if(this.labelWidth < 13 && this.labelmd == 0){
33117                     this.labelmd = this.labelWidth;
33118                 }
33119
33120                 if(this.labellg > 0){
33121                     labelCls = ' col-lg-' + this.labellg;
33122                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33123                 }
33124
33125                 if(this.labelmd > 0){
33126                     labelCls = ' col-md-' + this.labelmd;
33127                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33128                 }
33129
33130                 if(this.labelsm > 0){
33131                     labelCls = ' col-sm-' + this.labelsm;
33132                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33133                 }
33134
33135                 if(this.labelxs > 0){
33136                     labelCls = ' col-xs-' + this.labelxs;
33137                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33138                 }
33139             }
33140             
33141             label.cls += ' ' + labelCls;
33142             
33143             cfg.cn.push(label);
33144         }
33145         
33146         Roo.each(['day', 'month', 'year'], function(t){
33147             cfg.cn.push({
33148                 tag : 'div',
33149                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33150             });
33151         }, this);
33152         
33153         return cfg;
33154     },
33155     
33156     inputEl: function ()
33157     {
33158         return this.el.select('.roo-date-split-field-group-value', true).first();
33159     },
33160     
33161     onRender : function(ct, position) 
33162     {
33163         var _this = this;
33164         
33165         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33166         
33167         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33168         
33169         this.dayField = new Roo.bootstrap.ComboBox({
33170             allowBlank : this.dayAllowBlank,
33171             alwaysQuery : true,
33172             displayField : 'value',
33173             editable : false,
33174             fieldLabel : '',
33175             forceSelection : true,
33176             mode : 'local',
33177             placeholder : this.dayPlaceholder,
33178             selectOnFocus : true,
33179             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33180             triggerAction : 'all',
33181             typeAhead : true,
33182             valueField : 'value',
33183             store : new Roo.data.SimpleStore({
33184                 data : (function() {    
33185                     var days = [];
33186                     _this.fireEvent('days', _this, days);
33187                     return days;
33188                 })(),
33189                 fields : [ 'value' ]
33190             }),
33191             listeners : {
33192                 select : function (_self, record, index)
33193                 {
33194                     _this.setValue(_this.getValue());
33195                 }
33196             }
33197         });
33198
33199         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33200         
33201         this.monthField = new Roo.bootstrap.MonthField({
33202             after : '<i class=\"fa fa-calendar\"></i>',
33203             allowBlank : this.monthAllowBlank,
33204             placeholder : this.monthPlaceholder,
33205             readOnly : true,
33206             listeners : {
33207                 render : function (_self)
33208                 {
33209                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33210                         e.preventDefault();
33211                         _self.focus();
33212                     });
33213                 },
33214                 select : function (_self, oldvalue, newvalue)
33215                 {
33216                     _this.setValue(_this.getValue());
33217                 }
33218             }
33219         });
33220         
33221         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33222         
33223         this.yearField = new Roo.bootstrap.ComboBox({
33224             allowBlank : this.yearAllowBlank,
33225             alwaysQuery : true,
33226             displayField : 'value',
33227             editable : false,
33228             fieldLabel : '',
33229             forceSelection : true,
33230             mode : 'local',
33231             placeholder : this.yearPlaceholder,
33232             selectOnFocus : true,
33233             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33234             triggerAction : 'all',
33235             typeAhead : true,
33236             valueField : 'value',
33237             store : new Roo.data.SimpleStore({
33238                 data : (function() {
33239                     var years = [];
33240                     _this.fireEvent('years', _this, years);
33241                     return years;
33242                 })(),
33243                 fields : [ 'value' ]
33244             }),
33245             listeners : {
33246                 select : function (_self, record, index)
33247                 {
33248                     _this.setValue(_this.getValue());
33249                 }
33250             }
33251         });
33252
33253         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33254     },
33255     
33256     setValue : function(v, format)
33257     {
33258         this.inputEl.dom.value = v;
33259         
33260         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33261         
33262         var d = Date.parseDate(v, f);
33263         
33264         if(!d){
33265             this.validate();
33266             return;
33267         }
33268         
33269         this.setDay(d.format(this.dayFormat));
33270         this.setMonth(d.format(this.monthFormat));
33271         this.setYear(d.format(this.yearFormat));
33272         
33273         this.validate();
33274         
33275         return;
33276     },
33277     
33278     setDay : function(v)
33279     {
33280         this.dayField.setValue(v);
33281         this.inputEl.dom.value = this.getValue();
33282         this.validate();
33283         return;
33284     },
33285     
33286     setMonth : function(v)
33287     {
33288         this.monthField.setValue(v, true);
33289         this.inputEl.dom.value = this.getValue();
33290         this.validate();
33291         return;
33292     },
33293     
33294     setYear : function(v)
33295     {
33296         this.yearField.setValue(v);
33297         this.inputEl.dom.value = this.getValue();
33298         this.validate();
33299         return;
33300     },
33301     
33302     getDay : function()
33303     {
33304         return this.dayField.getValue();
33305     },
33306     
33307     getMonth : function()
33308     {
33309         return this.monthField.getValue();
33310     },
33311     
33312     getYear : function()
33313     {
33314         return this.yearField.getValue();
33315     },
33316     
33317     getValue : function()
33318     {
33319         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33320         
33321         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33322         
33323         return date;
33324     },
33325     
33326     reset : function()
33327     {
33328         this.setDay('');
33329         this.setMonth('');
33330         this.setYear('');
33331         this.inputEl.dom.value = '';
33332         this.validate();
33333         return;
33334     },
33335     
33336     validate : function()
33337     {
33338         var d = this.dayField.validate();
33339         var m = this.monthField.validate();
33340         var y = this.yearField.validate();
33341         
33342         var valid = true;
33343         
33344         if(
33345                 (!this.dayAllowBlank && !d) ||
33346                 (!this.monthAllowBlank && !m) ||
33347                 (!this.yearAllowBlank && !y)
33348         ){
33349             valid = false;
33350         }
33351         
33352         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33353             return valid;
33354         }
33355         
33356         if(valid){
33357             this.markValid();
33358             return valid;
33359         }
33360         
33361         this.markInvalid();
33362         
33363         return valid;
33364     },
33365     
33366     markValid : function()
33367     {
33368         
33369         var label = this.el.select('label', true).first();
33370         var icon = this.el.select('i.fa-star', true).first();
33371
33372         if(label && icon){
33373             icon.remove();
33374         }
33375         
33376         this.fireEvent('valid', this);
33377     },
33378     
33379      /**
33380      * Mark this field as invalid
33381      * @param {String} msg The validation message
33382      */
33383     markInvalid : function(msg)
33384     {
33385         
33386         var label = this.el.select('label', true).first();
33387         var icon = this.el.select('i.fa-star', true).first();
33388
33389         if(label && !icon){
33390             this.el.select('.roo-date-split-field-label', true).createChild({
33391                 tag : 'i',
33392                 cls : 'text-danger fa fa-lg fa-star',
33393                 tooltip : 'This field is required',
33394                 style : 'margin-right:5px;'
33395             }, label, true);
33396         }
33397         
33398         this.fireEvent('invalid', this, msg);
33399     },
33400     
33401     clearInvalid : function()
33402     {
33403         var label = this.el.select('label', true).first();
33404         var icon = this.el.select('i.fa-star', true).first();
33405
33406         if(label && icon){
33407             icon.remove();
33408         }
33409         
33410         this.fireEvent('valid', this);
33411     },
33412     
33413     getName: function()
33414     {
33415         return this.name;
33416     }
33417     
33418 });
33419
33420  /**
33421  *
33422  * This is based on 
33423  * http://masonry.desandro.com
33424  *
33425  * The idea is to render all the bricks based on vertical width...
33426  *
33427  * The original code extends 'outlayer' - we might need to use that....
33428  * 
33429  */
33430
33431
33432 /**
33433  * @class Roo.bootstrap.LayoutMasonry
33434  * @extends Roo.bootstrap.Component
33435  * Bootstrap Layout Masonry class
33436  * 
33437  * @constructor
33438  * Create a new Element
33439  * @param {Object} config The config object
33440  */
33441
33442 Roo.bootstrap.LayoutMasonry = function(config){
33443     
33444     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33445     
33446     this.bricks = [];
33447     
33448     Roo.bootstrap.LayoutMasonry.register(this);
33449     
33450     this.addEvents({
33451         // raw events
33452         /**
33453          * @event layout
33454          * Fire after layout the items
33455          * @param {Roo.bootstrap.LayoutMasonry} this
33456          * @param {Roo.EventObject} e
33457          */
33458         "layout" : true
33459     });
33460     
33461 };
33462
33463 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33464     
33465     /**
33466      * @cfg {Boolean} isLayoutInstant = no animation?
33467      */   
33468     isLayoutInstant : false, // needed?
33469    
33470     /**
33471      * @cfg {Number} boxWidth  width of the columns
33472      */   
33473     boxWidth : 450,
33474     
33475       /**
33476      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33477      */   
33478     boxHeight : 0,
33479     
33480     /**
33481      * @cfg {Number} padWidth padding below box..
33482      */   
33483     padWidth : 10, 
33484     
33485     /**
33486      * @cfg {Number} gutter gutter width..
33487      */   
33488     gutter : 10,
33489     
33490      /**
33491      * @cfg {Number} maxCols maximum number of columns
33492      */   
33493     
33494     maxCols: 0,
33495     
33496     /**
33497      * @cfg {Boolean} isAutoInitial defalut true
33498      */   
33499     isAutoInitial : true, 
33500     
33501     containerWidth: 0,
33502     
33503     /**
33504      * @cfg {Boolean} isHorizontal defalut false
33505      */   
33506     isHorizontal : false, 
33507
33508     currentSize : null,
33509     
33510     tag: 'div',
33511     
33512     cls: '',
33513     
33514     bricks: null, //CompositeElement
33515     
33516     cols : 1,
33517     
33518     _isLayoutInited : false,
33519     
33520 //    isAlternative : false, // only use for vertical layout...
33521     
33522     /**
33523      * @cfg {Number} alternativePadWidth padding below box..
33524      */   
33525     alternativePadWidth : 50,
33526     
33527     selectedBrick : [],
33528     
33529     getAutoCreate : function(){
33530         
33531         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33532         
33533         var cfg = {
33534             tag: this.tag,
33535             cls: 'blog-masonary-wrapper ' + this.cls,
33536             cn : {
33537                 cls : 'mas-boxes masonary'
33538             }
33539         };
33540         
33541         return cfg;
33542     },
33543     
33544     getChildContainer: function( )
33545     {
33546         if (this.boxesEl) {
33547             return this.boxesEl;
33548         }
33549         
33550         this.boxesEl = this.el.select('.mas-boxes').first();
33551         
33552         return this.boxesEl;
33553     },
33554     
33555     
33556     initEvents : function()
33557     {
33558         var _this = this;
33559         
33560         if(this.isAutoInitial){
33561             Roo.log('hook children rendered');
33562             this.on('childrenrendered', function() {
33563                 Roo.log('children rendered');
33564                 _this.initial();
33565             } ,this);
33566         }
33567     },
33568     
33569     initial : function()
33570     {
33571         this.selectedBrick = [];
33572         
33573         this.currentSize = this.el.getBox(true);
33574         
33575         Roo.EventManager.onWindowResize(this.resize, this); 
33576
33577         if(!this.isAutoInitial){
33578             this.layout();
33579             return;
33580         }
33581         
33582         this.layout();
33583         
33584         return;
33585         //this.layout.defer(500,this);
33586         
33587     },
33588     
33589     resize : function()
33590     {
33591         var cs = this.el.getBox(true);
33592         
33593         if (
33594                 this.currentSize.width == cs.width && 
33595                 this.currentSize.x == cs.x && 
33596                 this.currentSize.height == cs.height && 
33597                 this.currentSize.y == cs.y 
33598         ) {
33599             Roo.log("no change in with or X or Y");
33600             return;
33601         }
33602         
33603         this.currentSize = cs;
33604         
33605         this.layout();
33606         
33607     },
33608     
33609     layout : function()
33610     {   
33611         this._resetLayout();
33612         
33613         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33614         
33615         this.layoutItems( isInstant );
33616       
33617         this._isLayoutInited = true;
33618         
33619         this.fireEvent('layout', this);
33620         
33621     },
33622     
33623     _resetLayout : function()
33624     {
33625         if(this.isHorizontal){
33626             this.horizontalMeasureColumns();
33627             return;
33628         }
33629         
33630         this.verticalMeasureColumns();
33631         
33632     },
33633     
33634     verticalMeasureColumns : function()
33635     {
33636         this.getContainerWidth();
33637         
33638 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33639 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33640 //            return;
33641 //        }
33642         
33643         var boxWidth = this.boxWidth + this.padWidth;
33644         
33645         if(this.containerWidth < this.boxWidth){
33646             boxWidth = this.containerWidth
33647         }
33648         
33649         var containerWidth = this.containerWidth;
33650         
33651         var cols = Math.floor(containerWidth / boxWidth);
33652         
33653         this.cols = Math.max( cols, 1 );
33654         
33655         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33656         
33657         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33658         
33659         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33660         
33661         this.colWidth = boxWidth + avail - this.padWidth;
33662         
33663         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33664         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33665     },
33666     
33667     horizontalMeasureColumns : function()
33668     {
33669         this.getContainerWidth();
33670         
33671         var boxWidth = this.boxWidth;
33672         
33673         if(this.containerWidth < boxWidth){
33674             boxWidth = this.containerWidth;
33675         }
33676         
33677         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33678         
33679         this.el.setHeight(boxWidth);
33680         
33681     },
33682     
33683     getContainerWidth : function()
33684     {
33685         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33686     },
33687     
33688     layoutItems : function( isInstant )
33689     {
33690         Roo.log(this.bricks);
33691         
33692         var items = Roo.apply([], this.bricks);
33693         
33694         if(this.isHorizontal){
33695             this._horizontalLayoutItems( items , isInstant );
33696             return;
33697         }
33698         
33699 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33700 //            this._verticalAlternativeLayoutItems( items , isInstant );
33701 //            return;
33702 //        }
33703         
33704         this._verticalLayoutItems( items , isInstant );
33705         
33706     },
33707     
33708     _verticalLayoutItems : function ( items , isInstant)
33709     {
33710         if ( !items || !items.length ) {
33711             return;
33712         }
33713         
33714         var standard = [
33715             ['xs', 'xs', 'xs', 'tall'],
33716             ['xs', 'xs', 'tall'],
33717             ['xs', 'xs', 'sm'],
33718             ['xs', 'xs', 'xs'],
33719             ['xs', 'tall'],
33720             ['xs', 'sm'],
33721             ['xs', 'xs'],
33722             ['xs'],
33723             
33724             ['sm', 'xs', 'xs'],
33725             ['sm', 'xs'],
33726             ['sm'],
33727             
33728             ['tall', 'xs', 'xs', 'xs'],
33729             ['tall', 'xs', 'xs'],
33730             ['tall', 'xs'],
33731             ['tall']
33732             
33733         ];
33734         
33735         var queue = [];
33736         
33737         var boxes = [];
33738         
33739         var box = [];
33740         
33741         Roo.each(items, function(item, k){
33742             
33743             switch (item.size) {
33744                 // these layouts take up a full box,
33745                 case 'md' :
33746                 case 'md-left' :
33747                 case 'md-right' :
33748                 case 'wide' :
33749                     
33750                     if(box.length){
33751                         boxes.push(box);
33752                         box = [];
33753                     }
33754                     
33755                     boxes.push([item]);
33756                     
33757                     break;
33758                     
33759                 case 'xs' :
33760                 case 'sm' :
33761                 case 'tall' :
33762                     
33763                     box.push(item);
33764                     
33765                     break;
33766                 default :
33767                     break;
33768                     
33769             }
33770             
33771         }, this);
33772         
33773         if(box.length){
33774             boxes.push(box);
33775             box = [];
33776         }
33777         
33778         var filterPattern = function(box, length)
33779         {
33780             if(!box.length){
33781                 return;
33782             }
33783             
33784             var match = false;
33785             
33786             var pattern = box.slice(0, length);
33787             
33788             var format = [];
33789             
33790             Roo.each(pattern, function(i){
33791                 format.push(i.size);
33792             }, this);
33793             
33794             Roo.each(standard, function(s){
33795                 
33796                 if(String(s) != String(format)){
33797                     return;
33798                 }
33799                 
33800                 match = true;
33801                 return false;
33802                 
33803             }, this);
33804             
33805             if(!match && length == 1){
33806                 return;
33807             }
33808             
33809             if(!match){
33810                 filterPattern(box, length - 1);
33811                 return;
33812             }
33813                 
33814             queue.push(pattern);
33815
33816             box = box.slice(length, box.length);
33817
33818             filterPattern(box, 4);
33819
33820             return;
33821             
33822         }
33823         
33824         Roo.each(boxes, function(box, k){
33825             
33826             if(!box.length){
33827                 return;
33828             }
33829             
33830             if(box.length == 1){
33831                 queue.push(box);
33832                 return;
33833             }
33834             
33835             filterPattern(box, 4);
33836             
33837         }, this);
33838         
33839         this._processVerticalLayoutQueue( queue, isInstant );
33840         
33841     },
33842     
33843 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33844 //    {
33845 //        if ( !items || !items.length ) {
33846 //            return;
33847 //        }
33848 //
33849 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33850 //        
33851 //    },
33852     
33853     _horizontalLayoutItems : function ( items , isInstant)
33854     {
33855         if ( !items || !items.length || items.length < 3) {
33856             return;
33857         }
33858         
33859         items.reverse();
33860         
33861         var eItems = items.slice(0, 3);
33862         
33863         items = items.slice(3, items.length);
33864         
33865         var standard = [
33866             ['xs', 'xs', 'xs', 'wide'],
33867             ['xs', 'xs', 'wide'],
33868             ['xs', 'xs', 'sm'],
33869             ['xs', 'xs', 'xs'],
33870             ['xs', 'wide'],
33871             ['xs', 'sm'],
33872             ['xs', 'xs'],
33873             ['xs'],
33874             
33875             ['sm', 'xs', 'xs'],
33876             ['sm', 'xs'],
33877             ['sm'],
33878             
33879             ['wide', 'xs', 'xs', 'xs'],
33880             ['wide', 'xs', 'xs'],
33881             ['wide', 'xs'],
33882             ['wide'],
33883             
33884             ['wide-thin']
33885         ];
33886         
33887         var queue = [];
33888         
33889         var boxes = [];
33890         
33891         var box = [];
33892         
33893         Roo.each(items, function(item, k){
33894             
33895             switch (item.size) {
33896                 case 'md' :
33897                 case 'md-left' :
33898                 case 'md-right' :
33899                 case 'tall' :
33900                     
33901                     if(box.length){
33902                         boxes.push(box);
33903                         box = [];
33904                     }
33905                     
33906                     boxes.push([item]);
33907                     
33908                     break;
33909                     
33910                 case 'xs' :
33911                 case 'sm' :
33912                 case 'wide' :
33913                 case 'wide-thin' :
33914                     
33915                     box.push(item);
33916                     
33917                     break;
33918                 default :
33919                     break;
33920                     
33921             }
33922             
33923         }, this);
33924         
33925         if(box.length){
33926             boxes.push(box);
33927             box = [];
33928         }
33929         
33930         var filterPattern = function(box, length)
33931         {
33932             if(!box.length){
33933                 return;
33934             }
33935             
33936             var match = false;
33937             
33938             var pattern = box.slice(0, length);
33939             
33940             var format = [];
33941             
33942             Roo.each(pattern, function(i){
33943                 format.push(i.size);
33944             }, this);
33945             
33946             Roo.each(standard, function(s){
33947                 
33948                 if(String(s) != String(format)){
33949                     return;
33950                 }
33951                 
33952                 match = true;
33953                 return false;
33954                 
33955             }, this);
33956             
33957             if(!match && length == 1){
33958                 return;
33959             }
33960             
33961             if(!match){
33962                 filterPattern(box, length - 1);
33963                 return;
33964             }
33965                 
33966             queue.push(pattern);
33967
33968             box = box.slice(length, box.length);
33969
33970             filterPattern(box, 4);
33971
33972             return;
33973             
33974         }
33975         
33976         Roo.each(boxes, function(box, k){
33977             
33978             if(!box.length){
33979                 return;
33980             }
33981             
33982             if(box.length == 1){
33983                 queue.push(box);
33984                 return;
33985             }
33986             
33987             filterPattern(box, 4);
33988             
33989         }, this);
33990         
33991         
33992         var prune = [];
33993         
33994         var pos = this.el.getBox(true);
33995         
33996         var minX = pos.x;
33997         
33998         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33999         
34000         var hit_end = false;
34001         
34002         Roo.each(queue, function(box){
34003             
34004             if(hit_end){
34005                 
34006                 Roo.each(box, function(b){
34007                 
34008                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34009                     b.el.hide();
34010
34011                 }, this);
34012
34013                 return;
34014             }
34015             
34016             var mx = 0;
34017             
34018             Roo.each(box, function(b){
34019                 
34020                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34021                 b.el.show();
34022
34023                 mx = Math.max(mx, b.x);
34024                 
34025             }, this);
34026             
34027             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34028             
34029             if(maxX < minX){
34030                 
34031                 Roo.each(box, function(b){
34032                 
34033                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34034                     b.el.hide();
34035                     
34036                 }, this);
34037                 
34038                 hit_end = true;
34039                 
34040                 return;
34041             }
34042             
34043             prune.push(box);
34044             
34045         }, this);
34046         
34047         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34048     },
34049     
34050     /** Sets position of item in DOM
34051     * @param {Element} item
34052     * @param {Number} x - horizontal position
34053     * @param {Number} y - vertical position
34054     * @param {Boolean} isInstant - disables transitions
34055     */
34056     _processVerticalLayoutQueue : function( queue, isInstant )
34057     {
34058         var pos = this.el.getBox(true);
34059         var x = pos.x;
34060         var y = pos.y;
34061         var maxY = [];
34062         
34063         for (var i = 0; i < this.cols; i++){
34064             maxY[i] = pos.y;
34065         }
34066         
34067         Roo.each(queue, function(box, k){
34068             
34069             var col = k % this.cols;
34070             
34071             Roo.each(box, function(b,kk){
34072                 
34073                 b.el.position('absolute');
34074                 
34075                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34076                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34077                 
34078                 if(b.size == 'md-left' || b.size == 'md-right'){
34079                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34080                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34081                 }
34082                 
34083                 b.el.setWidth(width);
34084                 b.el.setHeight(height);
34085                 // iframe?
34086                 b.el.select('iframe',true).setSize(width,height);
34087                 
34088             }, this);
34089             
34090             for (var i = 0; i < this.cols; i++){
34091                 
34092                 if(maxY[i] < maxY[col]){
34093                     col = i;
34094                     continue;
34095                 }
34096                 
34097                 col = Math.min(col, i);
34098                 
34099             }
34100             
34101             x = pos.x + col * (this.colWidth + this.padWidth);
34102             
34103             y = maxY[col];
34104             
34105             var positions = [];
34106             
34107             switch (box.length){
34108                 case 1 :
34109                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34110                     break;
34111                 case 2 :
34112                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34113                     break;
34114                 case 3 :
34115                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34116                     break;
34117                 case 4 :
34118                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34119                     break;
34120                 default :
34121                     break;
34122             }
34123             
34124             Roo.each(box, function(b,kk){
34125                 
34126                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34127                 
34128                 var sz = b.el.getSize();
34129                 
34130                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34131                 
34132             }, this);
34133             
34134         }, this);
34135         
34136         var mY = 0;
34137         
34138         for (var i = 0; i < this.cols; i++){
34139             mY = Math.max(mY, maxY[i]);
34140         }
34141         
34142         this.el.setHeight(mY - pos.y);
34143         
34144     },
34145     
34146 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34147 //    {
34148 //        var pos = this.el.getBox(true);
34149 //        var x = pos.x;
34150 //        var y = pos.y;
34151 //        var maxX = pos.right;
34152 //        
34153 //        var maxHeight = 0;
34154 //        
34155 //        Roo.each(items, function(item, k){
34156 //            
34157 //            var c = k % 2;
34158 //            
34159 //            item.el.position('absolute');
34160 //                
34161 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34162 //
34163 //            item.el.setWidth(width);
34164 //
34165 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34166 //
34167 //            item.el.setHeight(height);
34168 //            
34169 //            if(c == 0){
34170 //                item.el.setXY([x, y], isInstant ? false : true);
34171 //            } else {
34172 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34173 //            }
34174 //            
34175 //            y = y + height + this.alternativePadWidth;
34176 //            
34177 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34178 //            
34179 //        }, this);
34180 //        
34181 //        this.el.setHeight(maxHeight);
34182 //        
34183 //    },
34184     
34185     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34186     {
34187         var pos = this.el.getBox(true);
34188         
34189         var minX = pos.x;
34190         var minY = pos.y;
34191         
34192         var maxX = pos.right;
34193         
34194         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34195         
34196         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34197         
34198         Roo.each(queue, function(box, k){
34199             
34200             Roo.each(box, function(b, kk){
34201                 
34202                 b.el.position('absolute');
34203                 
34204                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34205                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34206                 
34207                 if(b.size == 'md-left' || b.size == 'md-right'){
34208                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34209                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34210                 }
34211                 
34212                 b.el.setWidth(width);
34213                 b.el.setHeight(height);
34214                 
34215             }, this);
34216             
34217             if(!box.length){
34218                 return;
34219             }
34220             
34221             var positions = [];
34222             
34223             switch (box.length){
34224                 case 1 :
34225                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34226                     break;
34227                 case 2 :
34228                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34229                     break;
34230                 case 3 :
34231                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34232                     break;
34233                 case 4 :
34234                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34235                     break;
34236                 default :
34237                     break;
34238             }
34239             
34240             Roo.each(box, function(b,kk){
34241                 
34242                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34243                 
34244                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34245                 
34246             }, this);
34247             
34248         }, this);
34249         
34250     },
34251     
34252     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34253     {
34254         Roo.each(eItems, function(b,k){
34255             
34256             b.size = (k == 0) ? 'sm' : 'xs';
34257             b.x = (k == 0) ? 2 : 1;
34258             b.y = (k == 0) ? 2 : 1;
34259             
34260             b.el.position('absolute');
34261             
34262             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34263                 
34264             b.el.setWidth(width);
34265             
34266             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34267             
34268             b.el.setHeight(height);
34269             
34270         }, this);
34271
34272         var positions = [];
34273         
34274         positions.push({
34275             x : maxX - this.unitWidth * 2 - this.gutter,
34276             y : minY
34277         });
34278         
34279         positions.push({
34280             x : maxX - this.unitWidth,
34281             y : minY + (this.unitWidth + this.gutter) * 2
34282         });
34283         
34284         positions.push({
34285             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34286             y : minY
34287         });
34288         
34289         Roo.each(eItems, function(b,k){
34290             
34291             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34292
34293         }, this);
34294         
34295     },
34296     
34297     getVerticalOneBoxColPositions : function(x, y, box)
34298     {
34299         var pos = [];
34300         
34301         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34302         
34303         if(box[0].size == 'md-left'){
34304             rand = 0;
34305         }
34306         
34307         if(box[0].size == 'md-right'){
34308             rand = 1;
34309         }
34310         
34311         pos.push({
34312             x : x + (this.unitWidth + this.gutter) * rand,
34313             y : y
34314         });
34315         
34316         return pos;
34317     },
34318     
34319     getVerticalTwoBoxColPositions : function(x, y, box)
34320     {
34321         var pos = [];
34322         
34323         if(box[0].size == 'xs'){
34324             
34325             pos.push({
34326                 x : x,
34327                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34328             });
34329
34330             pos.push({
34331                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34332                 y : y
34333             });
34334             
34335             return pos;
34336             
34337         }
34338         
34339         pos.push({
34340             x : x,
34341             y : y
34342         });
34343
34344         pos.push({
34345             x : x + (this.unitWidth + this.gutter) * 2,
34346             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34347         });
34348         
34349         return pos;
34350         
34351     },
34352     
34353     getVerticalThreeBoxColPositions : function(x, y, box)
34354     {
34355         var pos = [];
34356         
34357         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34358             
34359             pos.push({
34360                 x : x,
34361                 y : y
34362             });
34363
34364             pos.push({
34365                 x : x + (this.unitWidth + this.gutter) * 1,
34366                 y : y
34367             });
34368             
34369             pos.push({
34370                 x : x + (this.unitWidth + this.gutter) * 2,
34371                 y : y
34372             });
34373             
34374             return pos;
34375             
34376         }
34377         
34378         if(box[0].size == 'xs' && box[1].size == 'xs'){
34379             
34380             pos.push({
34381                 x : x,
34382                 y : y
34383             });
34384
34385             pos.push({
34386                 x : x,
34387                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34388             });
34389             
34390             pos.push({
34391                 x : x + (this.unitWidth + this.gutter) * 1,
34392                 y : y
34393             });
34394             
34395             return pos;
34396             
34397         }
34398         
34399         pos.push({
34400             x : x,
34401             y : y
34402         });
34403
34404         pos.push({
34405             x : x + (this.unitWidth + this.gutter) * 2,
34406             y : y
34407         });
34408
34409         pos.push({
34410             x : x + (this.unitWidth + this.gutter) * 2,
34411             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34412         });
34413             
34414         return pos;
34415         
34416     },
34417     
34418     getVerticalFourBoxColPositions : function(x, y, box)
34419     {
34420         var pos = [];
34421         
34422         if(box[0].size == 'xs'){
34423             
34424             pos.push({
34425                 x : x,
34426                 y : y
34427             });
34428
34429             pos.push({
34430                 x : x,
34431                 y : y + (this.unitHeight + this.gutter) * 1
34432             });
34433             
34434             pos.push({
34435                 x : x,
34436                 y : y + (this.unitHeight + this.gutter) * 2
34437             });
34438             
34439             pos.push({
34440                 x : x + (this.unitWidth + this.gutter) * 1,
34441                 y : y
34442             });
34443             
34444             return pos;
34445             
34446         }
34447         
34448         pos.push({
34449             x : x,
34450             y : y
34451         });
34452
34453         pos.push({
34454             x : x + (this.unitWidth + this.gutter) * 2,
34455             y : y
34456         });
34457
34458         pos.push({
34459             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34460             y : y + (this.unitHeight + this.gutter) * 1
34461         });
34462
34463         pos.push({
34464             x : x + (this.unitWidth + this.gutter) * 2,
34465             y : y + (this.unitWidth + this.gutter) * 2
34466         });
34467
34468         return pos;
34469         
34470     },
34471     
34472     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34473     {
34474         var pos = [];
34475         
34476         if(box[0].size == 'md-left'){
34477             pos.push({
34478                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34479                 y : minY
34480             });
34481             
34482             return pos;
34483         }
34484         
34485         if(box[0].size == 'md-right'){
34486             pos.push({
34487                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34488                 y : minY + (this.unitWidth + this.gutter) * 1
34489             });
34490             
34491             return pos;
34492         }
34493         
34494         var rand = Math.floor(Math.random() * (4 - box[0].y));
34495         
34496         pos.push({
34497             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34498             y : minY + (this.unitWidth + this.gutter) * rand
34499         });
34500         
34501         return pos;
34502         
34503     },
34504     
34505     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34506     {
34507         var pos = [];
34508         
34509         if(box[0].size == 'xs'){
34510             
34511             pos.push({
34512                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34513                 y : minY
34514             });
34515
34516             pos.push({
34517                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34518                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34519             });
34520             
34521             return pos;
34522             
34523         }
34524         
34525         pos.push({
34526             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34527             y : minY
34528         });
34529
34530         pos.push({
34531             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34532             y : minY + (this.unitWidth + this.gutter) * 2
34533         });
34534         
34535         return pos;
34536         
34537     },
34538     
34539     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34540     {
34541         var pos = [];
34542         
34543         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
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) * 1
34553             });
34554             
34555             pos.push({
34556                 x : maxX - 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         if(box[0].size == 'xs' && box[1].size == 'xs'){
34565             
34566             pos.push({
34567                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34568                 y : minY
34569             });
34570
34571             pos.push({
34572                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34573                 y : minY
34574             });
34575             
34576             pos.push({
34577                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34578                 y : minY + (this.unitWidth + this.gutter) * 1
34579             });
34580             
34581             return pos;
34582             
34583         }
34584         
34585         pos.push({
34586             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34587             y : minY
34588         });
34589
34590         pos.push({
34591             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34592             y : minY + (this.unitWidth + this.gutter) * 2
34593         });
34594
34595         pos.push({
34596             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34597             y : minY + (this.unitWidth + this.gutter) * 2
34598         });
34599             
34600         return pos;
34601         
34602     },
34603     
34604     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34605     {
34606         var pos = [];
34607         
34608         if(box[0].size == 'xs'){
34609             
34610             pos.push({
34611                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34612                 y : minY
34613             });
34614
34615             pos.push({
34616                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34617                 y : minY
34618             });
34619             
34620             pos.push({
34621                 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),
34622                 y : minY
34623             });
34624             
34625             pos.push({
34626                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34627                 y : minY + (this.unitWidth + this.gutter) * 1
34628             });
34629             
34630             return pos;
34631             
34632         }
34633         
34634         pos.push({
34635             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34636             y : minY
34637         });
34638         
34639         pos.push({
34640             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34641             y : minY + (this.unitWidth + this.gutter) * 2
34642         });
34643         
34644         pos.push({
34645             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34646             y : minY + (this.unitWidth + this.gutter) * 2
34647         });
34648         
34649         pos.push({
34650             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),
34651             y : minY + (this.unitWidth + this.gutter) * 2
34652         });
34653
34654         return pos;
34655         
34656     },
34657     
34658     /**
34659     * remove a Masonry Brick
34660     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34661     */
34662     removeBrick : function(brick_id)
34663     {
34664         if (!brick_id) {
34665             return;
34666         }
34667         
34668         for (var i = 0; i<this.bricks.length; i++) {
34669             if (this.bricks[i].id == brick_id) {
34670                 this.bricks.splice(i,1);
34671                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34672                 this.initial();
34673             }
34674         }
34675     },
34676     
34677     /**
34678     * adds a Masonry Brick
34679     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34680     */
34681     addBrick : function(cfg)
34682     {
34683         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34684         //this.register(cn);
34685         cn.parentId = this.id;
34686         cn.render(this.el);
34687         return cn;
34688     },
34689     
34690     /**
34691     * register a Masonry Brick
34692     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34693     */
34694     
34695     register : function(brick)
34696     {
34697         this.bricks.push(brick);
34698         brick.masonryId = this.id;
34699     },
34700     
34701     /**
34702     * clear all the Masonry Brick
34703     */
34704     clearAll : function()
34705     {
34706         this.bricks = [];
34707         //this.getChildContainer().dom.innerHTML = "";
34708         this.el.dom.innerHTML = '';
34709     },
34710     
34711     getSelected : function()
34712     {
34713         if (!this.selectedBrick) {
34714             return false;
34715         }
34716         
34717         return this.selectedBrick;
34718     }
34719 });
34720
34721 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34722     
34723     groups: {},
34724      /**
34725     * register a Masonry Layout
34726     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34727     */
34728     
34729     register : function(layout)
34730     {
34731         this.groups[layout.id] = layout;
34732     },
34733     /**
34734     * fetch a  Masonry Layout based on the masonry layout ID
34735     * @param {string} the masonry layout to add
34736     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34737     */
34738     
34739     get: function(layout_id) {
34740         if (typeof(this.groups[layout_id]) == 'undefined') {
34741             return false;
34742         }
34743         return this.groups[layout_id] ;
34744     }
34745     
34746     
34747     
34748 });
34749
34750  
34751
34752  /**
34753  *
34754  * This is based on 
34755  * http://masonry.desandro.com
34756  *
34757  * The idea is to render all the bricks based on vertical width...
34758  *
34759  * The original code extends 'outlayer' - we might need to use that....
34760  * 
34761  */
34762
34763
34764 /**
34765  * @class Roo.bootstrap.LayoutMasonryAuto
34766  * @extends Roo.bootstrap.Component
34767  * Bootstrap Layout Masonry class
34768  * 
34769  * @constructor
34770  * Create a new Element
34771  * @param {Object} config The config object
34772  */
34773
34774 Roo.bootstrap.LayoutMasonryAuto = function(config){
34775     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34776 };
34777
34778 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34779     
34780       /**
34781      * @cfg {Boolean} isFitWidth  - resize the width..
34782      */   
34783     isFitWidth : false,  // options..
34784     /**
34785      * @cfg {Boolean} isOriginLeft = left align?
34786      */   
34787     isOriginLeft : true,
34788     /**
34789      * @cfg {Boolean} isOriginTop = top align?
34790      */   
34791     isOriginTop : false,
34792     /**
34793      * @cfg {Boolean} isLayoutInstant = no animation?
34794      */   
34795     isLayoutInstant : false, // needed?
34796     /**
34797      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34798      */   
34799     isResizingContainer : true,
34800     /**
34801      * @cfg {Number} columnWidth  width of the columns 
34802      */   
34803     
34804     columnWidth : 0,
34805     
34806     /**
34807      * @cfg {Number} maxCols maximum number of columns
34808      */   
34809     
34810     maxCols: 0,
34811     /**
34812      * @cfg {Number} padHeight padding below box..
34813      */   
34814     
34815     padHeight : 10, 
34816     
34817     /**
34818      * @cfg {Boolean} isAutoInitial defalut true
34819      */   
34820     
34821     isAutoInitial : true, 
34822     
34823     // private?
34824     gutter : 0,
34825     
34826     containerWidth: 0,
34827     initialColumnWidth : 0,
34828     currentSize : null,
34829     
34830     colYs : null, // array.
34831     maxY : 0,
34832     padWidth: 10,
34833     
34834     
34835     tag: 'div',
34836     cls: '',
34837     bricks: null, //CompositeElement
34838     cols : 0, // array?
34839     // element : null, // wrapped now this.el
34840     _isLayoutInited : null, 
34841     
34842     
34843     getAutoCreate : function(){
34844         
34845         var cfg = {
34846             tag: this.tag,
34847             cls: 'blog-masonary-wrapper ' + this.cls,
34848             cn : {
34849                 cls : 'mas-boxes masonary'
34850             }
34851         };
34852         
34853         return cfg;
34854     },
34855     
34856     getChildContainer: function( )
34857     {
34858         if (this.boxesEl) {
34859             return this.boxesEl;
34860         }
34861         
34862         this.boxesEl = this.el.select('.mas-boxes').first();
34863         
34864         return this.boxesEl;
34865     },
34866     
34867     
34868     initEvents : function()
34869     {
34870         var _this = this;
34871         
34872         if(this.isAutoInitial){
34873             Roo.log('hook children rendered');
34874             this.on('childrenrendered', function() {
34875                 Roo.log('children rendered');
34876                 _this.initial();
34877             } ,this);
34878         }
34879         
34880     },
34881     
34882     initial : function()
34883     {
34884         this.reloadItems();
34885
34886         this.currentSize = this.el.getBox(true);
34887
34888         /// was window resize... - let's see if this works..
34889         Roo.EventManager.onWindowResize(this.resize, this); 
34890
34891         if(!this.isAutoInitial){
34892             this.layout();
34893             return;
34894         }
34895         
34896         this.layout.defer(500,this);
34897     },
34898     
34899     reloadItems: function()
34900     {
34901         this.bricks = this.el.select('.masonry-brick', true);
34902         
34903         this.bricks.each(function(b) {
34904             //Roo.log(b.getSize());
34905             if (!b.attr('originalwidth')) {
34906                 b.attr('originalwidth',  b.getSize().width);
34907             }
34908             
34909         });
34910         
34911         Roo.log(this.bricks.elements.length);
34912     },
34913     
34914     resize : function()
34915     {
34916         Roo.log('resize');
34917         var cs = this.el.getBox(true);
34918         
34919         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34920             Roo.log("no change in with or X");
34921             return;
34922         }
34923         this.currentSize = cs;
34924         this.layout();
34925     },
34926     
34927     layout : function()
34928     {
34929          Roo.log('layout');
34930         this._resetLayout();
34931         //this._manageStamps();
34932       
34933         // don't animate first layout
34934         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34935         this.layoutItems( isInstant );
34936       
34937         // flag for initalized
34938         this._isLayoutInited = true;
34939     },
34940     
34941     layoutItems : function( isInstant )
34942     {
34943         //var items = this._getItemsForLayout( this.items );
34944         // original code supports filtering layout items.. we just ignore it..
34945         
34946         this._layoutItems( this.bricks , isInstant );
34947       
34948         this._postLayout();
34949     },
34950     _layoutItems : function ( items , isInstant)
34951     {
34952        //this.fireEvent( 'layout', this, items );
34953     
34954
34955         if ( !items || !items.elements.length ) {
34956           // no items, emit event with empty array
34957             return;
34958         }
34959
34960         var queue = [];
34961         items.each(function(item) {
34962             Roo.log("layout item");
34963             Roo.log(item);
34964             // get x/y object from method
34965             var position = this._getItemLayoutPosition( item );
34966             // enqueue
34967             position.item = item;
34968             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34969             queue.push( position );
34970         }, this);
34971       
34972         this._processLayoutQueue( queue );
34973     },
34974     /** Sets position of item in DOM
34975     * @param {Element} item
34976     * @param {Number} x - horizontal position
34977     * @param {Number} y - vertical position
34978     * @param {Boolean} isInstant - disables transitions
34979     */
34980     _processLayoutQueue : function( queue )
34981     {
34982         for ( var i=0, len = queue.length; i < len; i++ ) {
34983             var obj = queue[i];
34984             obj.item.position('absolute');
34985             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34986         }
34987     },
34988       
34989     
34990     /**
34991     * Any logic you want to do after each layout,
34992     * i.e. size the container
34993     */
34994     _postLayout : function()
34995     {
34996         this.resizeContainer();
34997     },
34998     
34999     resizeContainer : function()
35000     {
35001         if ( !this.isResizingContainer ) {
35002             return;
35003         }
35004         var size = this._getContainerSize();
35005         if ( size ) {
35006             this.el.setSize(size.width,size.height);
35007             this.boxesEl.setSize(size.width,size.height);
35008         }
35009     },
35010     
35011     
35012     
35013     _resetLayout : function()
35014     {
35015         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35016         this.colWidth = this.el.getWidth();
35017         //this.gutter = this.el.getWidth(); 
35018         
35019         this.measureColumns();
35020
35021         // reset column Y
35022         var i = this.cols;
35023         this.colYs = [];
35024         while (i--) {
35025             this.colYs.push( 0 );
35026         }
35027     
35028         this.maxY = 0;
35029     },
35030
35031     measureColumns : function()
35032     {
35033         this.getContainerWidth();
35034       // if columnWidth is 0, default to outerWidth of first item
35035         if ( !this.columnWidth ) {
35036             var firstItem = this.bricks.first();
35037             Roo.log(firstItem);
35038             this.columnWidth  = this.containerWidth;
35039             if (firstItem && firstItem.attr('originalwidth') ) {
35040                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35041             }
35042             // columnWidth fall back to item of first element
35043             Roo.log("set column width?");
35044                         this.initialColumnWidth = this.columnWidth  ;
35045
35046             // if first elem has no width, default to size of container
35047             
35048         }
35049         
35050         
35051         if (this.initialColumnWidth) {
35052             this.columnWidth = this.initialColumnWidth;
35053         }
35054         
35055         
35056             
35057         // column width is fixed at the top - however if container width get's smaller we should
35058         // reduce it...
35059         
35060         // this bit calcs how man columns..
35061             
35062         var columnWidth = this.columnWidth += this.gutter;
35063       
35064         // calculate columns
35065         var containerWidth = this.containerWidth + this.gutter;
35066         
35067         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35068         // fix rounding errors, typically with gutters
35069         var excess = columnWidth - containerWidth % columnWidth;
35070         
35071         
35072         // if overshoot is less than a pixel, round up, otherwise floor it
35073         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35074         cols = Math[ mathMethod ]( cols );
35075         this.cols = Math.max( cols, 1 );
35076         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35077         
35078          // padding positioning..
35079         var totalColWidth = this.cols * this.columnWidth;
35080         var padavail = this.containerWidth - totalColWidth;
35081         // so for 2 columns - we need 3 'pads'
35082         
35083         var padNeeded = (1+this.cols) * this.padWidth;
35084         
35085         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35086         
35087         this.columnWidth += padExtra
35088         //this.padWidth = Math.floor(padavail /  ( this.cols));
35089         
35090         // adjust colum width so that padding is fixed??
35091         
35092         // we have 3 columns ... total = width * 3
35093         // we have X left over... that should be used by 
35094         
35095         //if (this.expandC) {
35096             
35097         //}
35098         
35099         
35100         
35101     },
35102     
35103     getContainerWidth : function()
35104     {
35105        /* // container is parent if fit width
35106         var container = this.isFitWidth ? this.element.parentNode : this.element;
35107         // check that this.size and size are there
35108         // IE8 triggers resize on body size change, so they might not be
35109         
35110         var size = getSize( container );  //FIXME
35111         this.containerWidth = size && size.innerWidth; //FIXME
35112         */
35113          
35114         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35115         
35116     },
35117     
35118     _getItemLayoutPosition : function( item )  // what is item?
35119     {
35120         // we resize the item to our columnWidth..
35121       
35122         item.setWidth(this.columnWidth);
35123         item.autoBoxAdjust  = false;
35124         
35125         var sz = item.getSize();
35126  
35127         // how many columns does this brick span
35128         var remainder = this.containerWidth % this.columnWidth;
35129         
35130         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35131         // round if off by 1 pixel, otherwise use ceil
35132         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35133         colSpan = Math.min( colSpan, this.cols );
35134         
35135         // normally this should be '1' as we dont' currently allow multi width columns..
35136         
35137         var colGroup = this._getColGroup( colSpan );
35138         // get the minimum Y value from the columns
35139         var minimumY = Math.min.apply( Math, colGroup );
35140         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35141         
35142         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35143          
35144         // position the brick
35145         var position = {
35146             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35147             y: this.currentSize.y + minimumY + this.padHeight
35148         };
35149         
35150         Roo.log(position);
35151         // apply setHeight to necessary columns
35152         var setHeight = minimumY + sz.height + this.padHeight;
35153         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35154         
35155         var setSpan = this.cols + 1 - colGroup.length;
35156         for ( var i = 0; i < setSpan; i++ ) {
35157           this.colYs[ shortColIndex + i ] = setHeight ;
35158         }
35159       
35160         return position;
35161     },
35162     
35163     /**
35164      * @param {Number} colSpan - number of columns the element spans
35165      * @returns {Array} colGroup
35166      */
35167     _getColGroup : function( colSpan )
35168     {
35169         if ( colSpan < 2 ) {
35170           // if brick spans only one column, use all the column Ys
35171           return this.colYs;
35172         }
35173       
35174         var colGroup = [];
35175         // how many different places could this brick fit horizontally
35176         var groupCount = this.cols + 1 - colSpan;
35177         // for each group potential horizontal position
35178         for ( var i = 0; i < groupCount; i++ ) {
35179           // make an array of colY values for that one group
35180           var groupColYs = this.colYs.slice( i, i + colSpan );
35181           // and get the max value of the array
35182           colGroup[i] = Math.max.apply( Math, groupColYs );
35183         }
35184         return colGroup;
35185     },
35186     /*
35187     _manageStamp : function( stamp )
35188     {
35189         var stampSize =  stamp.getSize();
35190         var offset = stamp.getBox();
35191         // get the columns that this stamp affects
35192         var firstX = this.isOriginLeft ? offset.x : offset.right;
35193         var lastX = firstX + stampSize.width;
35194         var firstCol = Math.floor( firstX / this.columnWidth );
35195         firstCol = Math.max( 0, firstCol );
35196         
35197         var lastCol = Math.floor( lastX / this.columnWidth );
35198         // lastCol should not go over if multiple of columnWidth #425
35199         lastCol -= lastX % this.columnWidth ? 0 : 1;
35200         lastCol = Math.min( this.cols - 1, lastCol );
35201         
35202         // set colYs to bottom of the stamp
35203         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35204             stampSize.height;
35205             
35206         for ( var i = firstCol; i <= lastCol; i++ ) {
35207           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35208         }
35209     },
35210     */
35211     
35212     _getContainerSize : function()
35213     {
35214         this.maxY = Math.max.apply( Math, this.colYs );
35215         var size = {
35216             height: this.maxY
35217         };
35218       
35219         if ( this.isFitWidth ) {
35220             size.width = this._getContainerFitWidth();
35221         }
35222       
35223         return size;
35224     },
35225     
35226     _getContainerFitWidth : function()
35227     {
35228         var unusedCols = 0;
35229         // count unused columns
35230         var i = this.cols;
35231         while ( --i ) {
35232           if ( this.colYs[i] !== 0 ) {
35233             break;
35234           }
35235           unusedCols++;
35236         }
35237         // fit container to columns that have been used
35238         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35239     },
35240     
35241     needsResizeLayout : function()
35242     {
35243         var previousWidth = this.containerWidth;
35244         this.getContainerWidth();
35245         return previousWidth !== this.containerWidth;
35246     }
35247  
35248 });
35249
35250  
35251
35252  /*
35253  * - LGPL
35254  *
35255  * element
35256  * 
35257  */
35258
35259 /**
35260  * @class Roo.bootstrap.MasonryBrick
35261  * @extends Roo.bootstrap.Component
35262  * Bootstrap MasonryBrick class
35263  * 
35264  * @constructor
35265  * Create a new MasonryBrick
35266  * @param {Object} config The config object
35267  */
35268
35269 Roo.bootstrap.MasonryBrick = function(config){
35270     
35271     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35272     
35273     Roo.bootstrap.MasonryBrick.register(this);
35274     
35275     this.addEvents({
35276         // raw events
35277         /**
35278          * @event click
35279          * When a MasonryBrick is clcik
35280          * @param {Roo.bootstrap.MasonryBrick} this
35281          * @param {Roo.EventObject} e
35282          */
35283         "click" : true
35284     });
35285 };
35286
35287 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35288     
35289     /**
35290      * @cfg {String} title
35291      */   
35292     title : '',
35293     /**
35294      * @cfg {String} html
35295      */   
35296     html : '',
35297     /**
35298      * @cfg {String} bgimage
35299      */   
35300     bgimage : '',
35301     /**
35302      * @cfg {String} videourl
35303      */   
35304     videourl : '',
35305     /**
35306      * @cfg {String} cls
35307      */   
35308     cls : '',
35309     /**
35310      * @cfg {String} href
35311      */   
35312     href : '',
35313     /**
35314      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35315      */   
35316     size : 'xs',
35317     
35318     /**
35319      * @cfg {String} placetitle (center|bottom)
35320      */   
35321     placetitle : '',
35322     
35323     /**
35324      * @cfg {Boolean} isFitContainer defalut true
35325      */   
35326     isFitContainer : true, 
35327     
35328     /**
35329      * @cfg {Boolean} preventDefault defalut false
35330      */   
35331     preventDefault : false, 
35332     
35333     /**
35334      * @cfg {Boolean} inverse defalut false
35335      */   
35336     maskInverse : false, 
35337     
35338     getAutoCreate : function()
35339     {
35340         if(!this.isFitContainer){
35341             return this.getSplitAutoCreate();
35342         }
35343         
35344         var cls = 'masonry-brick masonry-brick-full';
35345         
35346         if(this.href.length){
35347             cls += ' masonry-brick-link';
35348         }
35349         
35350         if(this.bgimage.length){
35351             cls += ' masonry-brick-image';
35352         }
35353         
35354         if(this.maskInverse){
35355             cls += ' mask-inverse';
35356         }
35357         
35358         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35359             cls += ' enable-mask';
35360         }
35361         
35362         if(this.size){
35363             cls += ' masonry-' + this.size + '-brick';
35364         }
35365         
35366         if(this.placetitle.length){
35367             
35368             switch (this.placetitle) {
35369                 case 'center' :
35370                     cls += ' masonry-center-title';
35371                     break;
35372                 case 'bottom' :
35373                     cls += ' masonry-bottom-title';
35374                     break;
35375                 default:
35376                     break;
35377             }
35378             
35379         } else {
35380             if(!this.html.length && !this.bgimage.length){
35381                 cls += ' masonry-center-title';
35382             }
35383
35384             if(!this.html.length && this.bgimage.length){
35385                 cls += ' masonry-bottom-title';
35386             }
35387         }
35388         
35389         if(this.cls){
35390             cls += ' ' + this.cls;
35391         }
35392         
35393         var cfg = {
35394             tag: (this.href.length) ? 'a' : 'div',
35395             cls: cls,
35396             cn: [
35397                 {
35398                     tag: 'div',
35399                     cls: 'masonry-brick-mask'
35400                 },
35401                 {
35402                     tag: 'div',
35403                     cls: 'masonry-brick-paragraph',
35404                     cn: []
35405                 }
35406             ]
35407         };
35408         
35409         if(this.href.length){
35410             cfg.href = this.href;
35411         }
35412         
35413         var cn = cfg.cn[1].cn;
35414         
35415         if(this.title.length){
35416             cn.push({
35417                 tag: 'h4',
35418                 cls: 'masonry-brick-title',
35419                 html: this.title
35420             });
35421         }
35422         
35423         if(this.html.length){
35424             cn.push({
35425                 tag: 'p',
35426                 cls: 'masonry-brick-text',
35427                 html: this.html
35428             });
35429         }
35430         
35431         if (!this.title.length && !this.html.length) {
35432             cfg.cn[1].cls += ' hide';
35433         }
35434         
35435         if(this.bgimage.length){
35436             cfg.cn.push({
35437                 tag: 'img',
35438                 cls: 'masonry-brick-image-view',
35439                 src: this.bgimage
35440             });
35441         }
35442         
35443         if(this.videourl.length){
35444             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35445             // youtube support only?
35446             cfg.cn.push({
35447                 tag: 'iframe',
35448                 cls: 'masonry-brick-image-view',
35449                 src: vurl,
35450                 frameborder : 0,
35451                 allowfullscreen : true
35452             });
35453         }
35454         
35455         return cfg;
35456         
35457     },
35458     
35459     getSplitAutoCreate : function()
35460     {
35461         var cls = 'masonry-brick masonry-brick-split';
35462         
35463         if(this.href.length){
35464             cls += ' masonry-brick-link';
35465         }
35466         
35467         if(this.bgimage.length){
35468             cls += ' masonry-brick-image';
35469         }
35470         
35471         if(this.size){
35472             cls += ' masonry-' + this.size + '-brick';
35473         }
35474         
35475         switch (this.placetitle) {
35476             case 'center' :
35477                 cls += ' masonry-center-title';
35478                 break;
35479             case 'bottom' :
35480                 cls += ' masonry-bottom-title';
35481                 break;
35482             default:
35483                 if(!this.bgimage.length){
35484                     cls += ' masonry-center-title';
35485                 }
35486
35487                 if(this.bgimage.length){
35488                     cls += ' masonry-bottom-title';
35489                 }
35490                 break;
35491         }
35492         
35493         if(this.cls){
35494             cls += ' ' + this.cls;
35495         }
35496         
35497         var cfg = {
35498             tag: (this.href.length) ? 'a' : 'div',
35499             cls: cls,
35500             cn: [
35501                 {
35502                     tag: 'div',
35503                     cls: 'masonry-brick-split-head',
35504                     cn: [
35505                         {
35506                             tag: 'div',
35507                             cls: 'masonry-brick-paragraph',
35508                             cn: []
35509                         }
35510                     ]
35511                 },
35512                 {
35513                     tag: 'div',
35514                     cls: 'masonry-brick-split-body',
35515                     cn: []
35516                 }
35517             ]
35518         };
35519         
35520         if(this.href.length){
35521             cfg.href = this.href;
35522         }
35523         
35524         if(this.title.length){
35525             cfg.cn[0].cn[0].cn.push({
35526                 tag: 'h4',
35527                 cls: 'masonry-brick-title',
35528                 html: this.title
35529             });
35530         }
35531         
35532         if(this.html.length){
35533             cfg.cn[1].cn.push({
35534                 tag: 'p',
35535                 cls: 'masonry-brick-text',
35536                 html: this.html
35537             });
35538         }
35539
35540         if(this.bgimage.length){
35541             cfg.cn[0].cn.push({
35542                 tag: 'img',
35543                 cls: 'masonry-brick-image-view',
35544                 src: this.bgimage
35545             });
35546         }
35547         
35548         if(this.videourl.length){
35549             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35550             // youtube support only?
35551             cfg.cn[0].cn.cn.push({
35552                 tag: 'iframe',
35553                 cls: 'masonry-brick-image-view',
35554                 src: vurl,
35555                 frameborder : 0,
35556                 allowfullscreen : true
35557             });
35558         }
35559         
35560         return cfg;
35561     },
35562     
35563     initEvents: function() 
35564     {
35565         switch (this.size) {
35566             case 'xs' :
35567                 this.x = 1;
35568                 this.y = 1;
35569                 break;
35570             case 'sm' :
35571                 this.x = 2;
35572                 this.y = 2;
35573                 break;
35574             case 'md' :
35575             case 'md-left' :
35576             case 'md-right' :
35577                 this.x = 3;
35578                 this.y = 3;
35579                 break;
35580             case 'tall' :
35581                 this.x = 2;
35582                 this.y = 3;
35583                 break;
35584             case 'wide' :
35585                 this.x = 3;
35586                 this.y = 2;
35587                 break;
35588             case 'wide-thin' :
35589                 this.x = 3;
35590                 this.y = 1;
35591                 break;
35592                         
35593             default :
35594                 break;
35595         }
35596         
35597         if(Roo.isTouch){
35598             this.el.on('touchstart', this.onTouchStart, this);
35599             this.el.on('touchmove', this.onTouchMove, this);
35600             this.el.on('touchend', this.onTouchEnd, this);
35601             this.el.on('contextmenu', this.onContextMenu, this);
35602         } else {
35603             this.el.on('mouseenter'  ,this.enter, this);
35604             this.el.on('mouseleave', this.leave, this);
35605             this.el.on('click', this.onClick, this);
35606         }
35607         
35608         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35609             this.parent().bricks.push(this);   
35610         }
35611         
35612     },
35613     
35614     onClick: function(e, el)
35615     {
35616         var time = this.endTimer - this.startTimer;
35617         // Roo.log(e.preventDefault());
35618         if(Roo.isTouch){
35619             if(time > 1000){
35620                 e.preventDefault();
35621                 return;
35622             }
35623         }
35624         
35625         if(!this.preventDefault){
35626             return;
35627         }
35628         
35629         e.preventDefault();
35630         
35631         if (this.activeClass != '') {
35632             this.selectBrick();
35633         }
35634         
35635         this.fireEvent('click', this, e);
35636     },
35637     
35638     enter: function(e, el)
35639     {
35640         e.preventDefault();
35641         
35642         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35643             return;
35644         }
35645         
35646         if(this.bgimage.length && this.html.length){
35647             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35648         }
35649     },
35650     
35651     leave: function(e, el)
35652     {
35653         e.preventDefault();
35654         
35655         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35656             return;
35657         }
35658         
35659         if(this.bgimage.length && this.html.length){
35660             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35661         }
35662     },
35663     
35664     onTouchStart: function(e, el)
35665     {
35666 //        e.preventDefault();
35667         
35668         this.touchmoved = false;
35669         
35670         if(!this.isFitContainer){
35671             return;
35672         }
35673         
35674         if(!this.bgimage.length || !this.html.length){
35675             return;
35676         }
35677         
35678         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35679         
35680         this.timer = new Date().getTime();
35681         
35682     },
35683     
35684     onTouchMove: function(e, el)
35685     {
35686         this.touchmoved = true;
35687     },
35688     
35689     onContextMenu : function(e,el)
35690     {
35691         e.preventDefault();
35692         e.stopPropagation();
35693         return false;
35694     },
35695     
35696     onTouchEnd: function(e, el)
35697     {
35698 //        e.preventDefault();
35699         
35700         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35701         
35702             this.leave(e,el);
35703             
35704             return;
35705         }
35706         
35707         if(!this.bgimage.length || !this.html.length){
35708             
35709             if(this.href.length){
35710                 window.location.href = this.href;
35711             }
35712             
35713             return;
35714         }
35715         
35716         if(!this.isFitContainer){
35717             return;
35718         }
35719         
35720         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35721         
35722         window.location.href = this.href;
35723     },
35724     
35725     //selection on single brick only
35726     selectBrick : function() {
35727         
35728         if (!this.parentId) {
35729             return;
35730         }
35731         
35732         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35733         var index = m.selectedBrick.indexOf(this.id);
35734         
35735         if ( index > -1) {
35736             m.selectedBrick.splice(index,1);
35737             this.el.removeClass(this.activeClass);
35738             return;
35739         }
35740         
35741         for(var i = 0; i < m.selectedBrick.length; i++) {
35742             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35743             b.el.removeClass(b.activeClass);
35744         }
35745         
35746         m.selectedBrick = [];
35747         
35748         m.selectedBrick.push(this.id);
35749         this.el.addClass(this.activeClass);
35750         return;
35751     },
35752     
35753     isSelected : function(){
35754         return this.el.hasClass(this.activeClass);
35755         
35756     }
35757 });
35758
35759 Roo.apply(Roo.bootstrap.MasonryBrick, {
35760     
35761     //groups: {},
35762     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35763      /**
35764     * register a Masonry Brick
35765     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35766     */
35767     
35768     register : function(brick)
35769     {
35770         //this.groups[brick.id] = brick;
35771         this.groups.add(brick.id, brick);
35772     },
35773     /**
35774     * fetch a  masonry brick based on the masonry brick ID
35775     * @param {string} the masonry brick to add
35776     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35777     */
35778     
35779     get: function(brick_id) 
35780     {
35781         // if (typeof(this.groups[brick_id]) == 'undefined') {
35782         //     return false;
35783         // }
35784         // return this.groups[brick_id] ;
35785         
35786         if(this.groups.key(brick_id)) {
35787             return this.groups.key(brick_id);
35788         }
35789         
35790         return false;
35791     }
35792     
35793     
35794     
35795 });
35796
35797  /*
35798  * - LGPL
35799  *
35800  * element
35801  * 
35802  */
35803
35804 /**
35805  * @class Roo.bootstrap.Brick
35806  * @extends Roo.bootstrap.Component
35807  * Bootstrap Brick class
35808  * 
35809  * @constructor
35810  * Create a new Brick
35811  * @param {Object} config The config object
35812  */
35813
35814 Roo.bootstrap.Brick = function(config){
35815     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35816     
35817     this.addEvents({
35818         // raw events
35819         /**
35820          * @event click
35821          * When a Brick is click
35822          * @param {Roo.bootstrap.Brick} this
35823          * @param {Roo.EventObject} e
35824          */
35825         "click" : true
35826     });
35827 };
35828
35829 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35830     
35831     /**
35832      * @cfg {String} title
35833      */   
35834     title : '',
35835     /**
35836      * @cfg {String} html
35837      */   
35838     html : '',
35839     /**
35840      * @cfg {String} bgimage
35841      */   
35842     bgimage : '',
35843     /**
35844      * @cfg {String} cls
35845      */   
35846     cls : '',
35847     /**
35848      * @cfg {String} href
35849      */   
35850     href : '',
35851     /**
35852      * @cfg {String} video
35853      */   
35854     video : '',
35855     /**
35856      * @cfg {Boolean} square
35857      */   
35858     square : true,
35859     
35860     getAutoCreate : function()
35861     {
35862         var cls = 'roo-brick';
35863         
35864         if(this.href.length){
35865             cls += ' roo-brick-link';
35866         }
35867         
35868         if(this.bgimage.length){
35869             cls += ' roo-brick-image';
35870         }
35871         
35872         if(!this.html.length && !this.bgimage.length){
35873             cls += ' roo-brick-center-title';
35874         }
35875         
35876         if(!this.html.length && this.bgimage.length){
35877             cls += ' roo-brick-bottom-title';
35878         }
35879         
35880         if(this.cls){
35881             cls += ' ' + this.cls;
35882         }
35883         
35884         var cfg = {
35885             tag: (this.href.length) ? 'a' : 'div',
35886             cls: cls,
35887             cn: [
35888                 {
35889                     tag: 'div',
35890                     cls: 'roo-brick-paragraph',
35891                     cn: []
35892                 }
35893             ]
35894         };
35895         
35896         if(this.href.length){
35897             cfg.href = this.href;
35898         }
35899         
35900         var cn = cfg.cn[0].cn;
35901         
35902         if(this.title.length){
35903             cn.push({
35904                 tag: 'h4',
35905                 cls: 'roo-brick-title',
35906                 html: this.title
35907             });
35908         }
35909         
35910         if(this.html.length){
35911             cn.push({
35912                 tag: 'p',
35913                 cls: 'roo-brick-text',
35914                 html: this.html
35915             });
35916         } else {
35917             cn.cls += ' hide';
35918         }
35919         
35920         if(this.bgimage.length){
35921             cfg.cn.push({
35922                 tag: 'img',
35923                 cls: 'roo-brick-image-view',
35924                 src: this.bgimage
35925             });
35926         }
35927         
35928         return cfg;
35929     },
35930     
35931     initEvents: function() 
35932     {
35933         if(this.title.length || this.html.length){
35934             this.el.on('mouseenter'  ,this.enter, this);
35935             this.el.on('mouseleave', this.leave, this);
35936         }
35937         
35938         Roo.EventManager.onWindowResize(this.resize, this); 
35939         
35940         if(this.bgimage.length){
35941             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35942             this.imageEl.on('load', this.onImageLoad, this);
35943             return;
35944         }
35945         
35946         this.resize();
35947     },
35948     
35949     onImageLoad : function()
35950     {
35951         this.resize();
35952     },
35953     
35954     resize : function()
35955     {
35956         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35957         
35958         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35959         
35960         if(this.bgimage.length){
35961             var image = this.el.select('.roo-brick-image-view', true).first();
35962             
35963             image.setWidth(paragraph.getWidth());
35964             
35965             if(this.square){
35966                 image.setHeight(paragraph.getWidth());
35967             }
35968             
35969             this.el.setHeight(image.getHeight());
35970             paragraph.setHeight(image.getHeight());
35971             
35972         }
35973         
35974     },
35975     
35976     enter: function(e, el)
35977     {
35978         e.preventDefault();
35979         
35980         if(this.bgimage.length){
35981             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35982             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35983         }
35984     },
35985     
35986     leave: function(e, el)
35987     {
35988         e.preventDefault();
35989         
35990         if(this.bgimage.length){
35991             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35992             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35993         }
35994     }
35995     
35996 });
35997
35998  
35999
36000  /*
36001  * - LGPL
36002  *
36003  * Number field 
36004  */
36005
36006 /**
36007  * @class Roo.bootstrap.NumberField
36008  * @extends Roo.bootstrap.Input
36009  * Bootstrap NumberField class
36010  * 
36011  * 
36012  * 
36013  * 
36014  * @constructor
36015  * Create a new NumberField
36016  * @param {Object} config The config object
36017  */
36018
36019 Roo.bootstrap.NumberField = function(config){
36020     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36021 };
36022
36023 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36024     
36025     /**
36026      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36027      */
36028     allowDecimals : true,
36029     /**
36030      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36031      */
36032     decimalSeparator : ".",
36033     /**
36034      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36035      */
36036     decimalPrecision : 2,
36037     /**
36038      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36039      */
36040     allowNegative : true,
36041     
36042     /**
36043      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36044      */
36045     allowZero: true,
36046     /**
36047      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36048      */
36049     minValue : Number.NEGATIVE_INFINITY,
36050     /**
36051      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36052      */
36053     maxValue : Number.MAX_VALUE,
36054     /**
36055      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36056      */
36057     minText : "The minimum value for this field is {0}",
36058     /**
36059      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36060      */
36061     maxText : "The maximum value for this field is {0}",
36062     /**
36063      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36064      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36065      */
36066     nanText : "{0} is not a valid number",
36067     /**
36068      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36069      */
36070     thousandsDelimiter : false,
36071     /**
36072      * @cfg {String} valueAlign alignment of value
36073      */
36074     valueAlign : "left",
36075
36076     getAutoCreate : function()
36077     {
36078         var hiddenInput = {
36079             tag: 'input',
36080             type: 'hidden',
36081             id: Roo.id(),
36082             cls: 'hidden-number-input'
36083         };
36084         
36085         if (this.name) {
36086             hiddenInput.name = this.name;
36087         }
36088         
36089         this.name = '';
36090         
36091         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36092         
36093         this.name = hiddenInput.name;
36094         
36095         if(cfg.cn.length > 0) {
36096             cfg.cn.push(hiddenInput);
36097         }
36098         
36099         return cfg;
36100     },
36101
36102     // private
36103     initEvents : function()
36104     {   
36105         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36106         
36107         var allowed = "0123456789";
36108         
36109         if(this.allowDecimals){
36110             allowed += this.decimalSeparator;
36111         }
36112         
36113         if(this.allowNegative){
36114             allowed += "-";
36115         }
36116         
36117         if(this.thousandsDelimiter) {
36118             allowed += ",";
36119         }
36120         
36121         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36122         
36123         var keyPress = function(e){
36124             
36125             var k = e.getKey();
36126             
36127             var c = e.getCharCode();
36128             
36129             if(
36130                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36131                     allowed.indexOf(String.fromCharCode(c)) === -1
36132             ){
36133                 e.stopEvent();
36134                 return;
36135             }
36136             
36137             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36138                 return;
36139             }
36140             
36141             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36142                 e.stopEvent();
36143             }
36144         };
36145         
36146         this.el.on("keypress", keyPress, this);
36147     },
36148     
36149     validateValue : function(value)
36150     {
36151         
36152         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36153             return false;
36154         }
36155         
36156         var num = this.parseValue(value);
36157         
36158         if(isNaN(num)){
36159             this.markInvalid(String.format(this.nanText, value));
36160             return false;
36161         }
36162         
36163         if(num < this.minValue){
36164             this.markInvalid(String.format(this.minText, this.minValue));
36165             return false;
36166         }
36167         
36168         if(num > this.maxValue){
36169             this.markInvalid(String.format(this.maxText, this.maxValue));
36170             return false;
36171         }
36172         
36173         return true;
36174     },
36175
36176     getValue : function()
36177     {
36178         var v = this.hiddenEl().getValue();
36179         
36180         return this.fixPrecision(this.parseValue(v));
36181     },
36182
36183     parseValue : function(value)
36184     {
36185         if(this.thousandsDelimiter) {
36186             value += "";
36187             r = new RegExp(",", "g");
36188             value = value.replace(r, "");
36189         }
36190         
36191         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36192         return isNaN(value) ? '' : value;
36193     },
36194
36195     fixPrecision : function(value)
36196     {
36197         if(this.thousandsDelimiter) {
36198             value += "";
36199             r = new RegExp(",", "g");
36200             value = value.replace(r, "");
36201         }
36202         
36203         var nan = isNaN(value);
36204         
36205         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36206             return nan ? '' : value;
36207         }
36208         return parseFloat(value).toFixed(this.decimalPrecision);
36209     },
36210
36211     setValue : function(v)
36212     {
36213         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36214         
36215         this.value = v;
36216         
36217         if(this.rendered){
36218             
36219             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36220             
36221             this.inputEl().dom.value = (v == '') ? '' :
36222                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36223             
36224             if(!this.allowZero && v === '0') {
36225                 this.hiddenEl().dom.value = '';
36226                 this.inputEl().dom.value = '';
36227             }
36228             
36229             this.validate();
36230         }
36231     },
36232
36233     decimalPrecisionFcn : function(v)
36234     {
36235         return Math.floor(v);
36236     },
36237
36238     beforeBlur : function()
36239     {
36240         var v = this.parseValue(this.getRawValue());
36241         
36242         if(v || v === 0 || v === ''){
36243             this.setValue(v);
36244         }
36245     },
36246     
36247     hiddenEl : function()
36248     {
36249         return this.el.select('input.hidden-number-input',true).first();
36250     }
36251     
36252 });
36253
36254  
36255
36256 /*
36257 * Licence: LGPL
36258 */
36259
36260 /**
36261  * @class Roo.bootstrap.DocumentSlider
36262  * @extends Roo.bootstrap.Component
36263  * Bootstrap DocumentSlider class
36264  * 
36265  * @constructor
36266  * Create a new DocumentViewer
36267  * @param {Object} config The config object
36268  */
36269
36270 Roo.bootstrap.DocumentSlider = function(config){
36271     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36272     
36273     this.files = [];
36274     
36275     this.addEvents({
36276         /**
36277          * @event initial
36278          * Fire after initEvent
36279          * @param {Roo.bootstrap.DocumentSlider} this
36280          */
36281         "initial" : true,
36282         /**
36283          * @event update
36284          * Fire after update
36285          * @param {Roo.bootstrap.DocumentSlider} this
36286          */
36287         "update" : true,
36288         /**
36289          * @event click
36290          * Fire after click
36291          * @param {Roo.bootstrap.DocumentSlider} this
36292          */
36293         "click" : true
36294     });
36295 };
36296
36297 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36298     
36299     files : false,
36300     
36301     indicator : 0,
36302     
36303     getAutoCreate : function()
36304     {
36305         var cfg = {
36306             tag : 'div',
36307             cls : 'roo-document-slider',
36308             cn : [
36309                 {
36310                     tag : 'div',
36311                     cls : 'roo-document-slider-header',
36312                     cn : [
36313                         {
36314                             tag : 'div',
36315                             cls : 'roo-document-slider-header-title'
36316                         }
36317                     ]
36318                 },
36319                 {
36320                     tag : 'div',
36321                     cls : 'roo-document-slider-body',
36322                     cn : [
36323                         {
36324                             tag : 'div',
36325                             cls : 'roo-document-slider-prev',
36326                             cn : [
36327                                 {
36328                                     tag : 'i',
36329                                     cls : 'fa fa-chevron-left'
36330                                 }
36331                             ]
36332                         },
36333                         {
36334                             tag : 'div',
36335                             cls : 'roo-document-slider-thumb',
36336                             cn : [
36337                                 {
36338                                     tag : 'img',
36339                                     cls : 'roo-document-slider-image'
36340                                 }
36341                             ]
36342                         },
36343                         {
36344                             tag : 'div',
36345                             cls : 'roo-document-slider-next',
36346                             cn : [
36347                                 {
36348                                     tag : 'i',
36349                                     cls : 'fa fa-chevron-right'
36350                                 }
36351                             ]
36352                         }
36353                     ]
36354                 }
36355             ]
36356         };
36357         
36358         return cfg;
36359     },
36360     
36361     initEvents : function()
36362     {
36363         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36364         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36365         
36366         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36367         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36368         
36369         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36370         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36371         
36372         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36373         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36374         
36375         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36376         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36377         
36378         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36379         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36380         
36381         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36382         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36383         
36384         this.thumbEl.on('click', this.onClick, this);
36385         
36386         this.prevIndicator.on('click', this.prev, this);
36387         
36388         this.nextIndicator.on('click', this.next, this);
36389         
36390     },
36391     
36392     initial : function()
36393     {
36394         if(this.files.length){
36395             this.indicator = 1;
36396             this.update()
36397         }
36398         
36399         this.fireEvent('initial', this);
36400     },
36401     
36402     update : function()
36403     {
36404         this.imageEl.attr('src', this.files[this.indicator - 1]);
36405         
36406         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36407         
36408         this.prevIndicator.show();
36409         
36410         if(this.indicator == 1){
36411             this.prevIndicator.hide();
36412         }
36413         
36414         this.nextIndicator.show();
36415         
36416         if(this.indicator == this.files.length){
36417             this.nextIndicator.hide();
36418         }
36419         
36420         this.thumbEl.scrollTo('top');
36421         
36422         this.fireEvent('update', this);
36423     },
36424     
36425     onClick : function(e)
36426     {
36427         e.preventDefault();
36428         
36429         this.fireEvent('click', this);
36430     },
36431     
36432     prev : function(e)
36433     {
36434         e.preventDefault();
36435         
36436         this.indicator = Math.max(1, this.indicator - 1);
36437         
36438         this.update();
36439     },
36440     
36441     next : function(e)
36442     {
36443         e.preventDefault();
36444         
36445         this.indicator = Math.min(this.files.length, this.indicator + 1);
36446         
36447         this.update();
36448     }
36449 });
36450 /*
36451  * - LGPL
36452  *
36453  * RadioSet
36454  *
36455  *
36456  */
36457
36458 /**
36459  * @class Roo.bootstrap.RadioSet
36460  * @extends Roo.bootstrap.Input
36461  * Bootstrap RadioSet class
36462  * @cfg {String} indicatorpos (left|right) default left
36463  * @cfg {Boolean} inline (true|false) inline the element (default true)
36464  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36465  * @constructor
36466  * Create a new RadioSet
36467  * @param {Object} config The config object
36468  */
36469
36470 Roo.bootstrap.RadioSet = function(config){
36471     
36472     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36473     
36474     this.radioes = [];
36475     
36476     Roo.bootstrap.RadioSet.register(this);
36477     
36478     this.addEvents({
36479         /**
36480         * @event check
36481         * Fires when the element is checked or unchecked.
36482         * @param {Roo.bootstrap.RadioSet} this This radio
36483         * @param {Roo.bootstrap.Radio} item The checked item
36484         */
36485        check : true,
36486        /**
36487         * @event click
36488         * Fires when the element is click.
36489         * @param {Roo.bootstrap.RadioSet} this This radio set
36490         * @param {Roo.bootstrap.Radio} item The checked item
36491         * @param {Roo.EventObject} e The event object
36492         */
36493        click : true
36494     });
36495     
36496 };
36497
36498 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36499
36500     radioes : false,
36501     
36502     inline : true,
36503     
36504     weight : '',
36505     
36506     indicatorpos : 'left',
36507     
36508     getAutoCreate : function()
36509     {
36510         var label = {
36511             tag : 'label',
36512             cls : 'roo-radio-set-label',
36513             cn : [
36514                 {
36515                     tag : 'span',
36516                     html : this.fieldLabel
36517                 }
36518             ]
36519         };
36520         if (Roo.bootstrap.version == 3) {
36521             
36522             
36523             if(this.indicatorpos == 'left'){
36524                 label.cn.unshift({
36525                     tag : 'i',
36526                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36527                     tooltip : 'This field is required'
36528                 });
36529             } else {
36530                 label.cn.push({
36531                     tag : 'i',
36532                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36533                     tooltip : 'This field is required'
36534                 });
36535             }
36536         }
36537         var items = {
36538             tag : 'div',
36539             cls : 'roo-radio-set-items'
36540         };
36541         
36542         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36543         
36544         if (align === 'left' && this.fieldLabel.length) {
36545             
36546             items = {
36547                 cls : "roo-radio-set-right", 
36548                 cn: [
36549                     items
36550                 ]
36551             };
36552             
36553             if(this.labelWidth > 12){
36554                 label.style = "width: " + this.labelWidth + 'px';
36555             }
36556             
36557             if(this.labelWidth < 13 && this.labelmd == 0){
36558                 this.labelmd = this.labelWidth;
36559             }
36560             
36561             if(this.labellg > 0){
36562                 label.cls += ' col-lg-' + this.labellg;
36563                 items.cls += ' col-lg-' + (12 - this.labellg);
36564             }
36565             
36566             if(this.labelmd > 0){
36567                 label.cls += ' col-md-' + this.labelmd;
36568                 items.cls += ' col-md-' + (12 - this.labelmd);
36569             }
36570             
36571             if(this.labelsm > 0){
36572                 label.cls += ' col-sm-' + this.labelsm;
36573                 items.cls += ' col-sm-' + (12 - this.labelsm);
36574             }
36575             
36576             if(this.labelxs > 0){
36577                 label.cls += ' col-xs-' + this.labelxs;
36578                 items.cls += ' col-xs-' + (12 - this.labelxs);
36579             }
36580         }
36581         
36582         var cfg = {
36583             tag : 'div',
36584             cls : 'roo-radio-set',
36585             cn : [
36586                 {
36587                     tag : 'input',
36588                     cls : 'roo-radio-set-input',
36589                     type : 'hidden',
36590                     name : this.name,
36591                     value : this.value ? this.value :  ''
36592                 },
36593                 label,
36594                 items
36595             ]
36596         };
36597         
36598         if(this.weight.length){
36599             cfg.cls += ' roo-radio-' + this.weight;
36600         }
36601         
36602         if(this.inline) {
36603             cfg.cls += ' roo-radio-set-inline';
36604         }
36605         
36606         var settings=this;
36607         ['xs','sm','md','lg'].map(function(size){
36608             if (settings[size]) {
36609                 cfg.cls += ' col-' + size + '-' + settings[size];
36610             }
36611         });
36612         
36613         return cfg;
36614         
36615     },
36616
36617     initEvents : function()
36618     {
36619         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36620         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36621         
36622         if(!this.fieldLabel.length){
36623             this.labelEl.hide();
36624         }
36625         
36626         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36627         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36628         
36629         this.indicator = this.indicatorEl();
36630         
36631         if(this.indicator){
36632             this.indicator.addClass('invisible');
36633         }
36634         
36635         this.originalValue = this.getValue();
36636         
36637     },
36638     
36639     inputEl: function ()
36640     {
36641         return this.el.select('.roo-radio-set-input', true).first();
36642     },
36643     
36644     getChildContainer : function()
36645     {
36646         return this.itemsEl;
36647     },
36648     
36649     register : function(item)
36650     {
36651         this.radioes.push(item);
36652         
36653     },
36654     
36655     validate : function()
36656     {   
36657         if(this.getVisibilityEl().hasClass('hidden')){
36658             return true;
36659         }
36660         
36661         var valid = false;
36662         
36663         Roo.each(this.radioes, function(i){
36664             if(!i.checked){
36665                 return;
36666             }
36667             
36668             valid = true;
36669             return false;
36670         });
36671         
36672         if(this.allowBlank) {
36673             return true;
36674         }
36675         
36676         if(this.disabled || valid){
36677             this.markValid();
36678             return true;
36679         }
36680         
36681         this.markInvalid();
36682         return false;
36683         
36684     },
36685     
36686     markValid : function()
36687     {
36688         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36689             this.indicatorEl().removeClass('visible');
36690             this.indicatorEl().addClass('invisible');
36691         }
36692         
36693         
36694         if (Roo.bootstrap.version == 3) {
36695             this.el.removeClass([this.invalidClass, this.validClass]);
36696             this.el.addClass(this.validClass);
36697         } else {
36698             this.el.removeClass(['is-invalid','is-valid']);
36699             this.el.addClass(['is-valid']);
36700         }
36701         this.fireEvent('valid', this);
36702     },
36703     
36704     markInvalid : function(msg)
36705     {
36706         if(this.allowBlank || this.disabled){
36707             return;
36708         }
36709         
36710         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36711             this.indicatorEl().removeClass('invisible');
36712             this.indicatorEl().addClass('visible');
36713         }
36714         if (Roo.bootstrap.version == 3) {
36715             this.el.removeClass([this.invalidClass, this.validClass]);
36716             this.el.addClass(this.invalidClass);
36717         } else {
36718             this.el.removeClass(['is-invalid','is-valid']);
36719             this.el.addClass(['is-invalid']);
36720         }
36721         
36722         this.fireEvent('invalid', this, msg);
36723         
36724     },
36725     
36726     setValue : function(v, suppressEvent)
36727     {   
36728         if(this.value === v){
36729             return;
36730         }
36731         
36732         this.value = v;
36733         
36734         if(this.rendered){
36735             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36736         }
36737         
36738         Roo.each(this.radioes, function(i){
36739             i.checked = false;
36740             i.el.removeClass('checked');
36741         });
36742         
36743         Roo.each(this.radioes, function(i){
36744             
36745             if(i.value === v || i.value.toString() === v.toString()){
36746                 i.checked = true;
36747                 i.el.addClass('checked');
36748                 
36749                 if(suppressEvent !== true){
36750                     this.fireEvent('check', this, i);
36751                 }
36752                 
36753                 return false;
36754             }
36755             
36756         }, this);
36757         
36758         this.validate();
36759     },
36760     
36761     clearInvalid : function(){
36762         
36763         if(!this.el || this.preventMark){
36764             return;
36765         }
36766         
36767         this.el.removeClass([this.invalidClass]);
36768         
36769         this.fireEvent('valid', this);
36770     }
36771     
36772 });
36773
36774 Roo.apply(Roo.bootstrap.RadioSet, {
36775     
36776     groups: {},
36777     
36778     register : function(set)
36779     {
36780         this.groups[set.name] = set;
36781     },
36782     
36783     get: function(name) 
36784     {
36785         if (typeof(this.groups[name]) == 'undefined') {
36786             return false;
36787         }
36788         
36789         return this.groups[name] ;
36790     }
36791     
36792 });
36793 /*
36794  * Based on:
36795  * Ext JS Library 1.1.1
36796  * Copyright(c) 2006-2007, Ext JS, LLC.
36797  *
36798  * Originally Released Under LGPL - original licence link has changed is not relivant.
36799  *
36800  * Fork - LGPL
36801  * <script type="text/javascript">
36802  */
36803
36804
36805 /**
36806  * @class Roo.bootstrap.SplitBar
36807  * @extends Roo.util.Observable
36808  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36809  * <br><br>
36810  * Usage:
36811  * <pre><code>
36812 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36813                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36814 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36815 split.minSize = 100;
36816 split.maxSize = 600;
36817 split.animate = true;
36818 split.on('moved', splitterMoved);
36819 </code></pre>
36820  * @constructor
36821  * Create a new SplitBar
36822  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36823  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36824  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36825  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36826                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36827                         position of the SplitBar).
36828  */
36829 Roo.bootstrap.SplitBar = function(cfg){
36830     
36831     /** @private */
36832     
36833     //{
36834     //  dragElement : elm
36835     //  resizingElement: el,
36836         // optional..
36837     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36838     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36839         // existingProxy ???
36840     //}
36841     
36842     this.el = Roo.get(cfg.dragElement, true);
36843     this.el.dom.unselectable = "on";
36844     /** @private */
36845     this.resizingEl = Roo.get(cfg.resizingElement, true);
36846
36847     /**
36848      * @private
36849      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36850      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36851      * @type Number
36852      */
36853     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36854     
36855     /**
36856      * The minimum size of the resizing element. (Defaults to 0)
36857      * @type Number
36858      */
36859     this.minSize = 0;
36860     
36861     /**
36862      * The maximum size of the resizing element. (Defaults to 2000)
36863      * @type Number
36864      */
36865     this.maxSize = 2000;
36866     
36867     /**
36868      * Whether to animate the transition to the new size
36869      * @type Boolean
36870      */
36871     this.animate = false;
36872     
36873     /**
36874      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36875      * @type Boolean
36876      */
36877     this.useShim = false;
36878     
36879     /** @private */
36880     this.shim = null;
36881     
36882     if(!cfg.existingProxy){
36883         /** @private */
36884         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36885     }else{
36886         this.proxy = Roo.get(cfg.existingProxy).dom;
36887     }
36888     /** @private */
36889     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36890     
36891     /** @private */
36892     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36893     
36894     /** @private */
36895     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36896     
36897     /** @private */
36898     this.dragSpecs = {};
36899     
36900     /**
36901      * @private The adapter to use to positon and resize elements
36902      */
36903     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36904     this.adapter.init(this);
36905     
36906     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36907         /** @private */
36908         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36909         this.el.addClass("roo-splitbar-h");
36910     }else{
36911         /** @private */
36912         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36913         this.el.addClass("roo-splitbar-v");
36914     }
36915     
36916     this.addEvents({
36917         /**
36918          * @event resize
36919          * Fires when the splitter is moved (alias for {@link #event-moved})
36920          * @param {Roo.bootstrap.SplitBar} this
36921          * @param {Number} newSize the new width or height
36922          */
36923         "resize" : true,
36924         /**
36925          * @event moved
36926          * Fires when the splitter is moved
36927          * @param {Roo.bootstrap.SplitBar} this
36928          * @param {Number} newSize the new width or height
36929          */
36930         "moved" : true,
36931         /**
36932          * @event beforeresize
36933          * Fires before the splitter is dragged
36934          * @param {Roo.bootstrap.SplitBar} this
36935          */
36936         "beforeresize" : true,
36937
36938         "beforeapply" : true
36939     });
36940
36941     Roo.util.Observable.call(this);
36942 };
36943
36944 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36945     onStartProxyDrag : function(x, y){
36946         this.fireEvent("beforeresize", this);
36947         if(!this.overlay){
36948             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36949             o.unselectable();
36950             o.enableDisplayMode("block");
36951             // all splitbars share the same overlay
36952             Roo.bootstrap.SplitBar.prototype.overlay = o;
36953         }
36954         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36955         this.overlay.show();
36956         Roo.get(this.proxy).setDisplayed("block");
36957         var size = this.adapter.getElementSize(this);
36958         this.activeMinSize = this.getMinimumSize();;
36959         this.activeMaxSize = this.getMaximumSize();;
36960         var c1 = size - this.activeMinSize;
36961         var c2 = Math.max(this.activeMaxSize - size, 0);
36962         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36963             this.dd.resetConstraints();
36964             this.dd.setXConstraint(
36965                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36966                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36967             );
36968             this.dd.setYConstraint(0, 0);
36969         }else{
36970             this.dd.resetConstraints();
36971             this.dd.setXConstraint(0, 0);
36972             this.dd.setYConstraint(
36973                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36974                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36975             );
36976          }
36977         this.dragSpecs.startSize = size;
36978         this.dragSpecs.startPoint = [x, y];
36979         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36980     },
36981     
36982     /** 
36983      * @private Called after the drag operation by the DDProxy
36984      */
36985     onEndProxyDrag : function(e){
36986         Roo.get(this.proxy).setDisplayed(false);
36987         var endPoint = Roo.lib.Event.getXY(e);
36988         if(this.overlay){
36989             this.overlay.hide();
36990         }
36991         var newSize;
36992         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36993             newSize = this.dragSpecs.startSize + 
36994                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36995                     endPoint[0] - this.dragSpecs.startPoint[0] :
36996                     this.dragSpecs.startPoint[0] - endPoint[0]
36997                 );
36998         }else{
36999             newSize = this.dragSpecs.startSize + 
37000                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37001                     endPoint[1] - this.dragSpecs.startPoint[1] :
37002                     this.dragSpecs.startPoint[1] - endPoint[1]
37003                 );
37004         }
37005         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37006         if(newSize != this.dragSpecs.startSize){
37007             if(this.fireEvent('beforeapply', this, newSize) !== false){
37008                 this.adapter.setElementSize(this, newSize);
37009                 this.fireEvent("moved", this, newSize);
37010                 this.fireEvent("resize", this, newSize);
37011             }
37012         }
37013     },
37014     
37015     /**
37016      * Get the adapter this SplitBar uses
37017      * @return The adapter object
37018      */
37019     getAdapter : function(){
37020         return this.adapter;
37021     },
37022     
37023     /**
37024      * Set the adapter this SplitBar uses
37025      * @param {Object} adapter A SplitBar adapter object
37026      */
37027     setAdapter : function(adapter){
37028         this.adapter = adapter;
37029         this.adapter.init(this);
37030     },
37031     
37032     /**
37033      * Gets the minimum size for the resizing element
37034      * @return {Number} The minimum size
37035      */
37036     getMinimumSize : function(){
37037         return this.minSize;
37038     },
37039     
37040     /**
37041      * Sets the minimum size for the resizing element
37042      * @param {Number} minSize The minimum size
37043      */
37044     setMinimumSize : function(minSize){
37045         this.minSize = minSize;
37046     },
37047     
37048     /**
37049      * Gets the maximum size for the resizing element
37050      * @return {Number} The maximum size
37051      */
37052     getMaximumSize : function(){
37053         return this.maxSize;
37054     },
37055     
37056     /**
37057      * Sets the maximum size for the resizing element
37058      * @param {Number} maxSize The maximum size
37059      */
37060     setMaximumSize : function(maxSize){
37061         this.maxSize = maxSize;
37062     },
37063     
37064     /**
37065      * Sets the initialize size for the resizing element
37066      * @param {Number} size The initial size
37067      */
37068     setCurrentSize : function(size){
37069         var oldAnimate = this.animate;
37070         this.animate = false;
37071         this.adapter.setElementSize(this, size);
37072         this.animate = oldAnimate;
37073     },
37074     
37075     /**
37076      * Destroy this splitbar. 
37077      * @param {Boolean} removeEl True to remove the element
37078      */
37079     destroy : function(removeEl){
37080         if(this.shim){
37081             this.shim.remove();
37082         }
37083         this.dd.unreg();
37084         this.proxy.parentNode.removeChild(this.proxy);
37085         if(removeEl){
37086             this.el.remove();
37087         }
37088     }
37089 });
37090
37091 /**
37092  * @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.
37093  */
37094 Roo.bootstrap.SplitBar.createProxy = function(dir){
37095     var proxy = new Roo.Element(document.createElement("div"));
37096     proxy.unselectable();
37097     var cls = 'roo-splitbar-proxy';
37098     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37099     document.body.appendChild(proxy.dom);
37100     return proxy.dom;
37101 };
37102
37103 /** 
37104  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37105  * Default Adapter. It assumes the splitter and resizing element are not positioned
37106  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37107  */
37108 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37109 };
37110
37111 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37112     // do nothing for now
37113     init : function(s){
37114     
37115     },
37116     /**
37117      * Called before drag operations to get the current size of the resizing element. 
37118      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37119      */
37120      getElementSize : function(s){
37121         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37122             return s.resizingEl.getWidth();
37123         }else{
37124             return s.resizingEl.getHeight();
37125         }
37126     },
37127     
37128     /**
37129      * Called after drag operations to set the size of the resizing element.
37130      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37131      * @param {Number} newSize The new size to set
37132      * @param {Function} onComplete A function to be invoked when resizing is complete
37133      */
37134     setElementSize : function(s, newSize, onComplete){
37135         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37136             if(!s.animate){
37137                 s.resizingEl.setWidth(newSize);
37138                 if(onComplete){
37139                     onComplete(s, newSize);
37140                 }
37141             }else{
37142                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37143             }
37144         }else{
37145             
37146             if(!s.animate){
37147                 s.resizingEl.setHeight(newSize);
37148                 if(onComplete){
37149                     onComplete(s, newSize);
37150                 }
37151             }else{
37152                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37153             }
37154         }
37155     }
37156 };
37157
37158 /** 
37159  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37160  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37161  * Adapter that  moves the splitter element to align with the resized sizing element. 
37162  * Used with an absolute positioned SplitBar.
37163  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37164  * document.body, make sure you assign an id to the body element.
37165  */
37166 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37167     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37168     this.container = Roo.get(container);
37169 };
37170
37171 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37172     init : function(s){
37173         this.basic.init(s);
37174     },
37175     
37176     getElementSize : function(s){
37177         return this.basic.getElementSize(s);
37178     },
37179     
37180     setElementSize : function(s, newSize, onComplete){
37181         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37182     },
37183     
37184     moveSplitter : function(s){
37185         var yes = Roo.bootstrap.SplitBar;
37186         switch(s.placement){
37187             case yes.LEFT:
37188                 s.el.setX(s.resizingEl.getRight());
37189                 break;
37190             case yes.RIGHT:
37191                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37192                 break;
37193             case yes.TOP:
37194                 s.el.setY(s.resizingEl.getBottom());
37195                 break;
37196             case yes.BOTTOM:
37197                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37198                 break;
37199         }
37200     }
37201 };
37202
37203 /**
37204  * Orientation constant - Create a vertical SplitBar
37205  * @static
37206  * @type Number
37207  */
37208 Roo.bootstrap.SplitBar.VERTICAL = 1;
37209
37210 /**
37211  * Orientation constant - Create a horizontal SplitBar
37212  * @static
37213  * @type Number
37214  */
37215 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37216
37217 /**
37218  * Placement constant - The resizing element is to the left of the splitter element
37219  * @static
37220  * @type Number
37221  */
37222 Roo.bootstrap.SplitBar.LEFT = 1;
37223
37224 /**
37225  * Placement constant - The resizing element is to the right of the splitter element
37226  * @static
37227  * @type Number
37228  */
37229 Roo.bootstrap.SplitBar.RIGHT = 2;
37230
37231 /**
37232  * Placement constant - The resizing element is positioned above the splitter element
37233  * @static
37234  * @type Number
37235  */
37236 Roo.bootstrap.SplitBar.TOP = 3;
37237
37238 /**
37239  * Placement constant - The resizing element is positioned under splitter element
37240  * @static
37241  * @type Number
37242  */
37243 Roo.bootstrap.SplitBar.BOTTOM = 4;
37244 Roo.namespace("Roo.bootstrap.layout");/*
37245  * Based on:
37246  * Ext JS Library 1.1.1
37247  * Copyright(c) 2006-2007, Ext JS, LLC.
37248  *
37249  * Originally Released Under LGPL - original licence link has changed is not relivant.
37250  *
37251  * Fork - LGPL
37252  * <script type="text/javascript">
37253  */
37254
37255 /**
37256  * @class Roo.bootstrap.layout.Manager
37257  * @extends Roo.bootstrap.Component
37258  * Base class for layout managers.
37259  */
37260 Roo.bootstrap.layout.Manager = function(config)
37261 {
37262     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37263
37264
37265
37266
37267
37268     /** false to disable window resize monitoring @type Boolean */
37269     this.monitorWindowResize = true;
37270     this.regions = {};
37271     this.addEvents({
37272         /**
37273          * @event layout
37274          * Fires when a layout is performed.
37275          * @param {Roo.LayoutManager} this
37276          */
37277         "layout" : true,
37278         /**
37279          * @event regionresized
37280          * Fires when the user resizes a region.
37281          * @param {Roo.LayoutRegion} region The resized region
37282          * @param {Number} newSize The new size (width for east/west, height for north/south)
37283          */
37284         "regionresized" : true,
37285         /**
37286          * @event regioncollapsed
37287          * Fires when a region is collapsed.
37288          * @param {Roo.LayoutRegion} region The collapsed region
37289          */
37290         "regioncollapsed" : true,
37291         /**
37292          * @event regionexpanded
37293          * Fires when a region is expanded.
37294          * @param {Roo.LayoutRegion} region The expanded region
37295          */
37296         "regionexpanded" : true
37297     });
37298     this.updating = false;
37299
37300     if (config.el) {
37301         this.el = Roo.get(config.el);
37302         this.initEvents();
37303     }
37304
37305 };
37306
37307 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37308
37309
37310     regions : null,
37311
37312     monitorWindowResize : true,
37313
37314
37315     updating : false,
37316
37317
37318     onRender : function(ct, position)
37319     {
37320         if(!this.el){
37321             this.el = Roo.get(ct);
37322             this.initEvents();
37323         }
37324         //this.fireEvent('render',this);
37325     },
37326
37327
37328     initEvents: function()
37329     {
37330
37331
37332         // ie scrollbar fix
37333         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37334             document.body.scroll = "no";
37335         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37336             this.el.position('relative');
37337         }
37338         this.id = this.el.id;
37339         this.el.addClass("roo-layout-container");
37340         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37341         if(this.el.dom != document.body ) {
37342             this.el.on('resize', this.layout,this);
37343             this.el.on('show', this.layout,this);
37344         }
37345
37346     },
37347
37348     /**
37349      * Returns true if this layout is currently being updated
37350      * @return {Boolean}
37351      */
37352     isUpdating : function(){
37353         return this.updating;
37354     },
37355
37356     /**
37357      * Suspend the LayoutManager from doing auto-layouts while
37358      * making multiple add or remove calls
37359      */
37360     beginUpdate : function(){
37361         this.updating = true;
37362     },
37363
37364     /**
37365      * Restore auto-layouts and optionally disable the manager from performing a layout
37366      * @param {Boolean} noLayout true to disable a layout update
37367      */
37368     endUpdate : function(noLayout){
37369         this.updating = false;
37370         if(!noLayout){
37371             this.layout();
37372         }
37373     },
37374
37375     layout: function(){
37376         // abstract...
37377     },
37378
37379     onRegionResized : function(region, newSize){
37380         this.fireEvent("regionresized", region, newSize);
37381         this.layout();
37382     },
37383
37384     onRegionCollapsed : function(region){
37385         this.fireEvent("regioncollapsed", region);
37386     },
37387
37388     onRegionExpanded : function(region){
37389         this.fireEvent("regionexpanded", region);
37390     },
37391
37392     /**
37393      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37394      * performs box-model adjustments.
37395      * @return {Object} The size as an object {width: (the width), height: (the height)}
37396      */
37397     getViewSize : function()
37398     {
37399         var size;
37400         if(this.el.dom != document.body){
37401             size = this.el.getSize();
37402         }else{
37403             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37404         }
37405         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37406         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37407         return size;
37408     },
37409
37410     /**
37411      * Returns the Element this layout is bound to.
37412      * @return {Roo.Element}
37413      */
37414     getEl : function(){
37415         return this.el;
37416     },
37417
37418     /**
37419      * Returns the specified region.
37420      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37421      * @return {Roo.LayoutRegion}
37422      */
37423     getRegion : function(target){
37424         return this.regions[target.toLowerCase()];
37425     },
37426
37427     onWindowResize : function(){
37428         if(this.monitorWindowResize){
37429             this.layout();
37430         }
37431     }
37432 });
37433 /*
37434  * Based on:
37435  * Ext JS Library 1.1.1
37436  * Copyright(c) 2006-2007, Ext JS, LLC.
37437  *
37438  * Originally Released Under LGPL - original licence link has changed is not relivant.
37439  *
37440  * Fork - LGPL
37441  * <script type="text/javascript">
37442  */
37443 /**
37444  * @class Roo.bootstrap.layout.Border
37445  * @extends Roo.bootstrap.layout.Manager
37446  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37447  * please see: examples/bootstrap/nested.html<br><br>
37448  
37449 <b>The container the layout is rendered into can be either the body element or any other element.
37450 If it is not the body element, the container needs to either be an absolute positioned element,
37451 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37452 the container size if it is not the body element.</b>
37453
37454 * @constructor
37455 * Create a new Border
37456 * @param {Object} config Configuration options
37457  */
37458 Roo.bootstrap.layout.Border = function(config){
37459     config = config || {};
37460     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37461     
37462     
37463     
37464     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37465         if(config[region]){
37466             config[region].region = region;
37467             this.addRegion(config[region]);
37468         }
37469     },this);
37470     
37471 };
37472
37473 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37474
37475 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37476     
37477     parent : false, // this might point to a 'nest' or a ???
37478     
37479     /**
37480      * Creates and adds a new region if it doesn't already exist.
37481      * @param {String} target The target region key (north, south, east, west or center).
37482      * @param {Object} config The regions config object
37483      * @return {BorderLayoutRegion} The new region
37484      */
37485     addRegion : function(config)
37486     {
37487         if(!this.regions[config.region]){
37488             var r = this.factory(config);
37489             this.bindRegion(r);
37490         }
37491         return this.regions[config.region];
37492     },
37493
37494     // private (kinda)
37495     bindRegion : function(r){
37496         this.regions[r.config.region] = r;
37497         
37498         r.on("visibilitychange",    this.layout, this);
37499         r.on("paneladded",          this.layout, this);
37500         r.on("panelremoved",        this.layout, this);
37501         r.on("invalidated",         this.layout, this);
37502         r.on("resized",             this.onRegionResized, this);
37503         r.on("collapsed",           this.onRegionCollapsed, this);
37504         r.on("expanded",            this.onRegionExpanded, this);
37505     },
37506
37507     /**
37508      * Performs a layout update.
37509      */
37510     layout : function()
37511     {
37512         if(this.updating) {
37513             return;
37514         }
37515         
37516         // render all the rebions if they have not been done alreayd?
37517         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37518             if(this.regions[region] && !this.regions[region].bodyEl){
37519                 this.regions[region].onRender(this.el)
37520             }
37521         },this);
37522         
37523         var size = this.getViewSize();
37524         var w = size.width;
37525         var h = size.height;
37526         var centerW = w;
37527         var centerH = h;
37528         var centerY = 0;
37529         var centerX = 0;
37530         //var x = 0, y = 0;
37531
37532         var rs = this.regions;
37533         var north = rs["north"];
37534         var south = rs["south"]; 
37535         var west = rs["west"];
37536         var east = rs["east"];
37537         var center = rs["center"];
37538         //if(this.hideOnLayout){ // not supported anymore
37539             //c.el.setStyle("display", "none");
37540         //}
37541         if(north && north.isVisible()){
37542             var b = north.getBox();
37543             var m = north.getMargins();
37544             b.width = w - (m.left+m.right);
37545             b.x = m.left;
37546             b.y = m.top;
37547             centerY = b.height + b.y + m.bottom;
37548             centerH -= centerY;
37549             north.updateBox(this.safeBox(b));
37550         }
37551         if(south && south.isVisible()){
37552             var b = south.getBox();
37553             var m = south.getMargins();
37554             b.width = w - (m.left+m.right);
37555             b.x = m.left;
37556             var totalHeight = (b.height + m.top + m.bottom);
37557             b.y = h - totalHeight + m.top;
37558             centerH -= totalHeight;
37559             south.updateBox(this.safeBox(b));
37560         }
37561         if(west && west.isVisible()){
37562             var b = west.getBox();
37563             var m = west.getMargins();
37564             b.height = centerH - (m.top+m.bottom);
37565             b.x = m.left;
37566             b.y = centerY + m.top;
37567             var totalWidth = (b.width + m.left + m.right);
37568             centerX += totalWidth;
37569             centerW -= totalWidth;
37570             west.updateBox(this.safeBox(b));
37571         }
37572         if(east && east.isVisible()){
37573             var b = east.getBox();
37574             var m = east.getMargins();
37575             b.height = centerH - (m.top+m.bottom);
37576             var totalWidth = (b.width + m.left + m.right);
37577             b.x = w - totalWidth + m.left;
37578             b.y = centerY + m.top;
37579             centerW -= totalWidth;
37580             east.updateBox(this.safeBox(b));
37581         }
37582         if(center){
37583             var m = center.getMargins();
37584             var centerBox = {
37585                 x: centerX + m.left,
37586                 y: centerY + m.top,
37587                 width: centerW - (m.left+m.right),
37588                 height: centerH - (m.top+m.bottom)
37589             };
37590             //if(this.hideOnLayout){
37591                 //center.el.setStyle("display", "block");
37592             //}
37593             center.updateBox(this.safeBox(centerBox));
37594         }
37595         this.el.repaint();
37596         this.fireEvent("layout", this);
37597     },
37598
37599     // private
37600     safeBox : function(box){
37601         box.width = Math.max(0, box.width);
37602         box.height = Math.max(0, box.height);
37603         return box;
37604     },
37605
37606     /**
37607      * Adds a ContentPanel (or subclass) to this layout.
37608      * @param {String} target The target region key (north, south, east, west or center).
37609      * @param {Roo.ContentPanel} panel The panel to add
37610      * @return {Roo.ContentPanel} The added panel
37611      */
37612     add : function(target, panel){
37613          
37614         target = target.toLowerCase();
37615         return this.regions[target].add(panel);
37616     },
37617
37618     /**
37619      * Remove a ContentPanel (or subclass) to this layout.
37620      * @param {String} target The target region key (north, south, east, west or center).
37621      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37622      * @return {Roo.ContentPanel} The removed panel
37623      */
37624     remove : function(target, panel){
37625         target = target.toLowerCase();
37626         return this.regions[target].remove(panel);
37627     },
37628
37629     /**
37630      * Searches all regions for a panel with the specified id
37631      * @param {String} panelId
37632      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37633      */
37634     findPanel : function(panelId){
37635         var rs = this.regions;
37636         for(var target in rs){
37637             if(typeof rs[target] != "function"){
37638                 var p = rs[target].getPanel(panelId);
37639                 if(p){
37640                     return p;
37641                 }
37642             }
37643         }
37644         return null;
37645     },
37646
37647     /**
37648      * Searches all regions for a panel with the specified id and activates (shows) it.
37649      * @param {String/ContentPanel} panelId The panels id or the panel itself
37650      * @return {Roo.ContentPanel} The shown panel or null
37651      */
37652     showPanel : function(panelId) {
37653       var rs = this.regions;
37654       for(var target in rs){
37655          var r = rs[target];
37656          if(typeof r != "function"){
37657             if(r.hasPanel(panelId)){
37658                return r.showPanel(panelId);
37659             }
37660          }
37661       }
37662       return null;
37663    },
37664
37665    /**
37666      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37667      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37668      */
37669    /*
37670     restoreState : function(provider){
37671         if(!provider){
37672             provider = Roo.state.Manager;
37673         }
37674         var sm = new Roo.LayoutStateManager();
37675         sm.init(this, provider);
37676     },
37677 */
37678  
37679  
37680     /**
37681      * Adds a xtype elements to the layout.
37682      * <pre><code>
37683
37684 layout.addxtype({
37685        xtype : 'ContentPanel',
37686        region: 'west',
37687        items: [ .... ]
37688    }
37689 );
37690
37691 layout.addxtype({
37692         xtype : 'NestedLayoutPanel',
37693         region: 'west',
37694         layout: {
37695            center: { },
37696            west: { }   
37697         },
37698         items : [ ... list of content panels or nested layout panels.. ]
37699    }
37700 );
37701 </code></pre>
37702      * @param {Object} cfg Xtype definition of item to add.
37703      */
37704     addxtype : function(cfg)
37705     {
37706         // basically accepts a pannel...
37707         // can accept a layout region..!?!?
37708         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37709         
37710         
37711         // theory?  children can only be panels??
37712         
37713         //if (!cfg.xtype.match(/Panel$/)) {
37714         //    return false;
37715         //}
37716         var ret = false;
37717         
37718         if (typeof(cfg.region) == 'undefined') {
37719             Roo.log("Failed to add Panel, region was not set");
37720             Roo.log(cfg);
37721             return false;
37722         }
37723         var region = cfg.region;
37724         delete cfg.region;
37725         
37726           
37727         var xitems = [];
37728         if (cfg.items) {
37729             xitems = cfg.items;
37730             delete cfg.items;
37731         }
37732         var nb = false;
37733         
37734         if ( region == 'center') {
37735             Roo.log("Center: " + cfg.title);
37736         }
37737         
37738         
37739         switch(cfg.xtype) 
37740         {
37741             case 'Content':  // ContentPanel (el, cfg)
37742             case 'Scroll':  // ContentPanel (el, cfg)
37743             case 'View': 
37744                 cfg.autoCreate = cfg.autoCreate || true;
37745                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37746                 //} else {
37747                 //    var el = this.el.createChild();
37748                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37749                 //}
37750                 
37751                 this.add(region, ret);
37752                 break;
37753             
37754             /*
37755             case 'TreePanel': // our new panel!
37756                 cfg.el = this.el.createChild();
37757                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37758                 this.add(region, ret);
37759                 break;
37760             */
37761             
37762             case 'Nest': 
37763                 // create a new Layout (which is  a Border Layout...
37764                 
37765                 var clayout = cfg.layout;
37766                 clayout.el  = this.el.createChild();
37767                 clayout.items   = clayout.items  || [];
37768                 
37769                 delete cfg.layout;
37770                 
37771                 // replace this exitems with the clayout ones..
37772                 xitems = clayout.items;
37773                  
37774                 // force background off if it's in center...
37775                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37776                     cfg.background = false;
37777                 }
37778                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37779                 
37780                 
37781                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37782                 //console.log('adding nested layout panel '  + cfg.toSource());
37783                 this.add(region, ret);
37784                 nb = {}; /// find first...
37785                 break;
37786             
37787             case 'Grid':
37788                 
37789                 // needs grid and region
37790                 
37791                 //var el = this.getRegion(region).el.createChild();
37792                 /*
37793                  *var el = this.el.createChild();
37794                 // create the grid first...
37795                 cfg.grid.container = el;
37796                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37797                 */
37798                 
37799                 if (region == 'center' && this.active ) {
37800                     cfg.background = false;
37801                 }
37802                 
37803                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37804                 
37805                 this.add(region, ret);
37806                 /*
37807                 if (cfg.background) {
37808                     // render grid on panel activation (if panel background)
37809                     ret.on('activate', function(gp) {
37810                         if (!gp.grid.rendered) {
37811                     //        gp.grid.render(el);
37812                         }
37813                     });
37814                 } else {
37815                   //  cfg.grid.render(el);
37816                 }
37817                 */
37818                 break;
37819            
37820            
37821             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37822                 // it was the old xcomponent building that caused this before.
37823                 // espeically if border is the top element in the tree.
37824                 ret = this;
37825                 break; 
37826                 
37827                     
37828                 
37829                 
37830                 
37831             default:
37832                 /*
37833                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37834                     
37835                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37836                     this.add(region, ret);
37837                 } else {
37838                 */
37839                     Roo.log(cfg);
37840                     throw "Can not add '" + cfg.xtype + "' to Border";
37841                     return null;
37842              
37843                                 
37844              
37845         }
37846         this.beginUpdate();
37847         // add children..
37848         var region = '';
37849         var abn = {};
37850         Roo.each(xitems, function(i)  {
37851             region = nb && i.region ? i.region : false;
37852             
37853             var add = ret.addxtype(i);
37854            
37855             if (region) {
37856                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37857                 if (!i.background) {
37858                     abn[region] = nb[region] ;
37859                 }
37860             }
37861             
37862         });
37863         this.endUpdate();
37864
37865         // make the last non-background panel active..
37866         //if (nb) { Roo.log(abn); }
37867         if (nb) {
37868             
37869             for(var r in abn) {
37870                 region = this.getRegion(r);
37871                 if (region) {
37872                     // tried using nb[r], but it does not work..
37873                      
37874                     region.showPanel(abn[r]);
37875                    
37876                 }
37877             }
37878         }
37879         return ret;
37880         
37881     },
37882     
37883     
37884 // private
37885     factory : function(cfg)
37886     {
37887         
37888         var validRegions = Roo.bootstrap.layout.Border.regions;
37889
37890         var target = cfg.region;
37891         cfg.mgr = this;
37892         
37893         var r = Roo.bootstrap.layout;
37894         Roo.log(target);
37895         switch(target){
37896             case "north":
37897                 return new r.North(cfg);
37898             case "south":
37899                 return new r.South(cfg);
37900             case "east":
37901                 return new r.East(cfg);
37902             case "west":
37903                 return new r.West(cfg);
37904             case "center":
37905                 return new r.Center(cfg);
37906         }
37907         throw 'Layout region "'+target+'" not supported.';
37908     }
37909     
37910     
37911 });
37912  /*
37913  * Based on:
37914  * Ext JS Library 1.1.1
37915  * Copyright(c) 2006-2007, Ext JS, LLC.
37916  *
37917  * Originally Released Under LGPL - original licence link has changed is not relivant.
37918  *
37919  * Fork - LGPL
37920  * <script type="text/javascript">
37921  */
37922  
37923 /**
37924  * @class Roo.bootstrap.layout.Basic
37925  * @extends Roo.util.Observable
37926  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37927  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37928  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37929  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37930  * @cfg {string}   region  the region that it inhabits..
37931  * @cfg {bool}   skipConfig skip config?
37932  * 
37933
37934  */
37935 Roo.bootstrap.layout.Basic = function(config){
37936     
37937     this.mgr = config.mgr;
37938     
37939     this.position = config.region;
37940     
37941     var skipConfig = config.skipConfig;
37942     
37943     this.events = {
37944         /**
37945          * @scope Roo.BasicLayoutRegion
37946          */
37947         
37948         /**
37949          * @event beforeremove
37950          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37951          * @param {Roo.LayoutRegion} this
37952          * @param {Roo.ContentPanel} panel The panel
37953          * @param {Object} e The cancel event object
37954          */
37955         "beforeremove" : true,
37956         /**
37957          * @event invalidated
37958          * Fires when the layout for this region is changed.
37959          * @param {Roo.LayoutRegion} this
37960          */
37961         "invalidated" : true,
37962         /**
37963          * @event visibilitychange
37964          * Fires when this region is shown or hidden 
37965          * @param {Roo.LayoutRegion} this
37966          * @param {Boolean} visibility true or false
37967          */
37968         "visibilitychange" : true,
37969         /**
37970          * @event paneladded
37971          * Fires when a panel is added. 
37972          * @param {Roo.LayoutRegion} this
37973          * @param {Roo.ContentPanel} panel The panel
37974          */
37975         "paneladded" : true,
37976         /**
37977          * @event panelremoved
37978          * Fires when a panel is removed. 
37979          * @param {Roo.LayoutRegion} this
37980          * @param {Roo.ContentPanel} panel The panel
37981          */
37982         "panelremoved" : true,
37983         /**
37984          * @event beforecollapse
37985          * Fires when this region before collapse.
37986          * @param {Roo.LayoutRegion} this
37987          */
37988         "beforecollapse" : true,
37989         /**
37990          * @event collapsed
37991          * Fires when this region is collapsed.
37992          * @param {Roo.LayoutRegion} this
37993          */
37994         "collapsed" : true,
37995         /**
37996          * @event expanded
37997          * Fires when this region is expanded.
37998          * @param {Roo.LayoutRegion} this
37999          */
38000         "expanded" : true,
38001         /**
38002          * @event slideshow
38003          * Fires when this region is slid into view.
38004          * @param {Roo.LayoutRegion} this
38005          */
38006         "slideshow" : true,
38007         /**
38008          * @event slidehide
38009          * Fires when this region slides out of view. 
38010          * @param {Roo.LayoutRegion} this
38011          */
38012         "slidehide" : true,
38013         /**
38014          * @event panelactivated
38015          * Fires when a panel is activated. 
38016          * @param {Roo.LayoutRegion} this
38017          * @param {Roo.ContentPanel} panel The activated panel
38018          */
38019         "panelactivated" : true,
38020         /**
38021          * @event resized
38022          * Fires when the user resizes this region. 
38023          * @param {Roo.LayoutRegion} this
38024          * @param {Number} newSize The new size (width for east/west, height for north/south)
38025          */
38026         "resized" : true
38027     };
38028     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38029     this.panels = new Roo.util.MixedCollection();
38030     this.panels.getKey = this.getPanelId.createDelegate(this);
38031     this.box = null;
38032     this.activePanel = null;
38033     // ensure listeners are added...
38034     
38035     if (config.listeners || config.events) {
38036         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38037             listeners : config.listeners || {},
38038             events : config.events || {}
38039         });
38040     }
38041     
38042     if(skipConfig !== true){
38043         this.applyConfig(config);
38044     }
38045 };
38046
38047 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38048 {
38049     getPanelId : function(p){
38050         return p.getId();
38051     },
38052     
38053     applyConfig : function(config){
38054         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38055         this.config = config;
38056         
38057     },
38058     
38059     /**
38060      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38061      * the width, for horizontal (north, south) the height.
38062      * @param {Number} newSize The new width or height
38063      */
38064     resizeTo : function(newSize){
38065         var el = this.el ? this.el :
38066                  (this.activePanel ? this.activePanel.getEl() : null);
38067         if(el){
38068             switch(this.position){
38069                 case "east":
38070                 case "west":
38071                     el.setWidth(newSize);
38072                     this.fireEvent("resized", this, newSize);
38073                 break;
38074                 case "north":
38075                 case "south":
38076                     el.setHeight(newSize);
38077                     this.fireEvent("resized", this, newSize);
38078                 break;                
38079             }
38080         }
38081     },
38082     
38083     getBox : function(){
38084         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38085     },
38086     
38087     getMargins : function(){
38088         return this.margins;
38089     },
38090     
38091     updateBox : function(box){
38092         this.box = box;
38093         var el = this.activePanel.getEl();
38094         el.dom.style.left = box.x + "px";
38095         el.dom.style.top = box.y + "px";
38096         this.activePanel.setSize(box.width, box.height);
38097     },
38098     
38099     /**
38100      * Returns the container element for this region.
38101      * @return {Roo.Element}
38102      */
38103     getEl : function(){
38104         return this.activePanel;
38105     },
38106     
38107     /**
38108      * Returns true if this region is currently visible.
38109      * @return {Boolean}
38110      */
38111     isVisible : function(){
38112         return this.activePanel ? true : false;
38113     },
38114     
38115     setActivePanel : function(panel){
38116         panel = this.getPanel(panel);
38117         if(this.activePanel && this.activePanel != panel){
38118             this.activePanel.setActiveState(false);
38119             this.activePanel.getEl().setLeftTop(-10000,-10000);
38120         }
38121         this.activePanel = panel;
38122         panel.setActiveState(true);
38123         if(this.box){
38124             panel.setSize(this.box.width, this.box.height);
38125         }
38126         this.fireEvent("panelactivated", this, panel);
38127         this.fireEvent("invalidated");
38128     },
38129     
38130     /**
38131      * Show the specified panel.
38132      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38133      * @return {Roo.ContentPanel} The shown panel or null
38134      */
38135     showPanel : function(panel){
38136         panel = this.getPanel(panel);
38137         if(panel){
38138             this.setActivePanel(panel);
38139         }
38140         return panel;
38141     },
38142     
38143     /**
38144      * Get the active panel for this region.
38145      * @return {Roo.ContentPanel} The active panel or null
38146      */
38147     getActivePanel : function(){
38148         return this.activePanel;
38149     },
38150     
38151     /**
38152      * Add the passed ContentPanel(s)
38153      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38154      * @return {Roo.ContentPanel} The panel added (if only one was added)
38155      */
38156     add : function(panel){
38157         if(arguments.length > 1){
38158             for(var i = 0, len = arguments.length; i < len; i++) {
38159                 this.add(arguments[i]);
38160             }
38161             return null;
38162         }
38163         if(this.hasPanel(panel)){
38164             this.showPanel(panel);
38165             return panel;
38166         }
38167         var el = panel.getEl();
38168         if(el.dom.parentNode != this.mgr.el.dom){
38169             this.mgr.el.dom.appendChild(el.dom);
38170         }
38171         if(panel.setRegion){
38172             panel.setRegion(this);
38173         }
38174         this.panels.add(panel);
38175         el.setStyle("position", "absolute");
38176         if(!panel.background){
38177             this.setActivePanel(panel);
38178             if(this.config.initialSize && this.panels.getCount()==1){
38179                 this.resizeTo(this.config.initialSize);
38180             }
38181         }
38182         this.fireEvent("paneladded", this, panel);
38183         return panel;
38184     },
38185     
38186     /**
38187      * Returns true if the panel is in this region.
38188      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38189      * @return {Boolean}
38190      */
38191     hasPanel : function(panel){
38192         if(typeof panel == "object"){ // must be panel obj
38193             panel = panel.getId();
38194         }
38195         return this.getPanel(panel) ? true : false;
38196     },
38197     
38198     /**
38199      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38200      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38201      * @param {Boolean} preservePanel Overrides the config preservePanel option
38202      * @return {Roo.ContentPanel} The panel that was removed
38203      */
38204     remove : function(panel, preservePanel){
38205         panel = this.getPanel(panel);
38206         if(!panel){
38207             return null;
38208         }
38209         var e = {};
38210         this.fireEvent("beforeremove", this, panel, e);
38211         if(e.cancel === true){
38212             return null;
38213         }
38214         var panelId = panel.getId();
38215         this.panels.removeKey(panelId);
38216         return panel;
38217     },
38218     
38219     /**
38220      * Returns the panel specified or null if it's not in this region.
38221      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38222      * @return {Roo.ContentPanel}
38223      */
38224     getPanel : function(id){
38225         if(typeof id == "object"){ // must be panel obj
38226             return id;
38227         }
38228         return this.panels.get(id);
38229     },
38230     
38231     /**
38232      * Returns this regions position (north/south/east/west/center).
38233      * @return {String} 
38234      */
38235     getPosition: function(){
38236         return this.position;    
38237     }
38238 });/*
38239  * Based on:
38240  * Ext JS Library 1.1.1
38241  * Copyright(c) 2006-2007, Ext JS, LLC.
38242  *
38243  * Originally Released Under LGPL - original licence link has changed is not relivant.
38244  *
38245  * Fork - LGPL
38246  * <script type="text/javascript">
38247  */
38248  
38249 /**
38250  * @class Roo.bootstrap.layout.Region
38251  * @extends Roo.bootstrap.layout.Basic
38252  * This class represents a region in a layout manager.
38253  
38254  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38255  * @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})
38256  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38257  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38258  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38259  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38260  * @cfg {String}    title           The title for the region (overrides panel titles)
38261  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38262  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38263  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38264  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38265  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38266  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38267  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38268  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38269  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38270  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38271
38272  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38273  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38274  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38275  * @cfg {Number}    width           For East/West panels
38276  * @cfg {Number}    height          For North/South panels
38277  * @cfg {Boolean}   split           To show the splitter
38278  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38279  * 
38280  * @cfg {string}   cls             Extra CSS classes to add to region
38281  * 
38282  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38283  * @cfg {string}   region  the region that it inhabits..
38284  *
38285
38286  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38287  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38288
38289  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38290  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38291  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38292  */
38293 Roo.bootstrap.layout.Region = function(config)
38294 {
38295     this.applyConfig(config);
38296
38297     var mgr = config.mgr;
38298     var pos = config.region;
38299     config.skipConfig = true;
38300     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38301     
38302     if (mgr.el) {
38303         this.onRender(mgr.el);   
38304     }
38305      
38306     this.visible = true;
38307     this.collapsed = false;
38308     this.unrendered_panels = [];
38309 };
38310
38311 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38312
38313     position: '', // set by wrapper (eg. north/south etc..)
38314     unrendered_panels : null,  // unrendered panels.
38315     
38316     tabPosition : false,
38317     
38318     mgr: false, // points to 'Border'
38319     
38320     
38321     createBody : function(){
38322         /** This region's body element 
38323         * @type Roo.Element */
38324         this.bodyEl = this.el.createChild({
38325                 tag: "div",
38326                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38327         });
38328     },
38329
38330     onRender: function(ctr, pos)
38331     {
38332         var dh = Roo.DomHelper;
38333         /** This region's container element 
38334         * @type Roo.Element */
38335         this.el = dh.append(ctr.dom, {
38336                 tag: "div",
38337                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38338             }, true);
38339         /** This region's title element 
38340         * @type Roo.Element */
38341     
38342         this.titleEl = dh.append(this.el.dom,  {
38343                 tag: "div",
38344                 unselectable: "on",
38345                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38346                 children:[
38347                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38348                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38349                 ]
38350             }, true);
38351         
38352         this.titleEl.enableDisplayMode();
38353         /** This region's title text element 
38354         * @type HTMLElement */
38355         this.titleTextEl = this.titleEl.dom.firstChild;
38356         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38357         /*
38358         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38359         this.closeBtn.enableDisplayMode();
38360         this.closeBtn.on("click", this.closeClicked, this);
38361         this.closeBtn.hide();
38362     */
38363         this.createBody(this.config);
38364         if(this.config.hideWhenEmpty){
38365             this.hide();
38366             this.on("paneladded", this.validateVisibility, this);
38367             this.on("panelremoved", this.validateVisibility, this);
38368         }
38369         if(this.autoScroll){
38370             this.bodyEl.setStyle("overflow", "auto");
38371         }else{
38372             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38373         }
38374         //if(c.titlebar !== false){
38375             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38376                 this.titleEl.hide();
38377             }else{
38378                 this.titleEl.show();
38379                 if(this.config.title){
38380                     this.titleTextEl.innerHTML = this.config.title;
38381                 }
38382             }
38383         //}
38384         if(this.config.collapsed){
38385             this.collapse(true);
38386         }
38387         if(this.config.hidden){
38388             this.hide();
38389         }
38390         
38391         if (this.unrendered_panels && this.unrendered_panels.length) {
38392             for (var i =0;i< this.unrendered_panels.length; i++) {
38393                 this.add(this.unrendered_panels[i]);
38394             }
38395             this.unrendered_panels = null;
38396             
38397         }
38398         
38399     },
38400     
38401     applyConfig : function(c)
38402     {
38403         /*
38404          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38405             var dh = Roo.DomHelper;
38406             if(c.titlebar !== false){
38407                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38408                 this.collapseBtn.on("click", this.collapse, this);
38409                 this.collapseBtn.enableDisplayMode();
38410                 /*
38411                 if(c.showPin === true || this.showPin){
38412                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38413                     this.stickBtn.enableDisplayMode();
38414                     this.stickBtn.on("click", this.expand, this);
38415                     this.stickBtn.hide();
38416                 }
38417                 
38418             }
38419             */
38420             /** This region's collapsed element
38421             * @type Roo.Element */
38422             /*
38423              *
38424             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38425                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38426             ]}, true);
38427             
38428             if(c.floatable !== false){
38429                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38430                this.collapsedEl.on("click", this.collapseClick, this);
38431             }
38432
38433             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38434                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38435                    id: "message", unselectable: "on", style:{"float":"left"}});
38436                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38437              }
38438             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38439             this.expandBtn.on("click", this.expand, this);
38440             
38441         }
38442         
38443         if(this.collapseBtn){
38444             this.collapseBtn.setVisible(c.collapsible == true);
38445         }
38446         
38447         this.cmargins = c.cmargins || this.cmargins ||
38448                          (this.position == "west" || this.position == "east" ?
38449                              {top: 0, left: 2, right:2, bottom: 0} :
38450                              {top: 2, left: 0, right:0, bottom: 2});
38451         */
38452         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38453         
38454         
38455         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38456         
38457         this.autoScroll = c.autoScroll || false;
38458         
38459         
38460        
38461         
38462         this.duration = c.duration || .30;
38463         this.slideDuration = c.slideDuration || .45;
38464         this.config = c;
38465        
38466     },
38467     /**
38468      * Returns true if this region is currently visible.
38469      * @return {Boolean}
38470      */
38471     isVisible : function(){
38472         return this.visible;
38473     },
38474
38475     /**
38476      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38477      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38478      */
38479     //setCollapsedTitle : function(title){
38480     //    title = title || "&#160;";
38481      //   if(this.collapsedTitleTextEl){
38482       //      this.collapsedTitleTextEl.innerHTML = title;
38483        // }
38484     //},
38485
38486     getBox : function(){
38487         var b;
38488       //  if(!this.collapsed){
38489             b = this.el.getBox(false, true);
38490        // }else{
38491           //  b = this.collapsedEl.getBox(false, true);
38492         //}
38493         return b;
38494     },
38495
38496     getMargins : function(){
38497         return this.margins;
38498         //return this.collapsed ? this.cmargins : this.margins;
38499     },
38500 /*
38501     highlight : function(){
38502         this.el.addClass("x-layout-panel-dragover");
38503     },
38504
38505     unhighlight : function(){
38506         this.el.removeClass("x-layout-panel-dragover");
38507     },
38508 */
38509     updateBox : function(box)
38510     {
38511         if (!this.bodyEl) {
38512             return; // not rendered yet..
38513         }
38514         
38515         this.box = box;
38516         if(!this.collapsed){
38517             this.el.dom.style.left = box.x + "px";
38518             this.el.dom.style.top = box.y + "px";
38519             this.updateBody(box.width, box.height);
38520         }else{
38521             this.collapsedEl.dom.style.left = box.x + "px";
38522             this.collapsedEl.dom.style.top = box.y + "px";
38523             this.collapsedEl.setSize(box.width, box.height);
38524         }
38525         if(this.tabs){
38526             this.tabs.autoSizeTabs();
38527         }
38528     },
38529
38530     updateBody : function(w, h)
38531     {
38532         if(w !== null){
38533             this.el.setWidth(w);
38534             w -= this.el.getBorderWidth("rl");
38535             if(this.config.adjustments){
38536                 w += this.config.adjustments[0];
38537             }
38538         }
38539         if(h !== null && h > 0){
38540             this.el.setHeight(h);
38541             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38542             h -= this.el.getBorderWidth("tb");
38543             if(this.config.adjustments){
38544                 h += this.config.adjustments[1];
38545             }
38546             this.bodyEl.setHeight(h);
38547             if(this.tabs){
38548                 h = this.tabs.syncHeight(h);
38549             }
38550         }
38551         if(this.panelSize){
38552             w = w !== null ? w : this.panelSize.width;
38553             h = h !== null ? h : this.panelSize.height;
38554         }
38555         if(this.activePanel){
38556             var el = this.activePanel.getEl();
38557             w = w !== null ? w : el.getWidth();
38558             h = h !== null ? h : el.getHeight();
38559             this.panelSize = {width: w, height: h};
38560             this.activePanel.setSize(w, h);
38561         }
38562         if(Roo.isIE && this.tabs){
38563             this.tabs.el.repaint();
38564         }
38565     },
38566
38567     /**
38568      * Returns the container element for this region.
38569      * @return {Roo.Element}
38570      */
38571     getEl : function(){
38572         return this.el;
38573     },
38574
38575     /**
38576      * Hides this region.
38577      */
38578     hide : function(){
38579         //if(!this.collapsed){
38580             this.el.dom.style.left = "-2000px";
38581             this.el.hide();
38582         //}else{
38583          //   this.collapsedEl.dom.style.left = "-2000px";
38584          //   this.collapsedEl.hide();
38585        // }
38586         this.visible = false;
38587         this.fireEvent("visibilitychange", this, false);
38588     },
38589
38590     /**
38591      * Shows this region if it was previously hidden.
38592      */
38593     show : function(){
38594         //if(!this.collapsed){
38595             this.el.show();
38596         //}else{
38597         //    this.collapsedEl.show();
38598        // }
38599         this.visible = true;
38600         this.fireEvent("visibilitychange", this, true);
38601     },
38602 /*
38603     closeClicked : function(){
38604         if(this.activePanel){
38605             this.remove(this.activePanel);
38606         }
38607     },
38608
38609     collapseClick : function(e){
38610         if(this.isSlid){
38611            e.stopPropagation();
38612            this.slideIn();
38613         }else{
38614            e.stopPropagation();
38615            this.slideOut();
38616         }
38617     },
38618 */
38619     /**
38620      * Collapses this region.
38621      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38622      */
38623     /*
38624     collapse : function(skipAnim, skipCheck = false){
38625         if(this.collapsed) {
38626             return;
38627         }
38628         
38629         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38630             
38631             this.collapsed = true;
38632             if(this.split){
38633                 this.split.el.hide();
38634             }
38635             if(this.config.animate && skipAnim !== true){
38636                 this.fireEvent("invalidated", this);
38637                 this.animateCollapse();
38638             }else{
38639                 this.el.setLocation(-20000,-20000);
38640                 this.el.hide();
38641                 this.collapsedEl.show();
38642                 this.fireEvent("collapsed", this);
38643                 this.fireEvent("invalidated", this);
38644             }
38645         }
38646         
38647     },
38648 */
38649     animateCollapse : function(){
38650         // overridden
38651     },
38652
38653     /**
38654      * Expands this region if it was previously collapsed.
38655      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38656      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38657      */
38658     /*
38659     expand : function(e, skipAnim){
38660         if(e) {
38661             e.stopPropagation();
38662         }
38663         if(!this.collapsed || this.el.hasActiveFx()) {
38664             return;
38665         }
38666         if(this.isSlid){
38667             this.afterSlideIn();
38668             skipAnim = true;
38669         }
38670         this.collapsed = false;
38671         if(this.config.animate && skipAnim !== true){
38672             this.animateExpand();
38673         }else{
38674             this.el.show();
38675             if(this.split){
38676                 this.split.el.show();
38677             }
38678             this.collapsedEl.setLocation(-2000,-2000);
38679             this.collapsedEl.hide();
38680             this.fireEvent("invalidated", this);
38681             this.fireEvent("expanded", this);
38682         }
38683     },
38684 */
38685     animateExpand : function(){
38686         // overridden
38687     },
38688
38689     initTabs : function()
38690     {
38691         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38692         
38693         var ts = new Roo.bootstrap.panel.Tabs({
38694             el: this.bodyEl.dom,
38695             region : this,
38696             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38697             disableTooltips: this.config.disableTabTips,
38698             toolbar : this.config.toolbar
38699         });
38700         
38701         if(this.config.hideTabs){
38702             ts.stripWrap.setDisplayed(false);
38703         }
38704         this.tabs = ts;
38705         ts.resizeTabs = this.config.resizeTabs === true;
38706         ts.minTabWidth = this.config.minTabWidth || 40;
38707         ts.maxTabWidth = this.config.maxTabWidth || 250;
38708         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38709         ts.monitorResize = false;
38710         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38711         ts.bodyEl.addClass('roo-layout-tabs-body');
38712         this.panels.each(this.initPanelAsTab, this);
38713     },
38714
38715     initPanelAsTab : function(panel){
38716         var ti = this.tabs.addTab(
38717             panel.getEl().id,
38718             panel.getTitle(),
38719             null,
38720             this.config.closeOnTab && panel.isClosable(),
38721             panel.tpl
38722         );
38723         if(panel.tabTip !== undefined){
38724             ti.setTooltip(panel.tabTip);
38725         }
38726         ti.on("activate", function(){
38727               this.setActivePanel(panel);
38728         }, this);
38729         
38730         if(this.config.closeOnTab){
38731             ti.on("beforeclose", function(t, e){
38732                 e.cancel = true;
38733                 this.remove(panel);
38734             }, this);
38735         }
38736         
38737         panel.tabItem = ti;
38738         
38739         return ti;
38740     },
38741
38742     updatePanelTitle : function(panel, title)
38743     {
38744         if(this.activePanel == panel){
38745             this.updateTitle(title);
38746         }
38747         if(this.tabs){
38748             var ti = this.tabs.getTab(panel.getEl().id);
38749             ti.setText(title);
38750             if(panel.tabTip !== undefined){
38751                 ti.setTooltip(panel.tabTip);
38752             }
38753         }
38754     },
38755
38756     updateTitle : function(title){
38757         if(this.titleTextEl && !this.config.title){
38758             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38759         }
38760     },
38761
38762     setActivePanel : function(panel)
38763     {
38764         panel = this.getPanel(panel);
38765         if(this.activePanel && this.activePanel != panel){
38766             if(this.activePanel.setActiveState(false) === false){
38767                 return;
38768             }
38769         }
38770         this.activePanel = panel;
38771         panel.setActiveState(true);
38772         if(this.panelSize){
38773             panel.setSize(this.panelSize.width, this.panelSize.height);
38774         }
38775         if(this.closeBtn){
38776             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38777         }
38778         this.updateTitle(panel.getTitle());
38779         if(this.tabs){
38780             this.fireEvent("invalidated", this);
38781         }
38782         this.fireEvent("panelactivated", this, panel);
38783     },
38784
38785     /**
38786      * Shows the specified panel.
38787      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38788      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38789      */
38790     showPanel : function(panel)
38791     {
38792         panel = this.getPanel(panel);
38793         if(panel){
38794             if(this.tabs){
38795                 var tab = this.tabs.getTab(panel.getEl().id);
38796                 if(tab.isHidden()){
38797                     this.tabs.unhideTab(tab.id);
38798                 }
38799                 tab.activate();
38800             }else{
38801                 this.setActivePanel(panel);
38802             }
38803         }
38804         return panel;
38805     },
38806
38807     /**
38808      * Get the active panel for this region.
38809      * @return {Roo.ContentPanel} The active panel or null
38810      */
38811     getActivePanel : function(){
38812         return this.activePanel;
38813     },
38814
38815     validateVisibility : function(){
38816         if(this.panels.getCount() < 1){
38817             this.updateTitle("&#160;");
38818             this.closeBtn.hide();
38819             this.hide();
38820         }else{
38821             if(!this.isVisible()){
38822                 this.show();
38823             }
38824         }
38825     },
38826
38827     /**
38828      * Adds the passed ContentPanel(s) to this region.
38829      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38830      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38831      */
38832     add : function(panel)
38833     {
38834         if(arguments.length > 1){
38835             for(var i = 0, len = arguments.length; i < len; i++) {
38836                 this.add(arguments[i]);
38837             }
38838             return null;
38839         }
38840         
38841         // if we have not been rendered yet, then we can not really do much of this..
38842         if (!this.bodyEl) {
38843             this.unrendered_panels.push(panel);
38844             return panel;
38845         }
38846         
38847         
38848         
38849         
38850         if(this.hasPanel(panel)){
38851             this.showPanel(panel);
38852             return panel;
38853         }
38854         panel.setRegion(this);
38855         this.panels.add(panel);
38856        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38857             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38858             // and hide them... ???
38859             this.bodyEl.dom.appendChild(panel.getEl().dom);
38860             if(panel.background !== true){
38861                 this.setActivePanel(panel);
38862             }
38863             this.fireEvent("paneladded", this, panel);
38864             return panel;
38865         }
38866         */
38867         if(!this.tabs){
38868             this.initTabs();
38869         }else{
38870             this.initPanelAsTab(panel);
38871         }
38872         
38873         
38874         if(panel.background !== true){
38875             this.tabs.activate(panel.getEl().id);
38876         }
38877         this.fireEvent("paneladded", this, panel);
38878         return panel;
38879     },
38880
38881     /**
38882      * Hides the tab for the specified panel.
38883      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38884      */
38885     hidePanel : function(panel){
38886         if(this.tabs && (panel = this.getPanel(panel))){
38887             this.tabs.hideTab(panel.getEl().id);
38888         }
38889     },
38890
38891     /**
38892      * Unhides the tab for a previously hidden panel.
38893      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38894      */
38895     unhidePanel : function(panel){
38896         if(this.tabs && (panel = this.getPanel(panel))){
38897             this.tabs.unhideTab(panel.getEl().id);
38898         }
38899     },
38900
38901     clearPanels : function(){
38902         while(this.panels.getCount() > 0){
38903              this.remove(this.panels.first());
38904         }
38905     },
38906
38907     /**
38908      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38909      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38910      * @param {Boolean} preservePanel Overrides the config preservePanel option
38911      * @return {Roo.ContentPanel} The panel that was removed
38912      */
38913     remove : function(panel, preservePanel)
38914     {
38915         panel = this.getPanel(panel);
38916         if(!panel){
38917             return null;
38918         }
38919         var e = {};
38920         this.fireEvent("beforeremove", this, panel, e);
38921         if(e.cancel === true){
38922             return null;
38923         }
38924         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38925         var panelId = panel.getId();
38926         this.panels.removeKey(panelId);
38927         if(preservePanel){
38928             document.body.appendChild(panel.getEl().dom);
38929         }
38930         if(this.tabs){
38931             this.tabs.removeTab(panel.getEl().id);
38932         }else if (!preservePanel){
38933             this.bodyEl.dom.removeChild(panel.getEl().dom);
38934         }
38935         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38936             var p = this.panels.first();
38937             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38938             tempEl.appendChild(p.getEl().dom);
38939             this.bodyEl.update("");
38940             this.bodyEl.dom.appendChild(p.getEl().dom);
38941             tempEl = null;
38942             this.updateTitle(p.getTitle());
38943             this.tabs = null;
38944             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38945             this.setActivePanel(p);
38946         }
38947         panel.setRegion(null);
38948         if(this.activePanel == panel){
38949             this.activePanel = null;
38950         }
38951         if(this.config.autoDestroy !== false && preservePanel !== true){
38952             try{panel.destroy();}catch(e){}
38953         }
38954         this.fireEvent("panelremoved", this, panel);
38955         return panel;
38956     },
38957
38958     /**
38959      * Returns the TabPanel component used by this region
38960      * @return {Roo.TabPanel}
38961      */
38962     getTabs : function(){
38963         return this.tabs;
38964     },
38965
38966     createTool : function(parentEl, className){
38967         var btn = Roo.DomHelper.append(parentEl, {
38968             tag: "div",
38969             cls: "x-layout-tools-button",
38970             children: [ {
38971                 tag: "div",
38972                 cls: "roo-layout-tools-button-inner " + className,
38973                 html: "&#160;"
38974             }]
38975         }, true);
38976         btn.addClassOnOver("roo-layout-tools-button-over");
38977         return btn;
38978     }
38979 });/*
38980  * Based on:
38981  * Ext JS Library 1.1.1
38982  * Copyright(c) 2006-2007, Ext JS, LLC.
38983  *
38984  * Originally Released Under LGPL - original licence link has changed is not relivant.
38985  *
38986  * Fork - LGPL
38987  * <script type="text/javascript">
38988  */
38989  
38990
38991
38992 /**
38993  * @class Roo.SplitLayoutRegion
38994  * @extends Roo.LayoutRegion
38995  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38996  */
38997 Roo.bootstrap.layout.Split = function(config){
38998     this.cursor = config.cursor;
38999     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39000 };
39001
39002 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39003 {
39004     splitTip : "Drag to resize.",
39005     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39006     useSplitTips : false,
39007
39008     applyConfig : function(config){
39009         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39010     },
39011     
39012     onRender : function(ctr,pos) {
39013         
39014         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39015         if(!this.config.split){
39016             return;
39017         }
39018         if(!this.split){
39019             
39020             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39021                             tag: "div",
39022                             id: this.el.id + "-split",
39023                             cls: "roo-layout-split roo-layout-split-"+this.position,
39024                             html: "&#160;"
39025             });
39026             /** The SplitBar for this region 
39027             * @type Roo.SplitBar */
39028             // does not exist yet...
39029             Roo.log([this.position, this.orientation]);
39030             
39031             this.split = new Roo.bootstrap.SplitBar({
39032                 dragElement : splitEl,
39033                 resizingElement: this.el,
39034                 orientation : this.orientation
39035             });
39036             
39037             this.split.on("moved", this.onSplitMove, this);
39038             this.split.useShim = this.config.useShim === true;
39039             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39040             if(this.useSplitTips){
39041                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39042             }
39043             //if(config.collapsible){
39044             //    this.split.el.on("dblclick", this.collapse,  this);
39045             //}
39046         }
39047         if(typeof this.config.minSize != "undefined"){
39048             this.split.minSize = this.config.minSize;
39049         }
39050         if(typeof this.config.maxSize != "undefined"){
39051             this.split.maxSize = this.config.maxSize;
39052         }
39053         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39054             this.hideSplitter();
39055         }
39056         
39057     },
39058
39059     getHMaxSize : function(){
39060          var cmax = this.config.maxSize || 10000;
39061          var center = this.mgr.getRegion("center");
39062          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39063     },
39064
39065     getVMaxSize : function(){
39066          var cmax = this.config.maxSize || 10000;
39067          var center = this.mgr.getRegion("center");
39068          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39069     },
39070
39071     onSplitMove : function(split, newSize){
39072         this.fireEvent("resized", this, newSize);
39073     },
39074     
39075     /** 
39076      * Returns the {@link Roo.SplitBar} for this region.
39077      * @return {Roo.SplitBar}
39078      */
39079     getSplitBar : function(){
39080         return this.split;
39081     },
39082     
39083     hide : function(){
39084         this.hideSplitter();
39085         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39086     },
39087
39088     hideSplitter : function(){
39089         if(this.split){
39090             this.split.el.setLocation(-2000,-2000);
39091             this.split.el.hide();
39092         }
39093     },
39094
39095     show : function(){
39096         if(this.split){
39097             this.split.el.show();
39098         }
39099         Roo.bootstrap.layout.Split.superclass.show.call(this);
39100     },
39101     
39102     beforeSlide: function(){
39103         if(Roo.isGecko){// firefox overflow auto bug workaround
39104             this.bodyEl.clip();
39105             if(this.tabs) {
39106                 this.tabs.bodyEl.clip();
39107             }
39108             if(this.activePanel){
39109                 this.activePanel.getEl().clip();
39110                 
39111                 if(this.activePanel.beforeSlide){
39112                     this.activePanel.beforeSlide();
39113                 }
39114             }
39115         }
39116     },
39117     
39118     afterSlide : function(){
39119         if(Roo.isGecko){// firefox overflow auto bug workaround
39120             this.bodyEl.unclip();
39121             if(this.tabs) {
39122                 this.tabs.bodyEl.unclip();
39123             }
39124             if(this.activePanel){
39125                 this.activePanel.getEl().unclip();
39126                 if(this.activePanel.afterSlide){
39127                     this.activePanel.afterSlide();
39128                 }
39129             }
39130         }
39131     },
39132
39133     initAutoHide : function(){
39134         if(this.autoHide !== false){
39135             if(!this.autoHideHd){
39136                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39137                 this.autoHideHd = {
39138                     "mouseout": function(e){
39139                         if(!e.within(this.el, true)){
39140                             st.delay(500);
39141                         }
39142                     },
39143                     "mouseover" : function(e){
39144                         st.cancel();
39145                     },
39146                     scope : this
39147                 };
39148             }
39149             this.el.on(this.autoHideHd);
39150         }
39151     },
39152
39153     clearAutoHide : function(){
39154         if(this.autoHide !== false){
39155             this.el.un("mouseout", this.autoHideHd.mouseout);
39156             this.el.un("mouseover", this.autoHideHd.mouseover);
39157         }
39158     },
39159
39160     clearMonitor : function(){
39161         Roo.get(document).un("click", this.slideInIf, this);
39162     },
39163
39164     // these names are backwards but not changed for compat
39165     slideOut : function(){
39166         if(this.isSlid || this.el.hasActiveFx()){
39167             return;
39168         }
39169         this.isSlid = true;
39170         if(this.collapseBtn){
39171             this.collapseBtn.hide();
39172         }
39173         this.closeBtnState = this.closeBtn.getStyle('display');
39174         this.closeBtn.hide();
39175         if(this.stickBtn){
39176             this.stickBtn.show();
39177         }
39178         this.el.show();
39179         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39180         this.beforeSlide();
39181         this.el.setStyle("z-index", 10001);
39182         this.el.slideIn(this.getSlideAnchor(), {
39183             callback: function(){
39184                 this.afterSlide();
39185                 this.initAutoHide();
39186                 Roo.get(document).on("click", this.slideInIf, this);
39187                 this.fireEvent("slideshow", this);
39188             },
39189             scope: this,
39190             block: true
39191         });
39192     },
39193
39194     afterSlideIn : function(){
39195         this.clearAutoHide();
39196         this.isSlid = false;
39197         this.clearMonitor();
39198         this.el.setStyle("z-index", "");
39199         if(this.collapseBtn){
39200             this.collapseBtn.show();
39201         }
39202         this.closeBtn.setStyle('display', this.closeBtnState);
39203         if(this.stickBtn){
39204             this.stickBtn.hide();
39205         }
39206         this.fireEvent("slidehide", this);
39207     },
39208
39209     slideIn : function(cb){
39210         if(!this.isSlid || this.el.hasActiveFx()){
39211             Roo.callback(cb);
39212             return;
39213         }
39214         this.isSlid = false;
39215         this.beforeSlide();
39216         this.el.slideOut(this.getSlideAnchor(), {
39217             callback: function(){
39218                 this.el.setLeftTop(-10000, -10000);
39219                 this.afterSlide();
39220                 this.afterSlideIn();
39221                 Roo.callback(cb);
39222             },
39223             scope: this,
39224             block: true
39225         });
39226     },
39227     
39228     slideInIf : function(e){
39229         if(!e.within(this.el)){
39230             this.slideIn();
39231         }
39232     },
39233
39234     animateCollapse : function(){
39235         this.beforeSlide();
39236         this.el.setStyle("z-index", 20000);
39237         var anchor = this.getSlideAnchor();
39238         this.el.slideOut(anchor, {
39239             callback : function(){
39240                 this.el.setStyle("z-index", "");
39241                 this.collapsedEl.slideIn(anchor, {duration:.3});
39242                 this.afterSlide();
39243                 this.el.setLocation(-10000,-10000);
39244                 this.el.hide();
39245                 this.fireEvent("collapsed", this);
39246             },
39247             scope: this,
39248             block: true
39249         });
39250     },
39251
39252     animateExpand : function(){
39253         this.beforeSlide();
39254         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39255         this.el.setStyle("z-index", 20000);
39256         this.collapsedEl.hide({
39257             duration:.1
39258         });
39259         this.el.slideIn(this.getSlideAnchor(), {
39260             callback : function(){
39261                 this.el.setStyle("z-index", "");
39262                 this.afterSlide();
39263                 if(this.split){
39264                     this.split.el.show();
39265                 }
39266                 this.fireEvent("invalidated", this);
39267                 this.fireEvent("expanded", this);
39268             },
39269             scope: this,
39270             block: true
39271         });
39272     },
39273
39274     anchors : {
39275         "west" : "left",
39276         "east" : "right",
39277         "north" : "top",
39278         "south" : "bottom"
39279     },
39280
39281     sanchors : {
39282         "west" : "l",
39283         "east" : "r",
39284         "north" : "t",
39285         "south" : "b"
39286     },
39287
39288     canchors : {
39289         "west" : "tl-tr",
39290         "east" : "tr-tl",
39291         "north" : "tl-bl",
39292         "south" : "bl-tl"
39293     },
39294
39295     getAnchor : function(){
39296         return this.anchors[this.position];
39297     },
39298
39299     getCollapseAnchor : function(){
39300         return this.canchors[this.position];
39301     },
39302
39303     getSlideAnchor : function(){
39304         return this.sanchors[this.position];
39305     },
39306
39307     getAlignAdj : function(){
39308         var cm = this.cmargins;
39309         switch(this.position){
39310             case "west":
39311                 return [0, 0];
39312             break;
39313             case "east":
39314                 return [0, 0];
39315             break;
39316             case "north":
39317                 return [0, 0];
39318             break;
39319             case "south":
39320                 return [0, 0];
39321             break;
39322         }
39323     },
39324
39325     getExpandAdj : function(){
39326         var c = this.collapsedEl, cm = this.cmargins;
39327         switch(this.position){
39328             case "west":
39329                 return [-(cm.right+c.getWidth()+cm.left), 0];
39330             break;
39331             case "east":
39332                 return [cm.right+c.getWidth()+cm.left, 0];
39333             break;
39334             case "north":
39335                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39336             break;
39337             case "south":
39338                 return [0, cm.top+cm.bottom+c.getHeight()];
39339             break;
39340         }
39341     }
39342 });/*
39343  * Based on:
39344  * Ext JS Library 1.1.1
39345  * Copyright(c) 2006-2007, Ext JS, LLC.
39346  *
39347  * Originally Released Under LGPL - original licence link has changed is not relivant.
39348  *
39349  * Fork - LGPL
39350  * <script type="text/javascript">
39351  */
39352 /*
39353  * These classes are private internal classes
39354  */
39355 Roo.bootstrap.layout.Center = function(config){
39356     config.region = "center";
39357     Roo.bootstrap.layout.Region.call(this, config);
39358     this.visible = true;
39359     this.minWidth = config.minWidth || 20;
39360     this.minHeight = config.minHeight || 20;
39361 };
39362
39363 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39364     hide : function(){
39365         // center panel can't be hidden
39366     },
39367     
39368     show : function(){
39369         // center panel can't be hidden
39370     },
39371     
39372     getMinWidth: function(){
39373         return this.minWidth;
39374     },
39375     
39376     getMinHeight: function(){
39377         return this.minHeight;
39378     }
39379 });
39380
39381
39382
39383
39384  
39385
39386
39387
39388
39389
39390
39391 Roo.bootstrap.layout.North = function(config)
39392 {
39393     config.region = 'north';
39394     config.cursor = 'n-resize';
39395     
39396     Roo.bootstrap.layout.Split.call(this, config);
39397     
39398     
39399     if(this.split){
39400         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39401         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39402         this.split.el.addClass("roo-layout-split-v");
39403     }
39404     //var size = config.initialSize || config.height;
39405     //if(this.el && typeof size != "undefined"){
39406     //    this.el.setHeight(size);
39407     //}
39408 };
39409 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39410 {
39411     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39412      
39413      
39414     onRender : function(ctr, pos)
39415     {
39416         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39417         var size = this.config.initialSize || this.config.height;
39418         if(this.el && typeof size != "undefined"){
39419             this.el.setHeight(size);
39420         }
39421     
39422     },
39423     
39424     getBox : function(){
39425         if(this.collapsed){
39426             return this.collapsedEl.getBox();
39427         }
39428         var box = this.el.getBox();
39429         if(this.split){
39430             box.height += this.split.el.getHeight();
39431         }
39432         return box;
39433     },
39434     
39435     updateBox : function(box){
39436         if(this.split && !this.collapsed){
39437             box.height -= this.split.el.getHeight();
39438             this.split.el.setLeft(box.x);
39439             this.split.el.setTop(box.y+box.height);
39440             this.split.el.setWidth(box.width);
39441         }
39442         if(this.collapsed){
39443             this.updateBody(box.width, null);
39444         }
39445         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39446     }
39447 });
39448
39449
39450
39451
39452
39453 Roo.bootstrap.layout.South = function(config){
39454     config.region = 'south';
39455     config.cursor = 's-resize';
39456     Roo.bootstrap.layout.Split.call(this, config);
39457     if(this.split){
39458         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39459         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39460         this.split.el.addClass("roo-layout-split-v");
39461     }
39462     
39463 };
39464
39465 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39466     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39467     
39468     onRender : function(ctr, pos)
39469     {
39470         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39471         var size = this.config.initialSize || this.config.height;
39472         if(this.el && typeof size != "undefined"){
39473             this.el.setHeight(size);
39474         }
39475     
39476     },
39477     
39478     getBox : function(){
39479         if(this.collapsed){
39480             return this.collapsedEl.getBox();
39481         }
39482         var box = this.el.getBox();
39483         if(this.split){
39484             var sh = this.split.el.getHeight();
39485             box.height += sh;
39486             box.y -= sh;
39487         }
39488         return box;
39489     },
39490     
39491     updateBox : function(box){
39492         if(this.split && !this.collapsed){
39493             var sh = this.split.el.getHeight();
39494             box.height -= sh;
39495             box.y += sh;
39496             this.split.el.setLeft(box.x);
39497             this.split.el.setTop(box.y-sh);
39498             this.split.el.setWidth(box.width);
39499         }
39500         if(this.collapsed){
39501             this.updateBody(box.width, null);
39502         }
39503         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39504     }
39505 });
39506
39507 Roo.bootstrap.layout.East = function(config){
39508     config.region = "east";
39509     config.cursor = "e-resize";
39510     Roo.bootstrap.layout.Split.call(this, config);
39511     if(this.split){
39512         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39513         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39514         this.split.el.addClass("roo-layout-split-h");
39515     }
39516     
39517 };
39518 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39519     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39520     
39521     onRender : function(ctr, pos)
39522     {
39523         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39524         var size = this.config.initialSize || this.config.width;
39525         if(this.el && typeof size != "undefined"){
39526             this.el.setWidth(size);
39527         }
39528     
39529     },
39530     
39531     getBox : function(){
39532         if(this.collapsed){
39533             return this.collapsedEl.getBox();
39534         }
39535         var box = this.el.getBox();
39536         if(this.split){
39537             var sw = this.split.el.getWidth();
39538             box.width += sw;
39539             box.x -= sw;
39540         }
39541         return box;
39542     },
39543
39544     updateBox : function(box){
39545         if(this.split && !this.collapsed){
39546             var sw = this.split.el.getWidth();
39547             box.width -= sw;
39548             this.split.el.setLeft(box.x);
39549             this.split.el.setTop(box.y);
39550             this.split.el.setHeight(box.height);
39551             box.x += sw;
39552         }
39553         if(this.collapsed){
39554             this.updateBody(null, box.height);
39555         }
39556         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39557     }
39558 });
39559
39560 Roo.bootstrap.layout.West = function(config){
39561     config.region = "west";
39562     config.cursor = "w-resize";
39563     
39564     Roo.bootstrap.layout.Split.call(this, config);
39565     if(this.split){
39566         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39567         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39568         this.split.el.addClass("roo-layout-split-h");
39569     }
39570     
39571 };
39572 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39573     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39574     
39575     onRender: function(ctr, pos)
39576     {
39577         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39578         var size = this.config.initialSize || this.config.width;
39579         if(typeof size != "undefined"){
39580             this.el.setWidth(size);
39581         }
39582     },
39583     
39584     getBox : function(){
39585         if(this.collapsed){
39586             return this.collapsedEl.getBox();
39587         }
39588         var box = this.el.getBox();
39589         if (box.width == 0) {
39590             box.width = this.config.width; // kludge?
39591         }
39592         if(this.split){
39593             box.width += this.split.el.getWidth();
39594         }
39595         return box;
39596     },
39597     
39598     updateBox : function(box){
39599         if(this.split && !this.collapsed){
39600             var sw = this.split.el.getWidth();
39601             box.width -= sw;
39602             this.split.el.setLeft(box.x+box.width);
39603             this.split.el.setTop(box.y);
39604             this.split.el.setHeight(box.height);
39605         }
39606         if(this.collapsed){
39607             this.updateBody(null, box.height);
39608         }
39609         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39610     }
39611 });Roo.namespace("Roo.bootstrap.panel");/*
39612  * Based on:
39613  * Ext JS Library 1.1.1
39614  * Copyright(c) 2006-2007, Ext JS, LLC.
39615  *
39616  * Originally Released Under LGPL - original licence link has changed is not relivant.
39617  *
39618  * Fork - LGPL
39619  * <script type="text/javascript">
39620  */
39621 /**
39622  * @class Roo.ContentPanel
39623  * @extends Roo.util.Observable
39624  * A basic ContentPanel element.
39625  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39626  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39627  * @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
39628  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39629  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39630  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39631  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39632  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39633  * @cfg {String} title          The title for this panel
39634  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39635  * @cfg {String} url            Calls {@link #setUrl} with this value
39636  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39637  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39638  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39639  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39640  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39641  * @cfg {Boolean} badges render the badges
39642  * @cfg {String} cls  extra classes to use  
39643  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39644
39645  * @constructor
39646  * Create a new ContentPanel.
39647  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39648  * @param {String/Object} config A string to set only the title or a config object
39649  * @param {String} content (optional) Set the HTML content for this panel
39650  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39651  */
39652 Roo.bootstrap.panel.Content = function( config){
39653     
39654     this.tpl = config.tpl || false;
39655     
39656     var el = config.el;
39657     var content = config.content;
39658
39659     if(config.autoCreate){ // xtype is available if this is called from factory
39660         el = Roo.id();
39661     }
39662     this.el = Roo.get(el);
39663     if(!this.el && config && config.autoCreate){
39664         if(typeof config.autoCreate == "object"){
39665             if(!config.autoCreate.id){
39666                 config.autoCreate.id = config.id||el;
39667             }
39668             this.el = Roo.DomHelper.append(document.body,
39669                         config.autoCreate, true);
39670         }else{
39671             var elcfg =  {
39672                 tag: "div",
39673                 cls: (config.cls || '') +
39674                     (config.background ? ' bg-' + config.background : '') +
39675                     " roo-layout-inactive-content",
39676                 id: config.id||el
39677             };
39678             if (config.iframe) {
39679                 elcfg.cn = [
39680                     {
39681                         tag : 'iframe',
39682                         style : 'border: 0px',
39683                         src : 'about:blank'
39684                     }
39685                 ];
39686             }
39687               
39688             if (config.html) {
39689                 elcfg.html = config.html;
39690                 
39691             }
39692                         
39693             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39694             if (config.iframe) {
39695                 this.iframeEl = this.el.select('iframe',true).first();
39696             }
39697             
39698         }
39699     } 
39700     this.closable = false;
39701     this.loaded = false;
39702     this.active = false;
39703    
39704       
39705     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39706         
39707         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39708         
39709         this.wrapEl = this.el; //this.el.wrap();
39710         var ti = [];
39711         if (config.toolbar.items) {
39712             ti = config.toolbar.items ;
39713             delete config.toolbar.items ;
39714         }
39715         
39716         var nitems = [];
39717         this.toolbar.render(this.wrapEl, 'before');
39718         for(var i =0;i < ti.length;i++) {
39719           //  Roo.log(['add child', items[i]]);
39720             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39721         }
39722         this.toolbar.items = nitems;
39723         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39724         delete config.toolbar;
39725         
39726     }
39727     /*
39728     // xtype created footer. - not sure if will work as we normally have to render first..
39729     if (this.footer && !this.footer.el && this.footer.xtype) {
39730         if (!this.wrapEl) {
39731             this.wrapEl = this.el.wrap();
39732         }
39733     
39734         this.footer.container = this.wrapEl.createChild();
39735          
39736         this.footer = Roo.factory(this.footer, Roo);
39737         
39738     }
39739     */
39740     
39741      if(typeof config == "string"){
39742         this.title = config;
39743     }else{
39744         Roo.apply(this, config);
39745     }
39746     
39747     if(this.resizeEl){
39748         this.resizeEl = Roo.get(this.resizeEl, true);
39749     }else{
39750         this.resizeEl = this.el;
39751     }
39752     // handle view.xtype
39753     
39754  
39755     
39756     
39757     this.addEvents({
39758         /**
39759          * @event activate
39760          * Fires when this panel is activated. 
39761          * @param {Roo.ContentPanel} this
39762          */
39763         "activate" : true,
39764         /**
39765          * @event deactivate
39766          * Fires when this panel is activated. 
39767          * @param {Roo.ContentPanel} this
39768          */
39769         "deactivate" : true,
39770
39771         /**
39772          * @event resize
39773          * Fires when this panel is resized if fitToFrame is true.
39774          * @param {Roo.ContentPanel} this
39775          * @param {Number} width The width after any component adjustments
39776          * @param {Number} height The height after any component adjustments
39777          */
39778         "resize" : true,
39779         
39780          /**
39781          * @event render
39782          * Fires when this tab is created
39783          * @param {Roo.ContentPanel} this
39784          */
39785         "render" : true
39786         
39787         
39788         
39789     });
39790     
39791
39792     
39793     
39794     if(this.autoScroll && !this.iframe){
39795         this.resizeEl.setStyle("overflow", "auto");
39796     } else {
39797         // fix randome scrolling
39798         //this.el.on('scroll', function() {
39799         //    Roo.log('fix random scolling');
39800         //    this.scrollTo('top',0); 
39801         //});
39802     }
39803     content = content || this.content;
39804     if(content){
39805         this.setContent(content);
39806     }
39807     if(config && config.url){
39808         this.setUrl(this.url, this.params, this.loadOnce);
39809     }
39810     
39811     
39812     
39813     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39814     
39815     if (this.view && typeof(this.view.xtype) != 'undefined') {
39816         this.view.el = this.el.appendChild(document.createElement("div"));
39817         this.view = Roo.factory(this.view); 
39818         this.view.render  &&  this.view.render(false, '');  
39819     }
39820     
39821     
39822     this.fireEvent('render', this);
39823 };
39824
39825 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39826     
39827     cls : '',
39828     background : '',
39829     
39830     tabTip : '',
39831     
39832     iframe : false,
39833     iframeEl : false,
39834     
39835     setRegion : function(region){
39836         this.region = region;
39837         this.setActiveClass(region && !this.background);
39838     },
39839     
39840     
39841     setActiveClass: function(state)
39842     {
39843         if(state){
39844            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39845            this.el.setStyle('position','relative');
39846         }else{
39847            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39848            this.el.setStyle('position', 'absolute');
39849         } 
39850     },
39851     
39852     /**
39853      * Returns the toolbar for this Panel if one was configured. 
39854      * @return {Roo.Toolbar} 
39855      */
39856     getToolbar : function(){
39857         return this.toolbar;
39858     },
39859     
39860     setActiveState : function(active)
39861     {
39862         this.active = active;
39863         this.setActiveClass(active);
39864         if(!active){
39865             if(this.fireEvent("deactivate", this) === false){
39866                 return false;
39867             }
39868             return true;
39869         }
39870         this.fireEvent("activate", this);
39871         return true;
39872     },
39873     /**
39874      * Updates this panel's element (not for iframe)
39875      * @param {String} content The new content
39876      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39877     */
39878     setContent : function(content, loadScripts){
39879         if (this.iframe) {
39880             return;
39881         }
39882         
39883         this.el.update(content, loadScripts);
39884     },
39885
39886     ignoreResize : function(w, h){
39887         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39888             return true;
39889         }else{
39890             this.lastSize = {width: w, height: h};
39891             return false;
39892         }
39893     },
39894     /**
39895      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39896      * @return {Roo.UpdateManager} The UpdateManager
39897      */
39898     getUpdateManager : function(){
39899         if (this.iframe) {
39900             return false;
39901         }
39902         return this.el.getUpdateManager();
39903     },
39904      /**
39905      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39906      * Does not work with IFRAME contents
39907      * @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:
39908 <pre><code>
39909 panel.load({
39910     url: "your-url.php",
39911     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39912     callback: yourFunction,
39913     scope: yourObject, //(optional scope)
39914     discardUrl: false,
39915     nocache: false,
39916     text: "Loading...",
39917     timeout: 30,
39918     scripts: false
39919 });
39920 </code></pre>
39921      
39922      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39923      * 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.
39924      * @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}
39925      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39926      * @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.
39927      * @return {Roo.ContentPanel} this
39928      */
39929     load : function(){
39930         
39931         if (this.iframe) {
39932             return this;
39933         }
39934         
39935         var um = this.el.getUpdateManager();
39936         um.update.apply(um, arguments);
39937         return this;
39938     },
39939
39940
39941     /**
39942      * 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.
39943      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39944      * @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)
39945      * @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)
39946      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39947      */
39948     setUrl : function(url, params, loadOnce){
39949         if (this.iframe) {
39950             this.iframeEl.dom.src = url;
39951             return false;
39952         }
39953         
39954         if(this.refreshDelegate){
39955             this.removeListener("activate", this.refreshDelegate);
39956         }
39957         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39958         this.on("activate", this.refreshDelegate);
39959         return this.el.getUpdateManager();
39960     },
39961     
39962     _handleRefresh : function(url, params, loadOnce){
39963         if(!loadOnce || !this.loaded){
39964             var updater = this.el.getUpdateManager();
39965             updater.update(url, params, this._setLoaded.createDelegate(this));
39966         }
39967     },
39968     
39969     _setLoaded : function(){
39970         this.loaded = true;
39971     }, 
39972     
39973     /**
39974      * Returns this panel's id
39975      * @return {String} 
39976      */
39977     getId : function(){
39978         return this.el.id;
39979     },
39980     
39981     /** 
39982      * Returns this panel's element - used by regiosn to add.
39983      * @return {Roo.Element} 
39984      */
39985     getEl : function(){
39986         return this.wrapEl || this.el;
39987     },
39988     
39989    
39990     
39991     adjustForComponents : function(width, height)
39992     {
39993         //Roo.log('adjustForComponents ');
39994         if(this.resizeEl != this.el){
39995             width -= this.el.getFrameWidth('lr');
39996             height -= this.el.getFrameWidth('tb');
39997         }
39998         if(this.toolbar){
39999             var te = this.toolbar.getEl();
40000             te.setWidth(width);
40001             height -= te.getHeight();
40002         }
40003         if(this.footer){
40004             var te = this.footer.getEl();
40005             te.setWidth(width);
40006             height -= te.getHeight();
40007         }
40008         
40009         
40010         if(this.adjustments){
40011             width += this.adjustments[0];
40012             height += this.adjustments[1];
40013         }
40014         return {"width": width, "height": height};
40015     },
40016     
40017     setSize : function(width, height){
40018         if(this.fitToFrame && !this.ignoreResize(width, height)){
40019             if(this.fitContainer && this.resizeEl != this.el){
40020                 this.el.setSize(width, height);
40021             }
40022             var size = this.adjustForComponents(width, height);
40023             if (this.iframe) {
40024                 this.iframeEl.setSize(width,height);
40025             }
40026             
40027             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40028             this.fireEvent('resize', this, size.width, size.height);
40029             
40030             
40031         }
40032     },
40033     
40034     /**
40035      * Returns this panel's title
40036      * @return {String} 
40037      */
40038     getTitle : function(){
40039         
40040         if (typeof(this.title) != 'object') {
40041             return this.title;
40042         }
40043         
40044         var t = '';
40045         for (var k in this.title) {
40046             if (!this.title.hasOwnProperty(k)) {
40047                 continue;
40048             }
40049             
40050             if (k.indexOf('-') >= 0) {
40051                 var s = k.split('-');
40052                 for (var i = 0; i<s.length; i++) {
40053                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40054                 }
40055             } else {
40056                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40057             }
40058         }
40059         return t;
40060     },
40061     
40062     /**
40063      * Set this panel's title
40064      * @param {String} title
40065      */
40066     setTitle : function(title){
40067         this.title = title;
40068         if(this.region){
40069             this.region.updatePanelTitle(this, title);
40070         }
40071     },
40072     
40073     /**
40074      * Returns true is this panel was configured to be closable
40075      * @return {Boolean} 
40076      */
40077     isClosable : function(){
40078         return this.closable;
40079     },
40080     
40081     beforeSlide : function(){
40082         this.el.clip();
40083         this.resizeEl.clip();
40084     },
40085     
40086     afterSlide : function(){
40087         this.el.unclip();
40088         this.resizeEl.unclip();
40089     },
40090     
40091     /**
40092      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40093      *   Will fail silently if the {@link #setUrl} method has not been called.
40094      *   This does not activate the panel, just updates its content.
40095      */
40096     refresh : function(){
40097         if(this.refreshDelegate){
40098            this.loaded = false;
40099            this.refreshDelegate();
40100         }
40101     },
40102     
40103     /**
40104      * Destroys this panel
40105      */
40106     destroy : function(){
40107         this.el.removeAllListeners();
40108         var tempEl = document.createElement("span");
40109         tempEl.appendChild(this.el.dom);
40110         tempEl.innerHTML = "";
40111         this.el.remove();
40112         this.el = null;
40113     },
40114     
40115     /**
40116      * form - if the content panel contains a form - this is a reference to it.
40117      * @type {Roo.form.Form}
40118      */
40119     form : false,
40120     /**
40121      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40122      *    This contains a reference to it.
40123      * @type {Roo.View}
40124      */
40125     view : false,
40126     
40127       /**
40128      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40129      * <pre><code>
40130
40131 layout.addxtype({
40132        xtype : 'Form',
40133        items: [ .... ]
40134    }
40135 );
40136
40137 </code></pre>
40138      * @param {Object} cfg Xtype definition of item to add.
40139      */
40140     
40141     
40142     getChildContainer: function () {
40143         return this.getEl();
40144     }
40145     
40146     
40147     /*
40148         var  ret = new Roo.factory(cfg);
40149         return ret;
40150         
40151         
40152         // add form..
40153         if (cfg.xtype.match(/^Form$/)) {
40154             
40155             var el;
40156             //if (this.footer) {
40157             //    el = this.footer.container.insertSibling(false, 'before');
40158             //} else {
40159                 el = this.el.createChild();
40160             //}
40161
40162             this.form = new  Roo.form.Form(cfg);
40163             
40164             
40165             if ( this.form.allItems.length) {
40166                 this.form.render(el.dom);
40167             }
40168             return this.form;
40169         }
40170         // should only have one of theses..
40171         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40172             // views.. should not be just added - used named prop 'view''
40173             
40174             cfg.el = this.el.appendChild(document.createElement("div"));
40175             // factory?
40176             
40177             var ret = new Roo.factory(cfg);
40178              
40179              ret.render && ret.render(false, ''); // render blank..
40180             this.view = ret;
40181             return ret;
40182         }
40183         return false;
40184     }
40185     \*/
40186 });
40187  
40188 /**
40189  * @class Roo.bootstrap.panel.Grid
40190  * @extends Roo.bootstrap.panel.Content
40191  * @constructor
40192  * Create a new GridPanel.
40193  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40194  * @param {Object} config A the config object
40195   
40196  */
40197
40198
40199
40200 Roo.bootstrap.panel.Grid = function(config)
40201 {
40202     
40203       
40204     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40205         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40206
40207     config.el = this.wrapper;
40208     //this.el = this.wrapper;
40209     
40210       if (config.container) {
40211         // ctor'ed from a Border/panel.grid
40212         
40213         
40214         this.wrapper.setStyle("overflow", "hidden");
40215         this.wrapper.addClass('roo-grid-container');
40216
40217     }
40218     
40219     
40220     if(config.toolbar){
40221         var tool_el = this.wrapper.createChild();    
40222         this.toolbar = Roo.factory(config.toolbar);
40223         var ti = [];
40224         if (config.toolbar.items) {
40225             ti = config.toolbar.items ;
40226             delete config.toolbar.items ;
40227         }
40228         
40229         var nitems = [];
40230         this.toolbar.render(tool_el);
40231         for(var i =0;i < ti.length;i++) {
40232           //  Roo.log(['add child', items[i]]);
40233             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40234         }
40235         this.toolbar.items = nitems;
40236         
40237         delete config.toolbar;
40238     }
40239     
40240     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40241     config.grid.scrollBody = true;;
40242     config.grid.monitorWindowResize = false; // turn off autosizing
40243     config.grid.autoHeight = false;
40244     config.grid.autoWidth = false;
40245     
40246     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40247     
40248     if (config.background) {
40249         // render grid on panel activation (if panel background)
40250         this.on('activate', function(gp) {
40251             if (!gp.grid.rendered) {
40252                 gp.grid.render(this.wrapper);
40253                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40254             }
40255         });
40256             
40257     } else {
40258         this.grid.render(this.wrapper);
40259         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40260
40261     }
40262     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40263     // ??? needed ??? config.el = this.wrapper;
40264     
40265     
40266     
40267   
40268     // xtype created footer. - not sure if will work as we normally have to render first..
40269     if (this.footer && !this.footer.el && this.footer.xtype) {
40270         
40271         var ctr = this.grid.getView().getFooterPanel(true);
40272         this.footer.dataSource = this.grid.dataSource;
40273         this.footer = Roo.factory(this.footer, Roo);
40274         this.footer.render(ctr);
40275         
40276     }
40277     
40278     
40279     
40280     
40281      
40282 };
40283
40284 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40285     getId : function(){
40286         return this.grid.id;
40287     },
40288     
40289     /**
40290      * Returns the grid for this panel
40291      * @return {Roo.bootstrap.Table} 
40292      */
40293     getGrid : function(){
40294         return this.grid;    
40295     },
40296     
40297     setSize : function(width, height){
40298         if(!this.ignoreResize(width, height)){
40299             var grid = this.grid;
40300             var size = this.adjustForComponents(width, height);
40301             // tfoot is not a footer?
40302           
40303             
40304             var gridel = grid.getGridEl();
40305             gridel.setSize(size.width, size.height);
40306             
40307             var tbd = grid.getGridEl().select('tbody', true).first();
40308             var thd = grid.getGridEl().select('thead',true).first();
40309             var tbf= grid.getGridEl().select('tfoot', true).first();
40310
40311             if (tbf) {
40312                 size.height -= tbf.getHeight();
40313             }
40314             if (thd) {
40315                 size.height -= thd.getHeight();
40316             }
40317             
40318             tbd.setSize(size.width, size.height );
40319             // this is for the account management tab -seems to work there.
40320             var thd = grid.getGridEl().select('thead',true).first();
40321             //if (tbd) {
40322             //    tbd.setSize(size.width, size.height - thd.getHeight());
40323             //}
40324              
40325             grid.autoSize();
40326         }
40327     },
40328      
40329     
40330     
40331     beforeSlide : function(){
40332         this.grid.getView().scroller.clip();
40333     },
40334     
40335     afterSlide : function(){
40336         this.grid.getView().scroller.unclip();
40337     },
40338     
40339     destroy : function(){
40340         this.grid.destroy();
40341         delete this.grid;
40342         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40343     }
40344 });
40345
40346 /**
40347  * @class Roo.bootstrap.panel.Nest
40348  * @extends Roo.bootstrap.panel.Content
40349  * @constructor
40350  * Create a new Panel, that can contain a layout.Border.
40351  * 
40352  * 
40353  * @param {Roo.BorderLayout} layout The layout for this panel
40354  * @param {String/Object} config A string to set only the title or a config object
40355  */
40356 Roo.bootstrap.panel.Nest = function(config)
40357 {
40358     // construct with only one argument..
40359     /* FIXME - implement nicer consturctors
40360     if (layout.layout) {
40361         config = layout;
40362         layout = config.layout;
40363         delete config.layout;
40364     }
40365     if (layout.xtype && !layout.getEl) {
40366         // then layout needs constructing..
40367         layout = Roo.factory(layout, Roo);
40368     }
40369     */
40370     
40371     config.el =  config.layout.getEl();
40372     
40373     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40374     
40375     config.layout.monitorWindowResize = false; // turn off autosizing
40376     this.layout = config.layout;
40377     this.layout.getEl().addClass("roo-layout-nested-layout");
40378     this.layout.parent = this;
40379     
40380     
40381     
40382     
40383 };
40384
40385 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40386
40387     setSize : function(width, height){
40388         if(!this.ignoreResize(width, height)){
40389             var size = this.adjustForComponents(width, height);
40390             var el = this.layout.getEl();
40391             if (size.height < 1) {
40392                 el.setWidth(size.width);   
40393             } else {
40394                 el.setSize(size.width, size.height);
40395             }
40396             var touch = el.dom.offsetWidth;
40397             this.layout.layout();
40398             // ie requires a double layout on the first pass
40399             if(Roo.isIE && !this.initialized){
40400                 this.initialized = true;
40401                 this.layout.layout();
40402             }
40403         }
40404     },
40405     
40406     // activate all subpanels if not currently active..
40407     
40408     setActiveState : function(active){
40409         this.active = active;
40410         this.setActiveClass(active);
40411         
40412         if(!active){
40413             this.fireEvent("deactivate", this);
40414             return;
40415         }
40416         
40417         this.fireEvent("activate", this);
40418         // not sure if this should happen before or after..
40419         if (!this.layout) {
40420             return; // should not happen..
40421         }
40422         var reg = false;
40423         for (var r in this.layout.regions) {
40424             reg = this.layout.getRegion(r);
40425             if (reg.getActivePanel()) {
40426                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40427                 reg.setActivePanel(reg.getActivePanel());
40428                 continue;
40429             }
40430             if (!reg.panels.length) {
40431                 continue;
40432             }
40433             reg.showPanel(reg.getPanel(0));
40434         }
40435         
40436         
40437         
40438         
40439     },
40440     
40441     /**
40442      * Returns the nested BorderLayout for this panel
40443      * @return {Roo.BorderLayout} 
40444      */
40445     getLayout : function(){
40446         return this.layout;
40447     },
40448     
40449      /**
40450      * Adds a xtype elements to the layout of the nested panel
40451      * <pre><code>
40452
40453 panel.addxtype({
40454        xtype : 'ContentPanel',
40455        region: 'west',
40456        items: [ .... ]
40457    }
40458 );
40459
40460 panel.addxtype({
40461         xtype : 'NestedLayoutPanel',
40462         region: 'west',
40463         layout: {
40464            center: { },
40465            west: { }   
40466         },
40467         items : [ ... list of content panels or nested layout panels.. ]
40468    }
40469 );
40470 </code></pre>
40471      * @param {Object} cfg Xtype definition of item to add.
40472      */
40473     addxtype : function(cfg) {
40474         return this.layout.addxtype(cfg);
40475     
40476     }
40477 });/*
40478  * Based on:
40479  * Ext JS Library 1.1.1
40480  * Copyright(c) 2006-2007, Ext JS, LLC.
40481  *
40482  * Originally Released Under LGPL - original licence link has changed is not relivant.
40483  *
40484  * Fork - LGPL
40485  * <script type="text/javascript">
40486  */
40487 /**
40488  * @class Roo.TabPanel
40489  * @extends Roo.util.Observable
40490  * A lightweight tab container.
40491  * <br><br>
40492  * Usage:
40493  * <pre><code>
40494 // basic tabs 1, built from existing content
40495 var tabs = new Roo.TabPanel("tabs1");
40496 tabs.addTab("script", "View Script");
40497 tabs.addTab("markup", "View Markup");
40498 tabs.activate("script");
40499
40500 // more advanced tabs, built from javascript
40501 var jtabs = new Roo.TabPanel("jtabs");
40502 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40503
40504 // set up the UpdateManager
40505 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40506 var updater = tab2.getUpdateManager();
40507 updater.setDefaultUrl("ajax1.htm");
40508 tab2.on('activate', updater.refresh, updater, true);
40509
40510 // Use setUrl for Ajax loading
40511 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40512 tab3.setUrl("ajax2.htm", null, true);
40513
40514 // Disabled tab
40515 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40516 tab4.disable();
40517
40518 jtabs.activate("jtabs-1");
40519  * </code></pre>
40520  * @constructor
40521  * Create a new TabPanel.
40522  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40523  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40524  */
40525 Roo.bootstrap.panel.Tabs = function(config){
40526     /**
40527     * The container element for this TabPanel.
40528     * @type Roo.Element
40529     */
40530     this.el = Roo.get(config.el);
40531     delete config.el;
40532     if(config){
40533         if(typeof config == "boolean"){
40534             this.tabPosition = config ? "bottom" : "top";
40535         }else{
40536             Roo.apply(this, config);
40537         }
40538     }
40539     
40540     if(this.tabPosition == "bottom"){
40541         // if tabs are at the bottom = create the body first.
40542         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40543         this.el.addClass("roo-tabs-bottom");
40544     }
40545     // next create the tabs holders
40546     
40547     if (this.tabPosition == "west"){
40548         
40549         var reg = this.region; // fake it..
40550         while (reg) {
40551             if (!reg.mgr.parent) {
40552                 break;
40553             }
40554             reg = reg.mgr.parent.region;
40555         }
40556         Roo.log("got nest?");
40557         Roo.log(reg);
40558         if (reg.mgr.getRegion('west')) {
40559             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40560             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40561             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40562             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40563             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40564         
40565             
40566         }
40567         
40568         
40569     } else {
40570      
40571         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40572         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40573         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40574         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40575     }
40576     
40577     
40578     if(Roo.isIE){
40579         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40580     }
40581     
40582     // finally - if tabs are at the top, then create the body last..
40583     if(this.tabPosition != "bottom"){
40584         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40585          * @type Roo.Element
40586          */
40587         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40588         this.el.addClass("roo-tabs-top");
40589     }
40590     this.items = [];
40591
40592     this.bodyEl.setStyle("position", "relative");
40593
40594     this.active = null;
40595     this.activateDelegate = this.activate.createDelegate(this);
40596
40597     this.addEvents({
40598         /**
40599          * @event tabchange
40600          * Fires when the active tab changes
40601          * @param {Roo.TabPanel} this
40602          * @param {Roo.TabPanelItem} activePanel The new active tab
40603          */
40604         "tabchange": true,
40605         /**
40606          * @event beforetabchange
40607          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40608          * @param {Roo.TabPanel} this
40609          * @param {Object} e Set cancel to true on this object to cancel the tab change
40610          * @param {Roo.TabPanelItem} tab The tab being changed to
40611          */
40612         "beforetabchange" : true
40613     });
40614
40615     Roo.EventManager.onWindowResize(this.onResize, this);
40616     this.cpad = this.el.getPadding("lr");
40617     this.hiddenCount = 0;
40618
40619
40620     // toolbar on the tabbar support...
40621     if (this.toolbar) {
40622         alert("no toolbar support yet");
40623         this.toolbar  = false;
40624         /*
40625         var tcfg = this.toolbar;
40626         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40627         this.toolbar = new Roo.Toolbar(tcfg);
40628         if (Roo.isSafari) {
40629             var tbl = tcfg.container.child('table', true);
40630             tbl.setAttribute('width', '100%');
40631         }
40632         */
40633         
40634     }
40635    
40636
40637
40638     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40639 };
40640
40641 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40642     /*
40643      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40644      */
40645     tabPosition : "top",
40646     /*
40647      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40648      */
40649     currentTabWidth : 0,
40650     /*
40651      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40652      */
40653     minTabWidth : 40,
40654     /*
40655      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40656      */
40657     maxTabWidth : 250,
40658     /*
40659      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40660      */
40661     preferredTabWidth : 175,
40662     /*
40663      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40664      */
40665     resizeTabs : false,
40666     /*
40667      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40668      */
40669     monitorResize : true,
40670     /*
40671      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40672      */
40673     toolbar : false,  // set by caller..
40674     
40675     region : false, /// set by caller
40676     
40677     disableTooltips : true, // not used yet...
40678
40679     /**
40680      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40681      * @param {String} id The id of the div to use <b>or create</b>
40682      * @param {String} text The text for the tab
40683      * @param {String} content (optional) Content to put in the TabPanelItem body
40684      * @param {Boolean} closable (optional) True to create a close icon on the tab
40685      * @return {Roo.TabPanelItem} The created TabPanelItem
40686      */
40687     addTab : function(id, text, content, closable, tpl)
40688     {
40689         var item = new Roo.bootstrap.panel.TabItem({
40690             panel: this,
40691             id : id,
40692             text : text,
40693             closable : closable,
40694             tpl : tpl
40695         });
40696         this.addTabItem(item);
40697         if(content){
40698             item.setContent(content);
40699         }
40700         return item;
40701     },
40702
40703     /**
40704      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40705      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40706      * @return {Roo.TabPanelItem}
40707      */
40708     getTab : function(id){
40709         return this.items[id];
40710     },
40711
40712     /**
40713      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40714      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40715      */
40716     hideTab : function(id){
40717         var t = this.items[id];
40718         if(!t.isHidden()){
40719            t.setHidden(true);
40720            this.hiddenCount++;
40721            this.autoSizeTabs();
40722         }
40723     },
40724
40725     /**
40726      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40727      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40728      */
40729     unhideTab : function(id){
40730         var t = this.items[id];
40731         if(t.isHidden()){
40732            t.setHidden(false);
40733            this.hiddenCount--;
40734            this.autoSizeTabs();
40735         }
40736     },
40737
40738     /**
40739      * Adds an existing {@link Roo.TabPanelItem}.
40740      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40741      */
40742     addTabItem : function(item)
40743     {
40744         this.items[item.id] = item;
40745         this.items.push(item);
40746         this.autoSizeTabs();
40747       //  if(this.resizeTabs){
40748     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40749   //         this.autoSizeTabs();
40750 //        }else{
40751 //            item.autoSize();
40752        // }
40753     },
40754
40755     /**
40756      * Removes a {@link Roo.TabPanelItem}.
40757      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40758      */
40759     removeTab : function(id){
40760         var items = this.items;
40761         var tab = items[id];
40762         if(!tab) { return; }
40763         var index = items.indexOf(tab);
40764         if(this.active == tab && items.length > 1){
40765             var newTab = this.getNextAvailable(index);
40766             if(newTab) {
40767                 newTab.activate();
40768             }
40769         }
40770         this.stripEl.dom.removeChild(tab.pnode.dom);
40771         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40772             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40773         }
40774         items.splice(index, 1);
40775         delete this.items[tab.id];
40776         tab.fireEvent("close", tab);
40777         tab.purgeListeners();
40778         this.autoSizeTabs();
40779     },
40780
40781     getNextAvailable : function(start){
40782         var items = this.items;
40783         var index = start;
40784         // look for a next tab that will slide over to
40785         // replace the one being removed
40786         while(index < items.length){
40787             var item = items[++index];
40788             if(item && !item.isHidden()){
40789                 return item;
40790             }
40791         }
40792         // if one isn't found select the previous tab (on the left)
40793         index = start;
40794         while(index >= 0){
40795             var item = items[--index];
40796             if(item && !item.isHidden()){
40797                 return item;
40798             }
40799         }
40800         return null;
40801     },
40802
40803     /**
40804      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40805      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40806      */
40807     disableTab : function(id){
40808         var tab = this.items[id];
40809         if(tab && this.active != tab){
40810             tab.disable();
40811         }
40812     },
40813
40814     /**
40815      * Enables a {@link Roo.TabPanelItem} that is disabled.
40816      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40817      */
40818     enableTab : function(id){
40819         var tab = this.items[id];
40820         tab.enable();
40821     },
40822
40823     /**
40824      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40825      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40826      * @return {Roo.TabPanelItem} The TabPanelItem.
40827      */
40828     activate : function(id)
40829     {
40830         //Roo.log('activite:'  + id);
40831         
40832         var tab = this.items[id];
40833         if(!tab){
40834             return null;
40835         }
40836         if(tab == this.active || tab.disabled){
40837             return tab;
40838         }
40839         var e = {};
40840         this.fireEvent("beforetabchange", this, e, tab);
40841         if(e.cancel !== true && !tab.disabled){
40842             if(this.active){
40843                 this.active.hide();
40844             }
40845             this.active = this.items[id];
40846             this.active.show();
40847             this.fireEvent("tabchange", this, this.active);
40848         }
40849         return tab;
40850     },
40851
40852     /**
40853      * Gets the active {@link Roo.TabPanelItem}.
40854      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40855      */
40856     getActiveTab : function(){
40857         return this.active;
40858     },
40859
40860     /**
40861      * Updates the tab body element to fit the height of the container element
40862      * for overflow scrolling
40863      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40864      */
40865     syncHeight : function(targetHeight){
40866         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40867         var bm = this.bodyEl.getMargins();
40868         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40869         this.bodyEl.setHeight(newHeight);
40870         return newHeight;
40871     },
40872
40873     onResize : function(){
40874         if(this.monitorResize){
40875             this.autoSizeTabs();
40876         }
40877     },
40878
40879     /**
40880      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40881      */
40882     beginUpdate : function(){
40883         this.updating = true;
40884     },
40885
40886     /**
40887      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40888      */
40889     endUpdate : function(){
40890         this.updating = false;
40891         this.autoSizeTabs();
40892     },
40893
40894     /**
40895      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40896      */
40897     autoSizeTabs : function()
40898     {
40899         var count = this.items.length;
40900         var vcount = count - this.hiddenCount;
40901         
40902         if (vcount < 2) {
40903             this.stripEl.hide();
40904         } else {
40905             this.stripEl.show();
40906         }
40907         
40908         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40909             return;
40910         }
40911         
40912         
40913         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40914         var availWidth = Math.floor(w / vcount);
40915         var b = this.stripBody;
40916         if(b.getWidth() > w){
40917             var tabs = this.items;
40918             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40919             if(availWidth < this.minTabWidth){
40920                 /*if(!this.sleft){    // incomplete scrolling code
40921                     this.createScrollButtons();
40922                 }
40923                 this.showScroll();
40924                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40925             }
40926         }else{
40927             if(this.currentTabWidth < this.preferredTabWidth){
40928                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40929             }
40930         }
40931     },
40932
40933     /**
40934      * Returns the number of tabs in this TabPanel.
40935      * @return {Number}
40936      */
40937      getCount : function(){
40938          return this.items.length;
40939      },
40940
40941     /**
40942      * Resizes all the tabs to the passed width
40943      * @param {Number} The new width
40944      */
40945     setTabWidth : function(width){
40946         this.currentTabWidth = width;
40947         for(var i = 0, len = this.items.length; i < len; i++) {
40948                 if(!this.items[i].isHidden()) {
40949                 this.items[i].setWidth(width);
40950             }
40951         }
40952     },
40953
40954     /**
40955      * Destroys this TabPanel
40956      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40957      */
40958     destroy : function(removeEl){
40959         Roo.EventManager.removeResizeListener(this.onResize, this);
40960         for(var i = 0, len = this.items.length; i < len; i++){
40961             this.items[i].purgeListeners();
40962         }
40963         if(removeEl === true){
40964             this.el.update("");
40965             this.el.remove();
40966         }
40967     },
40968     
40969     createStrip : function(container)
40970     {
40971         var strip = document.createElement("nav");
40972         strip.className = Roo.bootstrap.version == 4 ?
40973             "navbar-light bg-light" : 
40974             "navbar navbar-default"; //"x-tabs-wrap";
40975         container.appendChild(strip);
40976         return strip;
40977     },
40978     
40979     createStripList : function(strip)
40980     {
40981         // div wrapper for retard IE
40982         // returns the "tr" element.
40983         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40984         //'<div class="x-tabs-strip-wrap">'+
40985           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40986           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40987         return strip.firstChild; //.firstChild.firstChild.firstChild;
40988     },
40989     createBody : function(container)
40990     {
40991         var body = document.createElement("div");
40992         Roo.id(body, "tab-body");
40993         //Roo.fly(body).addClass("x-tabs-body");
40994         Roo.fly(body).addClass("tab-content");
40995         container.appendChild(body);
40996         return body;
40997     },
40998     createItemBody :function(bodyEl, id){
40999         var body = Roo.getDom(id);
41000         if(!body){
41001             body = document.createElement("div");
41002             body.id = id;
41003         }
41004         //Roo.fly(body).addClass("x-tabs-item-body");
41005         Roo.fly(body).addClass("tab-pane");
41006          bodyEl.insertBefore(body, bodyEl.firstChild);
41007         return body;
41008     },
41009     /** @private */
41010     createStripElements :  function(stripEl, text, closable, tpl)
41011     {
41012         var td = document.createElement("li"); // was td..
41013         td.className = 'nav-item';
41014         
41015         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41016         
41017         
41018         stripEl.appendChild(td);
41019         /*if(closable){
41020             td.className = "x-tabs-closable";
41021             if(!this.closeTpl){
41022                 this.closeTpl = new Roo.Template(
41023                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41024                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41025                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41026                 );
41027             }
41028             var el = this.closeTpl.overwrite(td, {"text": text});
41029             var close = el.getElementsByTagName("div")[0];
41030             var inner = el.getElementsByTagName("em")[0];
41031             return {"el": el, "close": close, "inner": inner};
41032         } else {
41033         */
41034         // not sure what this is..
41035 //            if(!this.tabTpl){
41036                 //this.tabTpl = new Roo.Template(
41037                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41038                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41039                 //);
41040 //                this.tabTpl = new Roo.Template(
41041 //                   '<a href="#">' +
41042 //                   '<span unselectable="on"' +
41043 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41044 //                            ' >{text}</span></a>'
41045 //                );
41046 //                
41047 //            }
41048
41049
41050             var template = tpl || this.tabTpl || false;
41051             
41052             if(!template){
41053                 template =  new Roo.Template(
41054                         Roo.bootstrap.version == 4 ? 
41055                             (
41056                                 '<a class="nav-link" href="#" unselectable="on"' +
41057                                      (this.disableTooltips ? '' : ' title="{text}"') +
41058                                      ' >{text}</a>'
41059                             ) : (
41060                                 '<a class="nav-link" href="#">' +
41061                                 '<span unselectable="on"' +
41062                                          (this.disableTooltips ? '' : ' title="{text}"') +
41063                                     ' >{text}</span></a>'
41064                             )
41065                 );
41066             }
41067             
41068             switch (typeof(template)) {
41069                 case 'object' :
41070                     break;
41071                 case 'string' :
41072                     template = new Roo.Template(template);
41073                     break;
41074                 default :
41075                     break;
41076             }
41077             
41078             var el = template.overwrite(td, {"text": text});
41079             
41080             var inner = el.getElementsByTagName("span")[0];
41081             
41082             return {"el": el, "inner": inner};
41083             
41084     }
41085         
41086     
41087 });
41088
41089 /**
41090  * @class Roo.TabPanelItem
41091  * @extends Roo.util.Observable
41092  * Represents an individual item (tab plus body) in a TabPanel.
41093  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41094  * @param {String} id The id of this TabPanelItem
41095  * @param {String} text The text for the tab of this TabPanelItem
41096  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41097  */
41098 Roo.bootstrap.panel.TabItem = function(config){
41099     /**
41100      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41101      * @type Roo.TabPanel
41102      */
41103     this.tabPanel = config.panel;
41104     /**
41105      * The id for this TabPanelItem
41106      * @type String
41107      */
41108     this.id = config.id;
41109     /** @private */
41110     this.disabled = false;
41111     /** @private */
41112     this.text = config.text;
41113     /** @private */
41114     this.loaded = false;
41115     this.closable = config.closable;
41116
41117     /**
41118      * The body element for this TabPanelItem.
41119      * @type Roo.Element
41120      */
41121     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41122     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41123     this.bodyEl.setStyle("display", "block");
41124     this.bodyEl.setStyle("zoom", "1");
41125     //this.hideAction();
41126
41127     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41128     /** @private */
41129     this.el = Roo.get(els.el);
41130     this.inner = Roo.get(els.inner, true);
41131      this.textEl = Roo.bootstrap.version == 4 ?
41132         this.el : Roo.get(this.el.dom.firstChild, true);
41133
41134     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41135     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41136
41137     
41138 //    this.el.on("mousedown", this.onTabMouseDown, this);
41139     this.el.on("click", this.onTabClick, this);
41140     /** @private */
41141     if(config.closable){
41142         var c = Roo.get(els.close, true);
41143         c.dom.title = this.closeText;
41144         c.addClassOnOver("close-over");
41145         c.on("click", this.closeClick, this);
41146      }
41147
41148     this.addEvents({
41149          /**
41150          * @event activate
41151          * Fires when this tab becomes the active tab.
41152          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41153          * @param {Roo.TabPanelItem} this
41154          */
41155         "activate": true,
41156         /**
41157          * @event beforeclose
41158          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41159          * @param {Roo.TabPanelItem} this
41160          * @param {Object} e Set cancel to true on this object to cancel the close.
41161          */
41162         "beforeclose": true,
41163         /**
41164          * @event close
41165          * Fires when this tab is closed.
41166          * @param {Roo.TabPanelItem} this
41167          */
41168          "close": true,
41169         /**
41170          * @event deactivate
41171          * Fires when this tab is no longer the active tab.
41172          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41173          * @param {Roo.TabPanelItem} this
41174          */
41175          "deactivate" : true
41176     });
41177     this.hidden = false;
41178
41179     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41180 };
41181
41182 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41183            {
41184     purgeListeners : function(){
41185        Roo.util.Observable.prototype.purgeListeners.call(this);
41186        this.el.removeAllListeners();
41187     },
41188     /**
41189      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41190      */
41191     show : function(){
41192         this.status_node.addClass("active");
41193         this.showAction();
41194         if(Roo.isOpera){
41195             this.tabPanel.stripWrap.repaint();
41196         }
41197         this.fireEvent("activate", this.tabPanel, this);
41198     },
41199
41200     /**
41201      * Returns true if this tab is the active tab.
41202      * @return {Boolean}
41203      */
41204     isActive : function(){
41205         return this.tabPanel.getActiveTab() == this;
41206     },
41207
41208     /**
41209      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41210      */
41211     hide : function(){
41212         this.status_node.removeClass("active");
41213         this.hideAction();
41214         this.fireEvent("deactivate", this.tabPanel, this);
41215     },
41216
41217     hideAction : function(){
41218         this.bodyEl.hide();
41219         this.bodyEl.setStyle("position", "absolute");
41220         this.bodyEl.setLeft("-20000px");
41221         this.bodyEl.setTop("-20000px");
41222     },
41223
41224     showAction : function(){
41225         this.bodyEl.setStyle("position", "relative");
41226         this.bodyEl.setTop("");
41227         this.bodyEl.setLeft("");
41228         this.bodyEl.show();
41229     },
41230
41231     /**
41232      * Set the tooltip for the tab.
41233      * @param {String} tooltip The tab's tooltip
41234      */
41235     setTooltip : function(text){
41236         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41237             this.textEl.dom.qtip = text;
41238             this.textEl.dom.removeAttribute('title');
41239         }else{
41240             this.textEl.dom.title = text;
41241         }
41242     },
41243
41244     onTabClick : function(e){
41245         e.preventDefault();
41246         this.tabPanel.activate(this.id);
41247     },
41248
41249     onTabMouseDown : function(e){
41250         e.preventDefault();
41251         this.tabPanel.activate(this.id);
41252     },
41253 /*
41254     getWidth : function(){
41255         return this.inner.getWidth();
41256     },
41257
41258     setWidth : function(width){
41259         var iwidth = width - this.linode.getPadding("lr");
41260         this.inner.setWidth(iwidth);
41261         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41262         this.linode.setWidth(width);
41263     },
41264 */
41265     /**
41266      * Show or hide the tab
41267      * @param {Boolean} hidden True to hide or false to show.
41268      */
41269     setHidden : function(hidden){
41270         this.hidden = hidden;
41271         this.linode.setStyle("display", hidden ? "none" : "");
41272     },
41273
41274     /**
41275      * Returns true if this tab is "hidden"
41276      * @return {Boolean}
41277      */
41278     isHidden : function(){
41279         return this.hidden;
41280     },
41281
41282     /**
41283      * Returns the text for this tab
41284      * @return {String}
41285      */
41286     getText : function(){
41287         return this.text;
41288     },
41289     /*
41290     autoSize : function(){
41291         //this.el.beginMeasure();
41292         this.textEl.setWidth(1);
41293         /*
41294          *  #2804 [new] Tabs in Roojs
41295          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41296          */
41297         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41298         //this.el.endMeasure();
41299     //},
41300
41301     /**
41302      * Sets the text for the tab (Note: this also sets the tooltip text)
41303      * @param {String} text The tab's text and tooltip
41304      */
41305     setText : function(text){
41306         this.text = text;
41307         this.textEl.update(text);
41308         this.setTooltip(text);
41309         //if(!this.tabPanel.resizeTabs){
41310         //    this.autoSize();
41311         //}
41312     },
41313     /**
41314      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41315      */
41316     activate : function(){
41317         this.tabPanel.activate(this.id);
41318     },
41319
41320     /**
41321      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41322      */
41323     disable : function(){
41324         if(this.tabPanel.active != this){
41325             this.disabled = true;
41326             this.status_node.addClass("disabled");
41327         }
41328     },
41329
41330     /**
41331      * Enables this TabPanelItem if it was previously disabled.
41332      */
41333     enable : function(){
41334         this.disabled = false;
41335         this.status_node.removeClass("disabled");
41336     },
41337
41338     /**
41339      * Sets the content for this TabPanelItem.
41340      * @param {String} content The content
41341      * @param {Boolean} loadScripts true to look for and load scripts
41342      */
41343     setContent : function(content, loadScripts){
41344         this.bodyEl.update(content, loadScripts);
41345     },
41346
41347     /**
41348      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41349      * @return {Roo.UpdateManager} The UpdateManager
41350      */
41351     getUpdateManager : function(){
41352         return this.bodyEl.getUpdateManager();
41353     },
41354
41355     /**
41356      * Set a URL to be used to load the content for this TabPanelItem.
41357      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41358      * @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)
41359      * @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)
41360      * @return {Roo.UpdateManager} The UpdateManager
41361      */
41362     setUrl : function(url, params, loadOnce){
41363         if(this.refreshDelegate){
41364             this.un('activate', this.refreshDelegate);
41365         }
41366         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41367         this.on("activate", this.refreshDelegate);
41368         return this.bodyEl.getUpdateManager();
41369     },
41370
41371     /** @private */
41372     _handleRefresh : function(url, params, loadOnce){
41373         if(!loadOnce || !this.loaded){
41374             var updater = this.bodyEl.getUpdateManager();
41375             updater.update(url, params, this._setLoaded.createDelegate(this));
41376         }
41377     },
41378
41379     /**
41380      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41381      *   Will fail silently if the setUrl method has not been called.
41382      *   This does not activate the panel, just updates its content.
41383      */
41384     refresh : function(){
41385         if(this.refreshDelegate){
41386            this.loaded = false;
41387            this.refreshDelegate();
41388         }
41389     },
41390
41391     /** @private */
41392     _setLoaded : function(){
41393         this.loaded = true;
41394     },
41395
41396     /** @private */
41397     closeClick : function(e){
41398         var o = {};
41399         e.stopEvent();
41400         this.fireEvent("beforeclose", this, o);
41401         if(o.cancel !== true){
41402             this.tabPanel.removeTab(this.id);
41403         }
41404     },
41405     /**
41406      * The text displayed in the tooltip for the close icon.
41407      * @type String
41408      */
41409     closeText : "Close this tab"
41410 });
41411 /**
41412 *    This script refer to:
41413 *    Title: International Telephone Input
41414 *    Author: Jack O'Connor
41415 *    Code version:  v12.1.12
41416 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41417 **/
41418
41419 Roo.bootstrap.PhoneInputData = function() {
41420     var d = [
41421       [
41422         "Afghanistan (‫افغانستان‬‎)",
41423         "af",
41424         "93"
41425       ],
41426       [
41427         "Albania (Shqipëri)",
41428         "al",
41429         "355"
41430       ],
41431       [
41432         "Algeria (‫الجزائر‬‎)",
41433         "dz",
41434         "213"
41435       ],
41436       [
41437         "American Samoa",
41438         "as",
41439         "1684"
41440       ],
41441       [
41442         "Andorra",
41443         "ad",
41444         "376"
41445       ],
41446       [
41447         "Angola",
41448         "ao",
41449         "244"
41450       ],
41451       [
41452         "Anguilla",
41453         "ai",
41454         "1264"
41455       ],
41456       [
41457         "Antigua and Barbuda",
41458         "ag",
41459         "1268"
41460       ],
41461       [
41462         "Argentina",
41463         "ar",
41464         "54"
41465       ],
41466       [
41467         "Armenia (Հայաստան)",
41468         "am",
41469         "374"
41470       ],
41471       [
41472         "Aruba",
41473         "aw",
41474         "297"
41475       ],
41476       [
41477         "Australia",
41478         "au",
41479         "61",
41480         0
41481       ],
41482       [
41483         "Austria (Österreich)",
41484         "at",
41485         "43"
41486       ],
41487       [
41488         "Azerbaijan (Azərbaycan)",
41489         "az",
41490         "994"
41491       ],
41492       [
41493         "Bahamas",
41494         "bs",
41495         "1242"
41496       ],
41497       [
41498         "Bahrain (‫البحرين‬‎)",
41499         "bh",
41500         "973"
41501       ],
41502       [
41503         "Bangladesh (বাংলাদেশ)",
41504         "bd",
41505         "880"
41506       ],
41507       [
41508         "Barbados",
41509         "bb",
41510         "1246"
41511       ],
41512       [
41513         "Belarus (Беларусь)",
41514         "by",
41515         "375"
41516       ],
41517       [
41518         "Belgium (België)",
41519         "be",
41520         "32"
41521       ],
41522       [
41523         "Belize",
41524         "bz",
41525         "501"
41526       ],
41527       [
41528         "Benin (Bénin)",
41529         "bj",
41530         "229"
41531       ],
41532       [
41533         "Bermuda",
41534         "bm",
41535         "1441"
41536       ],
41537       [
41538         "Bhutan (འབྲུག)",
41539         "bt",
41540         "975"
41541       ],
41542       [
41543         "Bolivia",
41544         "bo",
41545         "591"
41546       ],
41547       [
41548         "Bosnia and Herzegovina (Босна и Херцеговина)",
41549         "ba",
41550         "387"
41551       ],
41552       [
41553         "Botswana",
41554         "bw",
41555         "267"
41556       ],
41557       [
41558         "Brazil (Brasil)",
41559         "br",
41560         "55"
41561       ],
41562       [
41563         "British Indian Ocean Territory",
41564         "io",
41565         "246"
41566       ],
41567       [
41568         "British Virgin Islands",
41569         "vg",
41570         "1284"
41571       ],
41572       [
41573         "Brunei",
41574         "bn",
41575         "673"
41576       ],
41577       [
41578         "Bulgaria (България)",
41579         "bg",
41580         "359"
41581       ],
41582       [
41583         "Burkina Faso",
41584         "bf",
41585         "226"
41586       ],
41587       [
41588         "Burundi (Uburundi)",
41589         "bi",
41590         "257"
41591       ],
41592       [
41593         "Cambodia (កម្ពុជា)",
41594         "kh",
41595         "855"
41596       ],
41597       [
41598         "Cameroon (Cameroun)",
41599         "cm",
41600         "237"
41601       ],
41602       [
41603         "Canada",
41604         "ca",
41605         "1",
41606         1,
41607         ["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"]
41608       ],
41609       [
41610         "Cape Verde (Kabu Verdi)",
41611         "cv",
41612         "238"
41613       ],
41614       [
41615         "Caribbean Netherlands",
41616         "bq",
41617         "599",
41618         1
41619       ],
41620       [
41621         "Cayman Islands",
41622         "ky",
41623         "1345"
41624       ],
41625       [
41626         "Central African Republic (République centrafricaine)",
41627         "cf",
41628         "236"
41629       ],
41630       [
41631         "Chad (Tchad)",
41632         "td",
41633         "235"
41634       ],
41635       [
41636         "Chile",
41637         "cl",
41638         "56"
41639       ],
41640       [
41641         "China (中国)",
41642         "cn",
41643         "86"
41644       ],
41645       [
41646         "Christmas Island",
41647         "cx",
41648         "61",
41649         2
41650       ],
41651       [
41652         "Cocos (Keeling) Islands",
41653         "cc",
41654         "61",
41655         1
41656       ],
41657       [
41658         "Colombia",
41659         "co",
41660         "57"
41661       ],
41662       [
41663         "Comoros (‫جزر القمر‬‎)",
41664         "km",
41665         "269"
41666       ],
41667       [
41668         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41669         "cd",
41670         "243"
41671       ],
41672       [
41673         "Congo (Republic) (Congo-Brazzaville)",
41674         "cg",
41675         "242"
41676       ],
41677       [
41678         "Cook Islands",
41679         "ck",
41680         "682"
41681       ],
41682       [
41683         "Costa Rica",
41684         "cr",
41685         "506"
41686       ],
41687       [
41688         "Côte d’Ivoire",
41689         "ci",
41690         "225"
41691       ],
41692       [
41693         "Croatia (Hrvatska)",
41694         "hr",
41695         "385"
41696       ],
41697       [
41698         "Cuba",
41699         "cu",
41700         "53"
41701       ],
41702       [
41703         "Curaçao",
41704         "cw",
41705         "599",
41706         0
41707       ],
41708       [
41709         "Cyprus (Κύπρος)",
41710         "cy",
41711         "357"
41712       ],
41713       [
41714         "Czech Republic (Česká republika)",
41715         "cz",
41716         "420"
41717       ],
41718       [
41719         "Denmark (Danmark)",
41720         "dk",
41721         "45"
41722       ],
41723       [
41724         "Djibouti",
41725         "dj",
41726         "253"
41727       ],
41728       [
41729         "Dominica",
41730         "dm",
41731         "1767"
41732       ],
41733       [
41734         "Dominican Republic (República Dominicana)",
41735         "do",
41736         "1",
41737         2,
41738         ["809", "829", "849"]
41739       ],
41740       [
41741         "Ecuador",
41742         "ec",
41743         "593"
41744       ],
41745       [
41746         "Egypt (‫مصر‬‎)",
41747         "eg",
41748         "20"
41749       ],
41750       [
41751         "El Salvador",
41752         "sv",
41753         "503"
41754       ],
41755       [
41756         "Equatorial Guinea (Guinea Ecuatorial)",
41757         "gq",
41758         "240"
41759       ],
41760       [
41761         "Eritrea",
41762         "er",
41763         "291"
41764       ],
41765       [
41766         "Estonia (Eesti)",
41767         "ee",
41768         "372"
41769       ],
41770       [
41771         "Ethiopia",
41772         "et",
41773         "251"
41774       ],
41775       [
41776         "Falkland Islands (Islas Malvinas)",
41777         "fk",
41778         "500"
41779       ],
41780       [
41781         "Faroe Islands (Føroyar)",
41782         "fo",
41783         "298"
41784       ],
41785       [
41786         "Fiji",
41787         "fj",
41788         "679"
41789       ],
41790       [
41791         "Finland (Suomi)",
41792         "fi",
41793         "358",
41794         0
41795       ],
41796       [
41797         "France",
41798         "fr",
41799         "33"
41800       ],
41801       [
41802         "French Guiana (Guyane française)",
41803         "gf",
41804         "594"
41805       ],
41806       [
41807         "French Polynesia (Polynésie française)",
41808         "pf",
41809         "689"
41810       ],
41811       [
41812         "Gabon",
41813         "ga",
41814         "241"
41815       ],
41816       [
41817         "Gambia",
41818         "gm",
41819         "220"
41820       ],
41821       [
41822         "Georgia (საქართველო)",
41823         "ge",
41824         "995"
41825       ],
41826       [
41827         "Germany (Deutschland)",
41828         "de",
41829         "49"
41830       ],
41831       [
41832         "Ghana (Gaana)",
41833         "gh",
41834         "233"
41835       ],
41836       [
41837         "Gibraltar",
41838         "gi",
41839         "350"
41840       ],
41841       [
41842         "Greece (Ελλάδα)",
41843         "gr",
41844         "30"
41845       ],
41846       [
41847         "Greenland (Kalaallit Nunaat)",
41848         "gl",
41849         "299"
41850       ],
41851       [
41852         "Grenada",
41853         "gd",
41854         "1473"
41855       ],
41856       [
41857         "Guadeloupe",
41858         "gp",
41859         "590",
41860         0
41861       ],
41862       [
41863         "Guam",
41864         "gu",
41865         "1671"
41866       ],
41867       [
41868         "Guatemala",
41869         "gt",
41870         "502"
41871       ],
41872       [
41873         "Guernsey",
41874         "gg",
41875         "44",
41876         1
41877       ],
41878       [
41879         "Guinea (Guinée)",
41880         "gn",
41881         "224"
41882       ],
41883       [
41884         "Guinea-Bissau (Guiné Bissau)",
41885         "gw",
41886         "245"
41887       ],
41888       [
41889         "Guyana",
41890         "gy",
41891         "592"
41892       ],
41893       [
41894         "Haiti",
41895         "ht",
41896         "509"
41897       ],
41898       [
41899         "Honduras",
41900         "hn",
41901         "504"
41902       ],
41903       [
41904         "Hong Kong (香港)",
41905         "hk",
41906         "852"
41907       ],
41908       [
41909         "Hungary (Magyarország)",
41910         "hu",
41911         "36"
41912       ],
41913       [
41914         "Iceland (Ísland)",
41915         "is",
41916         "354"
41917       ],
41918       [
41919         "India (भारत)",
41920         "in",
41921         "91"
41922       ],
41923       [
41924         "Indonesia",
41925         "id",
41926         "62"
41927       ],
41928       [
41929         "Iran (‫ایران‬‎)",
41930         "ir",
41931         "98"
41932       ],
41933       [
41934         "Iraq (‫العراق‬‎)",
41935         "iq",
41936         "964"
41937       ],
41938       [
41939         "Ireland",
41940         "ie",
41941         "353"
41942       ],
41943       [
41944         "Isle of Man",
41945         "im",
41946         "44",
41947         2
41948       ],
41949       [
41950         "Israel (‫ישראל‬‎)",
41951         "il",
41952         "972"
41953       ],
41954       [
41955         "Italy (Italia)",
41956         "it",
41957         "39",
41958         0
41959       ],
41960       [
41961         "Jamaica",
41962         "jm",
41963         "1876"
41964       ],
41965       [
41966         "Japan (日本)",
41967         "jp",
41968         "81"
41969       ],
41970       [
41971         "Jersey",
41972         "je",
41973         "44",
41974         3
41975       ],
41976       [
41977         "Jordan (‫الأردن‬‎)",
41978         "jo",
41979         "962"
41980       ],
41981       [
41982         "Kazakhstan (Казахстан)",
41983         "kz",
41984         "7",
41985         1
41986       ],
41987       [
41988         "Kenya",
41989         "ke",
41990         "254"
41991       ],
41992       [
41993         "Kiribati",
41994         "ki",
41995         "686"
41996       ],
41997       [
41998         "Kosovo",
41999         "xk",
42000         "383"
42001       ],
42002       [
42003         "Kuwait (‫الكويت‬‎)",
42004         "kw",
42005         "965"
42006       ],
42007       [
42008         "Kyrgyzstan (Кыргызстан)",
42009         "kg",
42010         "996"
42011       ],
42012       [
42013         "Laos (ລາວ)",
42014         "la",
42015         "856"
42016       ],
42017       [
42018         "Latvia (Latvija)",
42019         "lv",
42020         "371"
42021       ],
42022       [
42023         "Lebanon (‫لبنان‬‎)",
42024         "lb",
42025         "961"
42026       ],
42027       [
42028         "Lesotho",
42029         "ls",
42030         "266"
42031       ],
42032       [
42033         "Liberia",
42034         "lr",
42035         "231"
42036       ],
42037       [
42038         "Libya (‫ليبيا‬‎)",
42039         "ly",
42040         "218"
42041       ],
42042       [
42043         "Liechtenstein",
42044         "li",
42045         "423"
42046       ],
42047       [
42048         "Lithuania (Lietuva)",
42049         "lt",
42050         "370"
42051       ],
42052       [
42053         "Luxembourg",
42054         "lu",
42055         "352"
42056       ],
42057       [
42058         "Macau (澳門)",
42059         "mo",
42060         "853"
42061       ],
42062       [
42063         "Macedonia (FYROM) (Македонија)",
42064         "mk",
42065         "389"
42066       ],
42067       [
42068         "Madagascar (Madagasikara)",
42069         "mg",
42070         "261"
42071       ],
42072       [
42073         "Malawi",
42074         "mw",
42075         "265"
42076       ],
42077       [
42078         "Malaysia",
42079         "my",
42080         "60"
42081       ],
42082       [
42083         "Maldives",
42084         "mv",
42085         "960"
42086       ],
42087       [
42088         "Mali",
42089         "ml",
42090         "223"
42091       ],
42092       [
42093         "Malta",
42094         "mt",
42095         "356"
42096       ],
42097       [
42098         "Marshall Islands",
42099         "mh",
42100         "692"
42101       ],
42102       [
42103         "Martinique",
42104         "mq",
42105         "596"
42106       ],
42107       [
42108         "Mauritania (‫موريتانيا‬‎)",
42109         "mr",
42110         "222"
42111       ],
42112       [
42113         "Mauritius (Moris)",
42114         "mu",
42115         "230"
42116       ],
42117       [
42118         "Mayotte",
42119         "yt",
42120         "262",
42121         1
42122       ],
42123       [
42124         "Mexico (México)",
42125         "mx",
42126         "52"
42127       ],
42128       [
42129         "Micronesia",
42130         "fm",
42131         "691"
42132       ],
42133       [
42134         "Moldova (Republica Moldova)",
42135         "md",
42136         "373"
42137       ],
42138       [
42139         "Monaco",
42140         "mc",
42141         "377"
42142       ],
42143       [
42144         "Mongolia (Монгол)",
42145         "mn",
42146         "976"
42147       ],
42148       [
42149         "Montenegro (Crna Gora)",
42150         "me",
42151         "382"
42152       ],
42153       [
42154         "Montserrat",
42155         "ms",
42156         "1664"
42157       ],
42158       [
42159         "Morocco (‫المغرب‬‎)",
42160         "ma",
42161         "212",
42162         0
42163       ],
42164       [
42165         "Mozambique (Moçambique)",
42166         "mz",
42167         "258"
42168       ],
42169       [
42170         "Myanmar (Burma) (မြန်မာ)",
42171         "mm",
42172         "95"
42173       ],
42174       [
42175         "Namibia (Namibië)",
42176         "na",
42177         "264"
42178       ],
42179       [
42180         "Nauru",
42181         "nr",
42182         "674"
42183       ],
42184       [
42185         "Nepal (नेपाल)",
42186         "np",
42187         "977"
42188       ],
42189       [
42190         "Netherlands (Nederland)",
42191         "nl",
42192         "31"
42193       ],
42194       [
42195         "New Caledonia (Nouvelle-Calédonie)",
42196         "nc",
42197         "687"
42198       ],
42199       [
42200         "New Zealand",
42201         "nz",
42202         "64"
42203       ],
42204       [
42205         "Nicaragua",
42206         "ni",
42207         "505"
42208       ],
42209       [
42210         "Niger (Nijar)",
42211         "ne",
42212         "227"
42213       ],
42214       [
42215         "Nigeria",
42216         "ng",
42217         "234"
42218       ],
42219       [
42220         "Niue",
42221         "nu",
42222         "683"
42223       ],
42224       [
42225         "Norfolk Island",
42226         "nf",
42227         "672"
42228       ],
42229       [
42230         "North Korea (조선 민주주의 인민 공화국)",
42231         "kp",
42232         "850"
42233       ],
42234       [
42235         "Northern Mariana Islands",
42236         "mp",
42237         "1670"
42238       ],
42239       [
42240         "Norway (Norge)",
42241         "no",
42242         "47",
42243         0
42244       ],
42245       [
42246         "Oman (‫عُمان‬‎)",
42247         "om",
42248         "968"
42249       ],
42250       [
42251         "Pakistan (‫پاکستان‬‎)",
42252         "pk",
42253         "92"
42254       ],
42255       [
42256         "Palau",
42257         "pw",
42258         "680"
42259       ],
42260       [
42261         "Palestine (‫فلسطين‬‎)",
42262         "ps",
42263         "970"
42264       ],
42265       [
42266         "Panama (Panamá)",
42267         "pa",
42268         "507"
42269       ],
42270       [
42271         "Papua New Guinea",
42272         "pg",
42273         "675"
42274       ],
42275       [
42276         "Paraguay",
42277         "py",
42278         "595"
42279       ],
42280       [
42281         "Peru (Perú)",
42282         "pe",
42283         "51"
42284       ],
42285       [
42286         "Philippines",
42287         "ph",
42288         "63"
42289       ],
42290       [
42291         "Poland (Polska)",
42292         "pl",
42293         "48"
42294       ],
42295       [
42296         "Portugal",
42297         "pt",
42298         "351"
42299       ],
42300       [
42301         "Puerto Rico",
42302         "pr",
42303         "1",
42304         3,
42305         ["787", "939"]
42306       ],
42307       [
42308         "Qatar (‫قطر‬‎)",
42309         "qa",
42310         "974"
42311       ],
42312       [
42313         "Réunion (La Réunion)",
42314         "re",
42315         "262",
42316         0
42317       ],
42318       [
42319         "Romania (România)",
42320         "ro",
42321         "40"
42322       ],
42323       [
42324         "Russia (Россия)",
42325         "ru",
42326         "7",
42327         0
42328       ],
42329       [
42330         "Rwanda",
42331         "rw",
42332         "250"
42333       ],
42334       [
42335         "Saint Barthélemy",
42336         "bl",
42337         "590",
42338         1
42339       ],
42340       [
42341         "Saint Helena",
42342         "sh",
42343         "290"
42344       ],
42345       [
42346         "Saint Kitts and Nevis",
42347         "kn",
42348         "1869"
42349       ],
42350       [
42351         "Saint Lucia",
42352         "lc",
42353         "1758"
42354       ],
42355       [
42356         "Saint Martin (Saint-Martin (partie française))",
42357         "mf",
42358         "590",
42359         2
42360       ],
42361       [
42362         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42363         "pm",
42364         "508"
42365       ],
42366       [
42367         "Saint Vincent and the Grenadines",
42368         "vc",
42369         "1784"
42370       ],
42371       [
42372         "Samoa",
42373         "ws",
42374         "685"
42375       ],
42376       [
42377         "San Marino",
42378         "sm",
42379         "378"
42380       ],
42381       [
42382         "São Tomé and Príncipe (São Tomé e Príncipe)",
42383         "st",
42384         "239"
42385       ],
42386       [
42387         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42388         "sa",
42389         "966"
42390       ],
42391       [
42392         "Senegal (Sénégal)",
42393         "sn",
42394         "221"
42395       ],
42396       [
42397         "Serbia (Србија)",
42398         "rs",
42399         "381"
42400       ],
42401       [
42402         "Seychelles",
42403         "sc",
42404         "248"
42405       ],
42406       [
42407         "Sierra Leone",
42408         "sl",
42409         "232"
42410       ],
42411       [
42412         "Singapore",
42413         "sg",
42414         "65"
42415       ],
42416       [
42417         "Sint Maarten",
42418         "sx",
42419         "1721"
42420       ],
42421       [
42422         "Slovakia (Slovensko)",
42423         "sk",
42424         "421"
42425       ],
42426       [
42427         "Slovenia (Slovenija)",
42428         "si",
42429         "386"
42430       ],
42431       [
42432         "Solomon Islands",
42433         "sb",
42434         "677"
42435       ],
42436       [
42437         "Somalia (Soomaaliya)",
42438         "so",
42439         "252"
42440       ],
42441       [
42442         "South Africa",
42443         "za",
42444         "27"
42445       ],
42446       [
42447         "South Korea (대한민국)",
42448         "kr",
42449         "82"
42450       ],
42451       [
42452         "South Sudan (‫جنوب السودان‬‎)",
42453         "ss",
42454         "211"
42455       ],
42456       [
42457         "Spain (España)",
42458         "es",
42459         "34"
42460       ],
42461       [
42462         "Sri Lanka (ශ්‍රී ලංකාව)",
42463         "lk",
42464         "94"
42465       ],
42466       [
42467         "Sudan (‫السودان‬‎)",
42468         "sd",
42469         "249"
42470       ],
42471       [
42472         "Suriname",
42473         "sr",
42474         "597"
42475       ],
42476       [
42477         "Svalbard and Jan Mayen",
42478         "sj",
42479         "47",
42480         1
42481       ],
42482       [
42483         "Swaziland",
42484         "sz",
42485         "268"
42486       ],
42487       [
42488         "Sweden (Sverige)",
42489         "se",
42490         "46"
42491       ],
42492       [
42493         "Switzerland (Schweiz)",
42494         "ch",
42495         "41"
42496       ],
42497       [
42498         "Syria (‫سوريا‬‎)",
42499         "sy",
42500         "963"
42501       ],
42502       [
42503         "Taiwan (台灣)",
42504         "tw",
42505         "886"
42506       ],
42507       [
42508         "Tajikistan",
42509         "tj",
42510         "992"
42511       ],
42512       [
42513         "Tanzania",
42514         "tz",
42515         "255"
42516       ],
42517       [
42518         "Thailand (ไทย)",
42519         "th",
42520         "66"
42521       ],
42522       [
42523         "Timor-Leste",
42524         "tl",
42525         "670"
42526       ],
42527       [
42528         "Togo",
42529         "tg",
42530         "228"
42531       ],
42532       [
42533         "Tokelau",
42534         "tk",
42535         "690"
42536       ],
42537       [
42538         "Tonga",
42539         "to",
42540         "676"
42541       ],
42542       [
42543         "Trinidad and Tobago",
42544         "tt",
42545         "1868"
42546       ],
42547       [
42548         "Tunisia (‫تونس‬‎)",
42549         "tn",
42550         "216"
42551       ],
42552       [
42553         "Turkey (Türkiye)",
42554         "tr",
42555         "90"
42556       ],
42557       [
42558         "Turkmenistan",
42559         "tm",
42560         "993"
42561       ],
42562       [
42563         "Turks and Caicos Islands",
42564         "tc",
42565         "1649"
42566       ],
42567       [
42568         "Tuvalu",
42569         "tv",
42570         "688"
42571       ],
42572       [
42573         "U.S. Virgin Islands",
42574         "vi",
42575         "1340"
42576       ],
42577       [
42578         "Uganda",
42579         "ug",
42580         "256"
42581       ],
42582       [
42583         "Ukraine (Україна)",
42584         "ua",
42585         "380"
42586       ],
42587       [
42588         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42589         "ae",
42590         "971"
42591       ],
42592       [
42593         "United Kingdom",
42594         "gb",
42595         "44",
42596         0
42597       ],
42598       [
42599         "United States",
42600         "us",
42601         "1",
42602         0
42603       ],
42604       [
42605         "Uruguay",
42606         "uy",
42607         "598"
42608       ],
42609       [
42610         "Uzbekistan (Oʻzbekiston)",
42611         "uz",
42612         "998"
42613       ],
42614       [
42615         "Vanuatu",
42616         "vu",
42617         "678"
42618       ],
42619       [
42620         "Vatican City (Città del Vaticano)",
42621         "va",
42622         "39",
42623         1
42624       ],
42625       [
42626         "Venezuela",
42627         "ve",
42628         "58"
42629       ],
42630       [
42631         "Vietnam (Việt Nam)",
42632         "vn",
42633         "84"
42634       ],
42635       [
42636         "Wallis and Futuna (Wallis-et-Futuna)",
42637         "wf",
42638         "681"
42639       ],
42640       [
42641         "Western Sahara (‫الصحراء الغربية‬‎)",
42642         "eh",
42643         "212",
42644         1
42645       ],
42646       [
42647         "Yemen (‫اليمن‬‎)",
42648         "ye",
42649         "967"
42650       ],
42651       [
42652         "Zambia",
42653         "zm",
42654         "260"
42655       ],
42656       [
42657         "Zimbabwe",
42658         "zw",
42659         "263"
42660       ],
42661       [
42662         "Åland Islands",
42663         "ax",
42664         "358",
42665         1
42666       ]
42667   ];
42668   
42669   return d;
42670 }/**
42671 *    This script refer to:
42672 *    Title: International Telephone Input
42673 *    Author: Jack O'Connor
42674 *    Code version:  v12.1.12
42675 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42676 **/
42677
42678 /**
42679  * @class Roo.bootstrap.PhoneInput
42680  * @extends Roo.bootstrap.TriggerField
42681  * An input with International dial-code selection
42682  
42683  * @cfg {String} defaultDialCode default '+852'
42684  * @cfg {Array} preferedCountries default []
42685   
42686  * @constructor
42687  * Create a new PhoneInput.
42688  * @param {Object} config Configuration options
42689  */
42690
42691 Roo.bootstrap.PhoneInput = function(config) {
42692     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42693 };
42694
42695 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42696         
42697         listWidth: undefined,
42698         
42699         selectedClass: 'active',
42700         
42701         invalidClass : "has-warning",
42702         
42703         validClass: 'has-success',
42704         
42705         allowed: '0123456789',
42706         
42707         max_length: 15,
42708         
42709         /**
42710          * @cfg {String} defaultDialCode The default dial code when initializing the input
42711          */
42712         defaultDialCode: '+852',
42713         
42714         /**
42715          * @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
42716          */
42717         preferedCountries: false,
42718         
42719         getAutoCreate : function()
42720         {
42721             var data = Roo.bootstrap.PhoneInputData();
42722             var align = this.labelAlign || this.parentLabelAlign();
42723             var id = Roo.id();
42724             
42725             this.allCountries = [];
42726             this.dialCodeMapping = [];
42727             
42728             for (var i = 0; i < data.length; i++) {
42729               var c = data[i];
42730               this.allCountries[i] = {
42731                 name: c[0],
42732                 iso2: c[1],
42733                 dialCode: c[2],
42734                 priority: c[3] || 0,
42735                 areaCodes: c[4] || null
42736               };
42737               this.dialCodeMapping[c[2]] = {
42738                   name: c[0],
42739                   iso2: c[1],
42740                   priority: c[3] || 0,
42741                   areaCodes: c[4] || null
42742               };
42743             }
42744             
42745             var cfg = {
42746                 cls: 'form-group',
42747                 cn: []
42748             };
42749             
42750             var input =  {
42751                 tag: 'input',
42752                 id : id,
42753                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42754                 maxlength: this.max_length,
42755                 cls : 'form-control tel-input',
42756                 autocomplete: 'new-password'
42757             };
42758             
42759             var hiddenInput = {
42760                 tag: 'input',
42761                 type: 'hidden',
42762                 cls: 'hidden-tel-input'
42763             };
42764             
42765             if (this.name) {
42766                 hiddenInput.name = this.name;
42767             }
42768             
42769             if (this.disabled) {
42770                 input.disabled = true;
42771             }
42772             
42773             var flag_container = {
42774                 tag: 'div',
42775                 cls: 'flag-box',
42776                 cn: [
42777                     {
42778                         tag: 'div',
42779                         cls: 'flag'
42780                     },
42781                     {
42782                         tag: 'div',
42783                         cls: 'caret'
42784                     }
42785                 ]
42786             };
42787             
42788             var box = {
42789                 tag: 'div',
42790                 cls: this.hasFeedback ? 'has-feedback' : '',
42791                 cn: [
42792                     hiddenInput,
42793                     input,
42794                     {
42795                         tag: 'input',
42796                         cls: 'dial-code-holder',
42797                         disabled: true
42798                     }
42799                 ]
42800             };
42801             
42802             var container = {
42803                 cls: 'roo-select2-container input-group',
42804                 cn: [
42805                     flag_container,
42806                     box
42807                 ]
42808             };
42809             
42810             if (this.fieldLabel.length) {
42811                 var indicator = {
42812                     tag: 'i',
42813                     tooltip: 'This field is required'
42814                 };
42815                 
42816                 var label = {
42817                     tag: 'label',
42818                     'for':  id,
42819                     cls: 'control-label',
42820                     cn: []
42821                 };
42822                 
42823                 var label_text = {
42824                     tag: 'span',
42825                     html: this.fieldLabel
42826                 };
42827                 
42828                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42829                 label.cn = [
42830                     indicator,
42831                     label_text
42832                 ];
42833                 
42834                 if(this.indicatorpos == 'right') {
42835                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42836                     label.cn = [
42837                         label_text,
42838                         indicator
42839                     ];
42840                 }
42841                 
42842                 if(align == 'left') {
42843                     container = {
42844                         tag: 'div',
42845                         cn: [
42846                             container
42847                         ]
42848                     };
42849                     
42850                     if(this.labelWidth > 12){
42851                         label.style = "width: " + this.labelWidth + 'px';
42852                     }
42853                     if(this.labelWidth < 13 && this.labelmd == 0){
42854                         this.labelmd = this.labelWidth;
42855                     }
42856                     if(this.labellg > 0){
42857                         label.cls += ' col-lg-' + this.labellg;
42858                         input.cls += ' col-lg-' + (12 - this.labellg);
42859                     }
42860                     if(this.labelmd > 0){
42861                         label.cls += ' col-md-' + this.labelmd;
42862                         container.cls += ' col-md-' + (12 - this.labelmd);
42863                     }
42864                     if(this.labelsm > 0){
42865                         label.cls += ' col-sm-' + this.labelsm;
42866                         container.cls += ' col-sm-' + (12 - this.labelsm);
42867                     }
42868                     if(this.labelxs > 0){
42869                         label.cls += ' col-xs-' + this.labelxs;
42870                         container.cls += ' col-xs-' + (12 - this.labelxs);
42871                     }
42872                 }
42873             }
42874             
42875             cfg.cn = [
42876                 label,
42877                 container
42878             ];
42879             
42880             var settings = this;
42881             
42882             ['xs','sm','md','lg'].map(function(size){
42883                 if (settings[size]) {
42884                     cfg.cls += ' col-' + size + '-' + settings[size];
42885                 }
42886             });
42887             
42888             this.store = new Roo.data.Store({
42889                 proxy : new Roo.data.MemoryProxy({}),
42890                 reader : new Roo.data.JsonReader({
42891                     fields : [
42892                         {
42893                             'name' : 'name',
42894                             'type' : 'string'
42895                         },
42896                         {
42897                             'name' : 'iso2',
42898                             'type' : 'string'
42899                         },
42900                         {
42901                             'name' : 'dialCode',
42902                             'type' : 'string'
42903                         },
42904                         {
42905                             'name' : 'priority',
42906                             'type' : 'string'
42907                         },
42908                         {
42909                             'name' : 'areaCodes',
42910                             'type' : 'string'
42911                         }
42912                     ]
42913                 })
42914             });
42915             
42916             if(!this.preferedCountries) {
42917                 this.preferedCountries = [
42918                     'hk',
42919                     'gb',
42920                     'us'
42921                 ];
42922             }
42923             
42924             var p = this.preferedCountries.reverse();
42925             
42926             if(p) {
42927                 for (var i = 0; i < p.length; i++) {
42928                     for (var j = 0; j < this.allCountries.length; j++) {
42929                         if(this.allCountries[j].iso2 == p[i]) {
42930                             var t = this.allCountries[j];
42931                             this.allCountries.splice(j,1);
42932                             this.allCountries.unshift(t);
42933                         }
42934                     } 
42935                 }
42936             }
42937             
42938             this.store.proxy.data = {
42939                 success: true,
42940                 data: this.allCountries
42941             };
42942             
42943             return cfg;
42944         },
42945         
42946         initEvents : function()
42947         {
42948             this.createList();
42949             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42950             
42951             this.indicator = this.indicatorEl();
42952             this.flag = this.flagEl();
42953             this.dialCodeHolder = this.dialCodeHolderEl();
42954             
42955             this.trigger = this.el.select('div.flag-box',true).first();
42956             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42957             
42958             var _this = this;
42959             
42960             (function(){
42961                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42962                 _this.list.setWidth(lw);
42963             }).defer(100);
42964             
42965             this.list.on('mouseover', this.onViewOver, this);
42966             this.list.on('mousemove', this.onViewMove, this);
42967             this.inputEl().on("keyup", this.onKeyUp, this);
42968             this.inputEl().on("keypress", this.onKeyPress, this);
42969             
42970             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42971
42972             this.view = new Roo.View(this.list, this.tpl, {
42973                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42974             });
42975             
42976             this.view.on('click', this.onViewClick, this);
42977             this.setValue(this.defaultDialCode);
42978         },
42979         
42980         onTriggerClick : function(e)
42981         {
42982             Roo.log('trigger click');
42983             if(this.disabled){
42984                 return;
42985             }
42986             
42987             if(this.isExpanded()){
42988                 this.collapse();
42989                 this.hasFocus = false;
42990             }else {
42991                 this.store.load({});
42992                 this.hasFocus = true;
42993                 this.expand();
42994             }
42995         },
42996         
42997         isExpanded : function()
42998         {
42999             return this.list.isVisible();
43000         },
43001         
43002         collapse : function()
43003         {
43004             if(!this.isExpanded()){
43005                 return;
43006             }
43007             this.list.hide();
43008             Roo.get(document).un('mousedown', this.collapseIf, this);
43009             Roo.get(document).un('mousewheel', this.collapseIf, this);
43010             this.fireEvent('collapse', this);
43011             this.validate();
43012         },
43013         
43014         expand : function()
43015         {
43016             Roo.log('expand');
43017
43018             if(this.isExpanded() || !this.hasFocus){
43019                 return;
43020             }
43021             
43022             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43023             this.list.setWidth(lw);
43024             
43025             this.list.show();
43026             this.restrictHeight();
43027             
43028             Roo.get(document).on('mousedown', this.collapseIf, this);
43029             Roo.get(document).on('mousewheel', this.collapseIf, this);
43030             
43031             this.fireEvent('expand', this);
43032         },
43033         
43034         restrictHeight : function()
43035         {
43036             this.list.alignTo(this.inputEl(), this.listAlign);
43037             this.list.alignTo(this.inputEl(), this.listAlign);
43038         },
43039         
43040         onViewOver : function(e, t)
43041         {
43042             if(this.inKeyMode){
43043                 return;
43044             }
43045             var item = this.view.findItemFromChild(t);
43046             
43047             if(item){
43048                 var index = this.view.indexOf(item);
43049                 this.select(index, false);
43050             }
43051         },
43052
43053         // private
43054         onViewClick : function(view, doFocus, el, e)
43055         {
43056             var index = this.view.getSelectedIndexes()[0];
43057             
43058             var r = this.store.getAt(index);
43059             
43060             if(r){
43061                 this.onSelect(r, index);
43062             }
43063             if(doFocus !== false && !this.blockFocus){
43064                 this.inputEl().focus();
43065             }
43066         },
43067         
43068         onViewMove : function(e, t)
43069         {
43070             this.inKeyMode = false;
43071         },
43072         
43073         select : function(index, scrollIntoView)
43074         {
43075             this.selectedIndex = index;
43076             this.view.select(index);
43077             if(scrollIntoView !== false){
43078                 var el = this.view.getNode(index);
43079                 if(el){
43080                     this.list.scrollChildIntoView(el, false);
43081                 }
43082             }
43083         },
43084         
43085         createList : function()
43086         {
43087             this.list = Roo.get(document.body).createChild({
43088                 tag: 'ul',
43089                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43090                 style: 'display:none'
43091             });
43092             
43093             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43094         },
43095         
43096         collapseIf : function(e)
43097         {
43098             var in_combo  = e.within(this.el);
43099             var in_list =  e.within(this.list);
43100             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43101             
43102             if (in_combo || in_list || is_list) {
43103                 return;
43104             }
43105             this.collapse();
43106         },
43107         
43108         onSelect : function(record, index)
43109         {
43110             if(this.fireEvent('beforeselect', this, record, index) !== false){
43111                 
43112                 this.setFlagClass(record.data.iso2);
43113                 this.setDialCode(record.data.dialCode);
43114                 this.hasFocus = false;
43115                 this.collapse();
43116                 this.fireEvent('select', this, record, index);
43117             }
43118         },
43119         
43120         flagEl : function()
43121         {
43122             var flag = this.el.select('div.flag',true).first();
43123             if(!flag){
43124                 return false;
43125             }
43126             return flag;
43127         },
43128         
43129         dialCodeHolderEl : function()
43130         {
43131             var d = this.el.select('input.dial-code-holder',true).first();
43132             if(!d){
43133                 return false;
43134             }
43135             return d;
43136         },
43137         
43138         setDialCode : function(v)
43139         {
43140             this.dialCodeHolder.dom.value = '+'+v;
43141         },
43142         
43143         setFlagClass : function(n)
43144         {
43145             this.flag.dom.className = 'flag '+n;
43146         },
43147         
43148         getValue : function()
43149         {
43150             var v = this.inputEl().getValue();
43151             if(this.dialCodeHolder) {
43152                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43153             }
43154             return v;
43155         },
43156         
43157         setValue : function(v)
43158         {
43159             var d = this.getDialCode(v);
43160             
43161             //invalid dial code
43162             if(v.length == 0 || !d || d.length == 0) {
43163                 if(this.rendered){
43164                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43165                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43166                 }
43167                 return;
43168             }
43169             
43170             //valid dial code
43171             this.setFlagClass(this.dialCodeMapping[d].iso2);
43172             this.setDialCode(d);
43173             this.inputEl().dom.value = v.replace('+'+d,'');
43174             this.hiddenEl().dom.value = this.getValue();
43175             
43176             this.validate();
43177         },
43178         
43179         getDialCode : function(v)
43180         {
43181             v = v ||  '';
43182             
43183             if (v.length == 0) {
43184                 return this.dialCodeHolder.dom.value;
43185             }
43186             
43187             var dialCode = "";
43188             if (v.charAt(0) != "+") {
43189                 return false;
43190             }
43191             var numericChars = "";
43192             for (var i = 1; i < v.length; i++) {
43193               var c = v.charAt(i);
43194               if (!isNaN(c)) {
43195                 numericChars += c;
43196                 if (this.dialCodeMapping[numericChars]) {
43197                   dialCode = v.substr(1, i);
43198                 }
43199                 if (numericChars.length == 4) {
43200                   break;
43201                 }
43202               }
43203             }
43204             return dialCode;
43205         },
43206         
43207         reset : function()
43208         {
43209             this.setValue(this.defaultDialCode);
43210             this.validate();
43211         },
43212         
43213         hiddenEl : function()
43214         {
43215             return this.el.select('input.hidden-tel-input',true).first();
43216         },
43217         
43218         // after setting val
43219         onKeyUp : function(e){
43220             this.setValue(this.getValue());
43221         },
43222         
43223         onKeyPress : function(e){
43224             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43225                 e.stopEvent();
43226             }
43227         }
43228         
43229 });
43230 /**
43231  * @class Roo.bootstrap.MoneyField
43232  * @extends Roo.bootstrap.ComboBox
43233  * Bootstrap MoneyField class
43234  * 
43235  * @constructor
43236  * Create a new MoneyField.
43237  * @param {Object} config Configuration options
43238  */
43239
43240 Roo.bootstrap.MoneyField = function(config) {
43241     
43242     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43243     
43244 };
43245
43246 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43247     
43248     /**
43249      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43250      */
43251     allowDecimals : true,
43252     /**
43253      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43254      */
43255     decimalSeparator : ".",
43256     /**
43257      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43258      */
43259     decimalPrecision : 0,
43260     /**
43261      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43262      */
43263     allowNegative : true,
43264     /**
43265      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43266      */
43267     allowZero: true,
43268     /**
43269      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43270      */
43271     minValue : Number.NEGATIVE_INFINITY,
43272     /**
43273      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43274      */
43275     maxValue : Number.MAX_VALUE,
43276     /**
43277      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43278      */
43279     minText : "The minimum value for this field is {0}",
43280     /**
43281      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43282      */
43283     maxText : "The maximum value for this field is {0}",
43284     /**
43285      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43286      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43287      */
43288     nanText : "{0} is not a valid number",
43289     /**
43290      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43291      */
43292     castInt : true,
43293     /**
43294      * @cfg {String} defaults currency of the MoneyField
43295      * value should be in lkey
43296      */
43297     defaultCurrency : false,
43298     /**
43299      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43300      */
43301     thousandsDelimiter : false,
43302     /**
43303      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43304      */
43305     max_length: false,
43306     
43307     inputlg : 9,
43308     inputmd : 9,
43309     inputsm : 9,
43310     inputxs : 6,
43311     
43312     store : false,
43313     
43314     getAutoCreate : function()
43315     {
43316         var align = this.labelAlign || this.parentLabelAlign();
43317         
43318         var id = Roo.id();
43319
43320         var cfg = {
43321             cls: 'form-group',
43322             cn: []
43323         };
43324
43325         var input =  {
43326             tag: 'input',
43327             id : id,
43328             cls : 'form-control roo-money-amount-input',
43329             autocomplete: 'new-password'
43330         };
43331         
43332         var hiddenInput = {
43333             tag: 'input',
43334             type: 'hidden',
43335             id: Roo.id(),
43336             cls: 'hidden-number-input'
43337         };
43338         
43339         if(this.max_length) {
43340             input.maxlength = this.max_length; 
43341         }
43342         
43343         if (this.name) {
43344             hiddenInput.name = this.name;
43345         }
43346
43347         if (this.disabled) {
43348             input.disabled = true;
43349         }
43350
43351         var clg = 12 - this.inputlg;
43352         var cmd = 12 - this.inputmd;
43353         var csm = 12 - this.inputsm;
43354         var cxs = 12 - this.inputxs;
43355         
43356         var container = {
43357             tag : 'div',
43358             cls : 'row roo-money-field',
43359             cn : [
43360                 {
43361                     tag : 'div',
43362                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43363                     cn : [
43364                         {
43365                             tag : 'div',
43366                             cls: 'roo-select2-container input-group',
43367                             cn: [
43368                                 {
43369                                     tag : 'input',
43370                                     cls : 'form-control roo-money-currency-input',
43371                                     autocomplete: 'new-password',
43372                                     readOnly : 1,
43373                                     name : this.currencyName
43374                                 },
43375                                 {
43376                                     tag :'span',
43377                                     cls : 'input-group-addon',
43378                                     cn : [
43379                                         {
43380                                             tag: 'span',
43381                                             cls: 'caret'
43382                                         }
43383                                     ]
43384                                 }
43385                             ]
43386                         }
43387                     ]
43388                 },
43389                 {
43390                     tag : 'div',
43391                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43392                     cn : [
43393                         {
43394                             tag: 'div',
43395                             cls: this.hasFeedback ? 'has-feedback' : '',
43396                             cn: [
43397                                 input
43398                             ]
43399                         }
43400                     ]
43401                 }
43402             ]
43403             
43404         };
43405         
43406         if (this.fieldLabel.length) {
43407             var indicator = {
43408                 tag: 'i',
43409                 tooltip: 'This field is required'
43410             };
43411
43412             var label = {
43413                 tag: 'label',
43414                 'for':  id,
43415                 cls: 'control-label',
43416                 cn: []
43417             };
43418
43419             var label_text = {
43420                 tag: 'span',
43421                 html: this.fieldLabel
43422             };
43423
43424             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43425             label.cn = [
43426                 indicator,
43427                 label_text
43428             ];
43429
43430             if(this.indicatorpos == 'right') {
43431                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43432                 label.cn = [
43433                     label_text,
43434                     indicator
43435                 ];
43436             }
43437
43438             if(align == 'left') {
43439                 container = {
43440                     tag: 'div',
43441                     cn: [
43442                         container
43443                     ]
43444                 };
43445
43446                 if(this.labelWidth > 12){
43447                     label.style = "width: " + this.labelWidth + 'px';
43448                 }
43449                 if(this.labelWidth < 13 && this.labelmd == 0){
43450                     this.labelmd = this.labelWidth;
43451                 }
43452                 if(this.labellg > 0){
43453                     label.cls += ' col-lg-' + this.labellg;
43454                     input.cls += ' col-lg-' + (12 - this.labellg);
43455                 }
43456                 if(this.labelmd > 0){
43457                     label.cls += ' col-md-' + this.labelmd;
43458                     container.cls += ' col-md-' + (12 - this.labelmd);
43459                 }
43460                 if(this.labelsm > 0){
43461                     label.cls += ' col-sm-' + this.labelsm;
43462                     container.cls += ' col-sm-' + (12 - this.labelsm);
43463                 }
43464                 if(this.labelxs > 0){
43465                     label.cls += ' col-xs-' + this.labelxs;
43466                     container.cls += ' col-xs-' + (12 - this.labelxs);
43467                 }
43468             }
43469         }
43470
43471         cfg.cn = [
43472             label,
43473             container,
43474             hiddenInput
43475         ];
43476         
43477         var settings = this;
43478
43479         ['xs','sm','md','lg'].map(function(size){
43480             if (settings[size]) {
43481                 cfg.cls += ' col-' + size + '-' + settings[size];
43482             }
43483         });
43484         
43485         return cfg;
43486     },
43487     
43488     initEvents : function()
43489     {
43490         this.indicator = this.indicatorEl();
43491         
43492         this.initCurrencyEvent();
43493         
43494         this.initNumberEvent();
43495     },
43496     
43497     initCurrencyEvent : function()
43498     {
43499         if (!this.store) {
43500             throw "can not find store for combo";
43501         }
43502         
43503         this.store = Roo.factory(this.store, Roo.data);
43504         this.store.parent = this;
43505         
43506         this.createList();
43507         
43508         this.triggerEl = this.el.select('.input-group-addon', true).first();
43509         
43510         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43511         
43512         var _this = this;
43513         
43514         (function(){
43515             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43516             _this.list.setWidth(lw);
43517         }).defer(100);
43518         
43519         this.list.on('mouseover', this.onViewOver, this);
43520         this.list.on('mousemove', this.onViewMove, this);
43521         this.list.on('scroll', this.onViewScroll, this);
43522         
43523         if(!this.tpl){
43524             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43525         }
43526         
43527         this.view = new Roo.View(this.list, this.tpl, {
43528             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43529         });
43530         
43531         this.view.on('click', this.onViewClick, this);
43532         
43533         this.store.on('beforeload', this.onBeforeLoad, this);
43534         this.store.on('load', this.onLoad, this);
43535         this.store.on('loadexception', this.onLoadException, this);
43536         
43537         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43538             "up" : function(e){
43539                 this.inKeyMode = true;
43540                 this.selectPrev();
43541             },
43542
43543             "down" : function(e){
43544                 if(!this.isExpanded()){
43545                     this.onTriggerClick();
43546                 }else{
43547                     this.inKeyMode = true;
43548                     this.selectNext();
43549                 }
43550             },
43551
43552             "enter" : function(e){
43553                 this.collapse();
43554                 
43555                 if(this.fireEvent("specialkey", this, e)){
43556                     this.onViewClick(false);
43557                 }
43558                 
43559                 return true;
43560             },
43561
43562             "esc" : function(e){
43563                 this.collapse();
43564             },
43565
43566             "tab" : function(e){
43567                 this.collapse();
43568                 
43569                 if(this.fireEvent("specialkey", this, e)){
43570                     this.onViewClick(false);
43571                 }
43572                 
43573                 return true;
43574             },
43575
43576             scope : this,
43577
43578             doRelay : function(foo, bar, hname){
43579                 if(hname == 'down' || this.scope.isExpanded()){
43580                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43581                 }
43582                 return true;
43583             },
43584
43585             forceKeyDown: true
43586         });
43587         
43588         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43589         
43590     },
43591     
43592     initNumberEvent : function(e)
43593     {
43594         this.inputEl().on("keydown" , this.fireKey,  this);
43595         this.inputEl().on("focus", this.onFocus,  this);
43596         this.inputEl().on("blur", this.onBlur,  this);
43597         
43598         this.inputEl().relayEvent('keyup', this);
43599         
43600         if(this.indicator){
43601             this.indicator.addClass('invisible');
43602         }
43603  
43604         this.originalValue = this.getValue();
43605         
43606         if(this.validationEvent == 'keyup'){
43607             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43608             this.inputEl().on('keyup', this.filterValidation, this);
43609         }
43610         else if(this.validationEvent !== false){
43611             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43612         }
43613         
43614         if(this.selectOnFocus){
43615             this.on("focus", this.preFocus, this);
43616             
43617         }
43618         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43619             this.inputEl().on("keypress", this.filterKeys, this);
43620         } else {
43621             this.inputEl().relayEvent('keypress', this);
43622         }
43623         
43624         var allowed = "0123456789";
43625         
43626         if(this.allowDecimals){
43627             allowed += this.decimalSeparator;
43628         }
43629         
43630         if(this.allowNegative){
43631             allowed += "-";
43632         }
43633         
43634         if(this.thousandsDelimiter) {
43635             allowed += ",";
43636         }
43637         
43638         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43639         
43640         var keyPress = function(e){
43641             
43642             var k = e.getKey();
43643             
43644             var c = e.getCharCode();
43645             
43646             if(
43647                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43648                     allowed.indexOf(String.fromCharCode(c)) === -1
43649             ){
43650                 e.stopEvent();
43651                 return;
43652             }
43653             
43654             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43655                 return;
43656             }
43657             
43658             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43659                 e.stopEvent();
43660             }
43661         };
43662         
43663         this.inputEl().on("keypress", keyPress, this);
43664         
43665     },
43666     
43667     onTriggerClick : function(e)
43668     {   
43669         if(this.disabled){
43670             return;
43671         }
43672         
43673         this.page = 0;
43674         this.loadNext = false;
43675         
43676         if(this.isExpanded()){
43677             this.collapse();
43678             return;
43679         }
43680         
43681         this.hasFocus = true;
43682         
43683         if(this.triggerAction == 'all') {
43684             this.doQuery(this.allQuery, true);
43685             return;
43686         }
43687         
43688         this.doQuery(this.getRawValue());
43689     },
43690     
43691     getCurrency : function()
43692     {   
43693         var v = this.currencyEl().getValue();
43694         
43695         return v;
43696     },
43697     
43698     restrictHeight : function()
43699     {
43700         this.list.alignTo(this.currencyEl(), this.listAlign);
43701         this.list.alignTo(this.currencyEl(), this.listAlign);
43702     },
43703     
43704     onViewClick : function(view, doFocus, el, e)
43705     {
43706         var index = this.view.getSelectedIndexes()[0];
43707         
43708         var r = this.store.getAt(index);
43709         
43710         if(r){
43711             this.onSelect(r, index);
43712         }
43713     },
43714     
43715     onSelect : function(record, index){
43716         
43717         if(this.fireEvent('beforeselect', this, record, index) !== false){
43718         
43719             this.setFromCurrencyData(index > -1 ? record.data : false);
43720             
43721             this.collapse();
43722             
43723             this.fireEvent('select', this, record, index);
43724         }
43725     },
43726     
43727     setFromCurrencyData : function(o)
43728     {
43729         var currency = '';
43730         
43731         this.lastCurrency = o;
43732         
43733         if (this.currencyField) {
43734             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43735         } else {
43736             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43737         }
43738         
43739         this.lastSelectionText = currency;
43740         
43741         //setting default currency
43742         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43743             this.setCurrency(this.defaultCurrency);
43744             return;
43745         }
43746         
43747         this.setCurrency(currency);
43748     },
43749     
43750     setFromData : function(o)
43751     {
43752         var c = {};
43753         
43754         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43755         
43756         this.setFromCurrencyData(c);
43757         
43758         var value = '';
43759         
43760         if (this.name) {
43761             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43762         } else {
43763             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43764         }
43765         
43766         this.setValue(value);
43767         
43768     },
43769     
43770     setCurrency : function(v)
43771     {   
43772         this.currencyValue = v;
43773         
43774         if(this.rendered){
43775             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43776             this.validate();
43777         }
43778     },
43779     
43780     setValue : function(v)
43781     {
43782         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43783         
43784         this.value = v;
43785         
43786         if(this.rendered){
43787             
43788             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43789             
43790             this.inputEl().dom.value = (v == '') ? '' :
43791                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43792             
43793             if(!this.allowZero && v === '0') {
43794                 this.hiddenEl().dom.value = '';
43795                 this.inputEl().dom.value = '';
43796             }
43797             
43798             this.validate();
43799         }
43800     },
43801     
43802     getRawValue : function()
43803     {
43804         var v = this.inputEl().getValue();
43805         
43806         return v;
43807     },
43808     
43809     getValue : function()
43810     {
43811         return this.fixPrecision(this.parseValue(this.getRawValue()));
43812     },
43813     
43814     parseValue : function(value)
43815     {
43816         if(this.thousandsDelimiter) {
43817             value += "";
43818             r = new RegExp(",", "g");
43819             value = value.replace(r, "");
43820         }
43821         
43822         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43823         return isNaN(value) ? '' : value;
43824         
43825     },
43826     
43827     fixPrecision : function(value)
43828     {
43829         if(this.thousandsDelimiter) {
43830             value += "";
43831             r = new RegExp(",", "g");
43832             value = value.replace(r, "");
43833         }
43834         
43835         var nan = isNaN(value);
43836         
43837         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43838             return nan ? '' : value;
43839         }
43840         return parseFloat(value).toFixed(this.decimalPrecision);
43841     },
43842     
43843     decimalPrecisionFcn : function(v)
43844     {
43845         return Math.floor(v);
43846     },
43847     
43848     validateValue : function(value)
43849     {
43850         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43851             return false;
43852         }
43853         
43854         var num = this.parseValue(value);
43855         
43856         if(isNaN(num)){
43857             this.markInvalid(String.format(this.nanText, value));
43858             return false;
43859         }
43860         
43861         if(num < this.minValue){
43862             this.markInvalid(String.format(this.minText, this.minValue));
43863             return false;
43864         }
43865         
43866         if(num > this.maxValue){
43867             this.markInvalid(String.format(this.maxText, this.maxValue));
43868             return false;
43869         }
43870         
43871         return true;
43872     },
43873     
43874     validate : function()
43875     {
43876         if(this.disabled || this.allowBlank){
43877             this.markValid();
43878             return true;
43879         }
43880         
43881         var currency = this.getCurrency();
43882         
43883         if(this.validateValue(this.getRawValue()) && currency.length){
43884             this.markValid();
43885             return true;
43886         }
43887         
43888         this.markInvalid();
43889         return false;
43890     },
43891     
43892     getName: function()
43893     {
43894         return this.name;
43895     },
43896     
43897     beforeBlur : function()
43898     {
43899         if(!this.castInt){
43900             return;
43901         }
43902         
43903         var v = this.parseValue(this.getRawValue());
43904         
43905         if(v || v == 0){
43906             this.setValue(v);
43907         }
43908     },
43909     
43910     onBlur : function()
43911     {
43912         this.beforeBlur();
43913         
43914         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43915             //this.el.removeClass(this.focusClass);
43916         }
43917         
43918         this.hasFocus = false;
43919         
43920         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43921             this.validate();
43922         }
43923         
43924         var v = this.getValue();
43925         
43926         if(String(v) !== String(this.startValue)){
43927             this.fireEvent('change', this, v, this.startValue);
43928         }
43929         
43930         this.fireEvent("blur", this);
43931     },
43932     
43933     inputEl : function()
43934     {
43935         return this.el.select('.roo-money-amount-input', true).first();
43936     },
43937     
43938     currencyEl : function()
43939     {
43940         return this.el.select('.roo-money-currency-input', true).first();
43941     },
43942     
43943     hiddenEl : function()
43944     {
43945         return this.el.select('input.hidden-number-input',true).first();
43946     }
43947     
43948 });/**
43949  * @class Roo.bootstrap.BezierSignature
43950  * @extends Roo.bootstrap.Component
43951  * Bootstrap BezierSignature class
43952  * This script refer to:
43953  *    Title: Signature Pad
43954  *    Author: szimek
43955  *    Availability: https://github.com/szimek/signature_pad
43956  *
43957  * @constructor
43958  * Create a new BezierSignature
43959  * @param {Object} config The config object
43960  */
43961
43962 Roo.bootstrap.BezierSignature = function(config){
43963     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43964     this.addEvents({
43965         "resize" : true
43966     });
43967 };
43968
43969 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43970 {
43971      
43972     curve_data: [],
43973     
43974     is_empty: true,
43975     
43976     mouse_btn_down: true,
43977     
43978     /**
43979      * @cfg {int} canvas height
43980      */
43981     canvas_height: '200px',
43982     
43983     /**
43984      * @cfg {float|function} Radius of a single dot.
43985      */ 
43986     dot_size: false,
43987     
43988     /**
43989      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43990      */
43991     min_width: 0.5,
43992     
43993     /**
43994      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43995      */
43996     max_width: 2.5,
43997     
43998     /**
43999      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44000      */
44001     throttle: 16,
44002     
44003     /**
44004      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44005      */
44006     min_distance: 5,
44007     
44008     /**
44009      * @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.
44010      */
44011     bg_color: 'rgba(0, 0, 0, 0)',
44012     
44013     /**
44014      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44015      */
44016     dot_color: 'black',
44017     
44018     /**
44019      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44020      */ 
44021     velocity_filter_weight: 0.7,
44022     
44023     /**
44024      * @cfg {function} Callback when stroke begin. 
44025      */
44026     onBegin: false,
44027     
44028     /**
44029      * @cfg {function} Callback when stroke end.
44030      */
44031     onEnd: false,
44032     
44033     getAutoCreate : function()
44034     {
44035         var cls = 'roo-signature column';
44036         
44037         if(this.cls){
44038             cls += ' ' + this.cls;
44039         }
44040         
44041         var col_sizes = [
44042             'lg',
44043             'md',
44044             'sm',
44045             'xs'
44046         ];
44047         
44048         for(var i = 0; i < col_sizes.length; i++) {
44049             if(this[col_sizes[i]]) {
44050                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44051             }
44052         }
44053         
44054         var cfg = {
44055             tag: 'div',
44056             cls: cls,
44057             cn: [
44058                 {
44059                     tag: 'div',
44060                     cls: 'roo-signature-body',
44061                     cn: [
44062                         {
44063                             tag: 'canvas',
44064                             cls: 'roo-signature-body-canvas',
44065                             height: this.canvas_height,
44066                             width: this.canvas_width
44067                         }
44068                     ]
44069                 },
44070                 {
44071                     tag: 'input',
44072                     type: 'file',
44073                     style: 'display: none'
44074                 }
44075             ]
44076         };
44077         
44078         return cfg;
44079     },
44080     
44081     initEvents: function() 
44082     {
44083         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44084         
44085         var canvas = this.canvasEl();
44086         
44087         // mouse && touch event swapping...
44088         canvas.dom.style.touchAction = 'none';
44089         canvas.dom.style.msTouchAction = 'none';
44090         
44091         this.mouse_btn_down = false;
44092         canvas.on('mousedown', this._handleMouseDown, this);
44093         canvas.on('mousemove', this._handleMouseMove, this);
44094         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44095         
44096         if (window.PointerEvent) {
44097             canvas.on('pointerdown', this._handleMouseDown, this);
44098             canvas.on('pointermove', this._handleMouseMove, this);
44099             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44100         }
44101         
44102         if ('ontouchstart' in window) {
44103             canvas.on('touchstart', this._handleTouchStart, this);
44104             canvas.on('touchmove', this._handleTouchMove, this);
44105             canvas.on('touchend', this._handleTouchEnd, this);
44106         }
44107         
44108         Roo.EventManager.onWindowResize(this.resize, this, true);
44109         
44110         // file input event
44111         this.fileEl().on('change', this.uploadImage, this);
44112         
44113         this.clear();
44114         
44115         this.resize();
44116     },
44117     
44118     resize: function(){
44119         
44120         var canvas = this.canvasEl().dom;
44121         var ctx = this.canvasElCtx();
44122         var img_data = false;
44123         
44124         if(canvas.width > 0) {
44125             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44126         }
44127         // setting canvas width will clean img data
44128         canvas.width = 0;
44129         
44130         var style = window.getComputedStyle ? 
44131             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44132             
44133         var padding_left = parseInt(style.paddingLeft) || 0;
44134         var padding_right = parseInt(style.paddingRight) || 0;
44135         
44136         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44137         
44138         if(img_data) {
44139             ctx.putImageData(img_data, 0, 0);
44140         }
44141     },
44142     
44143     _handleMouseDown: function(e)
44144     {
44145         if (e.browserEvent.which === 1) {
44146             this.mouse_btn_down = true;
44147             this.strokeBegin(e);
44148         }
44149     },
44150     
44151     _handleMouseMove: function (e)
44152     {
44153         if (this.mouse_btn_down) {
44154             this.strokeMoveUpdate(e);
44155         }
44156     },
44157     
44158     _handleMouseUp: function (e)
44159     {
44160         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44161             this.mouse_btn_down = false;
44162             this.strokeEnd(e);
44163         }
44164     },
44165     
44166     _handleTouchStart: function (e) {
44167         
44168         e.preventDefault();
44169         if (e.browserEvent.targetTouches.length === 1) {
44170             // var touch = e.browserEvent.changedTouches[0];
44171             // this.strokeBegin(touch);
44172             
44173              this.strokeBegin(e); // assume e catching the correct xy...
44174         }
44175     },
44176     
44177     _handleTouchMove: function (e) {
44178         e.preventDefault();
44179         // var touch = event.targetTouches[0];
44180         // _this._strokeMoveUpdate(touch);
44181         this.strokeMoveUpdate(e);
44182     },
44183     
44184     _handleTouchEnd: function (e) {
44185         var wasCanvasTouched = e.target === this.canvasEl().dom;
44186         if (wasCanvasTouched) {
44187             e.preventDefault();
44188             // var touch = event.changedTouches[0];
44189             // _this._strokeEnd(touch);
44190             this.strokeEnd(e);
44191         }
44192     },
44193     
44194     reset: function () {
44195         this._lastPoints = [];
44196         this._lastVelocity = 0;
44197         this._lastWidth = (this.min_width + this.max_width) / 2;
44198         this.canvasElCtx().fillStyle = this.dot_color;
44199     },
44200     
44201     strokeMoveUpdate: function(e)
44202     {
44203         this.strokeUpdate(e);
44204         
44205         if (this.throttle) {
44206             this.throttleStroke(this.strokeUpdate, this.throttle);
44207         }
44208         else {
44209             this.strokeUpdate(e);
44210         }
44211     },
44212     
44213     strokeBegin: function(e)
44214     {
44215         var newPointGroup = {
44216             color: this.dot_color,
44217             points: []
44218         };
44219         
44220         if (typeof this.onBegin === 'function') {
44221             this.onBegin(e);
44222         }
44223         
44224         this.curve_data.push(newPointGroup);
44225         this.reset();
44226         this.strokeUpdate(e);
44227     },
44228     
44229     strokeUpdate: function(e)
44230     {
44231         var rect = this.canvasEl().dom.getBoundingClientRect();
44232         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44233         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44234         var lastPoints = lastPointGroup.points;
44235         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44236         var isLastPointTooClose = lastPoint
44237             ? point.distanceTo(lastPoint) <= this.min_distance
44238             : false;
44239         var color = lastPointGroup.color;
44240         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44241             var curve = this.addPoint(point);
44242             if (!lastPoint) {
44243                 this.drawDot({color: color, point: point});
44244             }
44245             else if (curve) {
44246                 this.drawCurve({color: color, curve: curve});
44247             }
44248             lastPoints.push({
44249                 time: point.time,
44250                 x: point.x,
44251                 y: point.y
44252             });
44253         }
44254     },
44255     
44256     strokeEnd: function(e)
44257     {
44258         this.strokeUpdate(e);
44259         if (typeof this.onEnd === 'function') {
44260             this.onEnd(e);
44261         }
44262     },
44263     
44264     addPoint:  function (point) {
44265         var _lastPoints = this._lastPoints;
44266         _lastPoints.push(point);
44267         if (_lastPoints.length > 2) {
44268             if (_lastPoints.length === 3) {
44269                 _lastPoints.unshift(_lastPoints[0]);
44270             }
44271             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44272             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44273             _lastPoints.shift();
44274             return curve;
44275         }
44276         return null;
44277     },
44278     
44279     calculateCurveWidths: function (startPoint, endPoint) {
44280         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44281             (1 - this.velocity_filter_weight) * this._lastVelocity;
44282
44283         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44284         var widths = {
44285             end: newWidth,
44286             start: this._lastWidth
44287         };
44288         
44289         this._lastVelocity = velocity;
44290         this._lastWidth = newWidth;
44291         return widths;
44292     },
44293     
44294     drawDot: function (_a) {
44295         var color = _a.color, point = _a.point;
44296         var ctx = this.canvasElCtx();
44297         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44298         ctx.beginPath();
44299         this.drawCurveSegment(point.x, point.y, width);
44300         ctx.closePath();
44301         ctx.fillStyle = color;
44302         ctx.fill();
44303     },
44304     
44305     drawCurve: function (_a) {
44306         var color = _a.color, curve = _a.curve;
44307         var ctx = this.canvasElCtx();
44308         var widthDelta = curve.endWidth - curve.startWidth;
44309         var drawSteps = Math.floor(curve.length()) * 2;
44310         ctx.beginPath();
44311         ctx.fillStyle = color;
44312         for (var i = 0; i < drawSteps; i += 1) {
44313         var t = i / drawSteps;
44314         var tt = t * t;
44315         var ttt = tt * t;
44316         var u = 1 - t;
44317         var uu = u * u;
44318         var uuu = uu * u;
44319         var x = uuu * curve.startPoint.x;
44320         x += 3 * uu * t * curve.control1.x;
44321         x += 3 * u * tt * curve.control2.x;
44322         x += ttt * curve.endPoint.x;
44323         var y = uuu * curve.startPoint.y;
44324         y += 3 * uu * t * curve.control1.y;
44325         y += 3 * u * tt * curve.control2.y;
44326         y += ttt * curve.endPoint.y;
44327         var width = curve.startWidth + ttt * widthDelta;
44328         this.drawCurveSegment(x, y, width);
44329         }
44330         ctx.closePath();
44331         ctx.fill();
44332     },
44333     
44334     drawCurveSegment: function (x, y, width) {
44335         var ctx = this.canvasElCtx();
44336         ctx.moveTo(x, y);
44337         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44338         this.is_empty = false;
44339     },
44340     
44341     clear: function()
44342     {
44343         var ctx = this.canvasElCtx();
44344         var canvas = this.canvasEl().dom;
44345         ctx.fillStyle = this.bg_color;
44346         ctx.clearRect(0, 0, canvas.width, canvas.height);
44347         ctx.fillRect(0, 0, canvas.width, canvas.height);
44348         this.curve_data = [];
44349         this.reset();
44350         this.is_empty = true;
44351     },
44352     
44353     fileEl: function()
44354     {
44355         return  this.el.select('input',true).first();
44356     },
44357     
44358     canvasEl: function()
44359     {
44360         return this.el.select('canvas',true).first();
44361     },
44362     
44363     canvasElCtx: function()
44364     {
44365         return this.el.select('canvas',true).first().dom.getContext('2d');
44366     },
44367     
44368     getImage: function(type)
44369     {
44370         if(this.is_empty) {
44371             return false;
44372         }
44373         
44374         // encryption ?
44375         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44376     },
44377     
44378     drawFromImage: function(img_src)
44379     {
44380         var img = new Image();
44381         
44382         img.onload = function(){
44383             this.canvasElCtx().drawImage(img, 0, 0);
44384         }.bind(this);
44385         
44386         img.src = img_src;
44387         
44388         this.is_empty = false;
44389     },
44390     
44391     selectImage: function()
44392     {
44393         this.fileEl().dom.click();
44394     },
44395     
44396     uploadImage: function(e)
44397     {
44398         var reader = new FileReader();
44399         
44400         reader.onload = function(e){
44401             var img = new Image();
44402             img.onload = function(){
44403                 this.reset();
44404                 this.canvasElCtx().drawImage(img, 0, 0);
44405             }.bind(this);
44406             img.src = e.target.result;
44407         }.bind(this);
44408         
44409         reader.readAsDataURL(e.target.files[0]);
44410     },
44411     
44412     // Bezier Point Constructor
44413     Point: (function () {
44414         function Point(x, y, time) {
44415             this.x = x;
44416             this.y = y;
44417             this.time = time || Date.now();
44418         }
44419         Point.prototype.distanceTo = function (start) {
44420             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44421         };
44422         Point.prototype.equals = function (other) {
44423             return this.x === other.x && this.y === other.y && this.time === other.time;
44424         };
44425         Point.prototype.velocityFrom = function (start) {
44426             return this.time !== start.time
44427             ? this.distanceTo(start) / (this.time - start.time)
44428             : 0;
44429         };
44430         return Point;
44431     }()),
44432     
44433     
44434     // Bezier Constructor
44435     Bezier: (function () {
44436         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44437             this.startPoint = startPoint;
44438             this.control2 = control2;
44439             this.control1 = control1;
44440             this.endPoint = endPoint;
44441             this.startWidth = startWidth;
44442             this.endWidth = endWidth;
44443         }
44444         Bezier.fromPoints = function (points, widths, scope) {
44445             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44446             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44447             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44448         };
44449         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44450             var dx1 = s1.x - s2.x;
44451             var dy1 = s1.y - s2.y;
44452             var dx2 = s2.x - s3.x;
44453             var dy2 = s2.y - s3.y;
44454             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44455             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44456             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44457             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44458             var dxm = m1.x - m2.x;
44459             var dym = m1.y - m2.y;
44460             var k = l2 / (l1 + l2);
44461             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44462             var tx = s2.x - cm.x;
44463             var ty = s2.y - cm.y;
44464             return {
44465                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44466                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44467             };
44468         };
44469         Bezier.prototype.length = function () {
44470             var steps = 10;
44471             var length = 0;
44472             var px;
44473             var py;
44474             for (var i = 0; i <= steps; i += 1) {
44475                 var t = i / steps;
44476                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44477                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44478                 if (i > 0) {
44479                     var xdiff = cx - px;
44480                     var ydiff = cy - py;
44481                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44482                 }
44483                 px = cx;
44484                 py = cy;
44485             }
44486             return length;
44487         };
44488         Bezier.prototype.point = function (t, start, c1, c2, end) {
44489             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44490             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44491             + (3.0 * c2 * (1.0 - t) * t * t)
44492             + (end * t * t * t);
44493         };
44494         return Bezier;
44495     }()),
44496     
44497     throttleStroke: function(fn, wait) {
44498       if (wait === void 0) { wait = 250; }
44499       var previous = 0;
44500       var timeout = null;
44501       var result;
44502       var storedContext;
44503       var storedArgs;
44504       var later = function () {
44505           previous = Date.now();
44506           timeout = null;
44507           result = fn.apply(storedContext, storedArgs);
44508           if (!timeout) {
44509               storedContext = null;
44510               storedArgs = [];
44511           }
44512       };
44513       return function wrapper() {
44514           var args = [];
44515           for (var _i = 0; _i < arguments.length; _i++) {
44516               args[_i] = arguments[_i];
44517           }
44518           var now = Date.now();
44519           var remaining = wait - (now - previous);
44520           storedContext = this;
44521           storedArgs = args;
44522           if (remaining <= 0 || remaining > wait) {
44523               if (timeout) {
44524                   clearTimeout(timeout);
44525                   timeout = null;
44526               }
44527               previous = now;
44528               result = fn.apply(storedContext, storedArgs);
44529               if (!timeout) {
44530                   storedContext = null;
44531                   storedArgs = [];
44532               }
44533           }
44534           else if (!timeout) {
44535               timeout = window.setTimeout(later, remaining);
44536           }
44537           return result;
44538       };
44539   }
44540   
44541 });
44542
44543  
44544
44545