roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     header_imageEl : false,
2079     
2080     layoutCls : function()
2081     {
2082         var cls = '';
2083         var t = this;
2084         Roo.log(this.margin_bottom.length);
2085         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2086             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087             
2088             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2089                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2090             }
2091             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2092                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2093             }
2094         });
2095         
2096         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2097             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2098                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2099             }
2100         });
2101         
2102         // more generic support?
2103         if (this.hidden) {
2104             cls += ' d-none';
2105         }
2106         
2107         return cls;
2108     },
2109  
2110        // Roo.log("Call onRender: " + this.xtype);
2111         /*  We are looking at something like this.
2112 <div class="card">
2113     <img src="..." class="card-img-top" alt="...">
2114     <div class="card-body">
2115         <h5 class="card-title">Card title</h5>
2116          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117
2118         >> this bit is really the body...
2119         <div> << we will ad dthis in hopefully it will not break shit.
2120         
2121         ** card text does not actually have any styling...
2122         
2123             <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>
2124         
2125         </div> <<
2126           <a href="#" class="card-link">Card link</a>
2127           
2128     </div>
2129     <div class="card-footer">
2130         <small class="text-muted">Last updated 3 mins ago</small>
2131     </div>
2132 </div>
2133          */
2134     getAutoCreate : function(){
2135         
2136         var cfg = {
2137             tag : 'div',
2138             cls : 'card',
2139             cn : [ ]
2140         };
2141         
2142         if (this.weight.length && this.weight != 'light') {
2143             cfg.cls += ' text-white';
2144         } else {
2145             cfg.cls += ' text-dark'; // need as it's nested..
2146         }
2147         if (this.weight.length) {
2148             cfg.cls += ' bg-' + this.weight;
2149         }
2150         
2151         cfg.cls += ' ' + this.layoutCls(); 
2152         
2153         var hdr = false;
2154         var hdr_ctr = false;
2155         if (this.header.length) {
2156             hdr = {
2157                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2158                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2159                 cn : []
2160             };
2161             cfg.cn.push(hdr);
2162             hdr_ctr = hdr;
2163         } else {
2164             hdr = {
2165                 tag : 'div',
2166                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2167                 cn : []
2168             };
2169             cfg.cn.push(hdr);
2170             hdr_ctr = hdr;
2171         }
2172         if (this.collapsable) {
2173             hdr_ctr = {
2174             tag : 'a',
2175             cls : 'd-block user-select-none',
2176             cn: [
2177                     {
2178                         tag: 'i',
2179                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2180                     }
2181                    
2182                 ]
2183             };
2184             hdr.cn.push(hdr_ctr);
2185         }
2186         
2187         hdr_ctr.cn.push(        {
2188             tag: 'span',
2189             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2190             html : this.header
2191         });
2192         
2193         
2194         if (this.header_image.length) {
2195             cfg.cn.push({
2196                 tag : 'img',
2197                 cls : 'card-img-top',
2198                 src: this.header_image // escape?
2199             });
2200         } else {
2201             cfg.cn.push({
2202                     tag : 'div',
2203                     cls : 'card-img-top d-none' 
2204                 });
2205         }
2206             
2207         var body = {
2208             tag : 'div',
2209             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2210             cn : []
2211         };
2212         var obody = body;
2213         if (this.collapsable || this.rotateable) {
2214             obody = {
2215                 tag: 'div',
2216                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2217                 cn : [  body ]
2218             };
2219         }
2220         
2221         cfg.cn.push(obody);
2222         
2223         if (this.title.length) {
2224             body.cn.push({
2225                 tag : 'div',
2226                 cls : 'card-title',
2227                 src: this.title // escape?
2228             });
2229         }  
2230         
2231         if (this.subtitle.length) {
2232             body.cn.push({
2233                 tag : 'div',
2234                 cls : 'card-title',
2235                 src: this.subtitle // escape?
2236             });
2237         }
2238         
2239         body.cn.push({
2240             tag : 'div',
2241             cls : 'roo-card-body-ctr'
2242         });
2243         
2244         if (this.html.length) {
2245             body.cn.push({
2246                 tag: 'div',
2247                 html : this.html
2248             });
2249         }
2250         // fixme ? handle objects?
2251         
2252         if (this.footer.length) {
2253            
2254             cfg.cn.push({
2255                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2256                 html : this.footer
2257             });
2258             
2259         } else {
2260             cfg.cn.push({cls : 'card-footer d-none'});
2261         }
2262         
2263         // footer...
2264         
2265         return cfg;
2266     },
2267     
2268     
2269     getCardHeader : function()
2270     {
2271         var  ret = this.el.select('.card-header',true).first();
2272         if (ret.hasClass('d-none')) {
2273             ret.removeClass('d-none');
2274         }
2275         
2276         return ret;
2277     },
2278     getCardFooter : function()
2279     {
2280         var  ret = this.el.select('.card-footer',true).first();
2281         if (ret.hasClass('d-none')) {
2282             ret.removeClass('d-none');
2283         }
2284         
2285         return ret;
2286     },
2287     getCardImageTop : function()
2288     {
2289         var  ret = this.header_imageEl;
2290         if (ret.hasClass('d-none')) {
2291             ret.removeClass('d-none');
2292         }
2293             
2294         return ret;
2295     },
2296     
2297     getChildContainer : function()
2298     {
2299         
2300         if(!this.el){
2301             return false;
2302         }
2303         return this.el.select('.roo-card-body-ctr',true).first();    
2304     },
2305     
2306     initEvents: function() 
2307     {
2308         this.bodyEl = this.el.select('.card-body',true).first(); 
2309         this.containerEl = this.getChildContainer();
2310         if(this.dragable){
2311             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2312                     containerScroll: true,
2313                     ddGroup: this.drag_group || 'default_card_drag_group'
2314             });
2315             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316         }
2317         if (this.dropable) {
2318             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2319                 containerScroll: true,
2320                 ddGroup: this.drop_group || 'default_card_drag_group'
2321             });
2322             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2323             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2324             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2325             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2326             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2327         }
2328         
2329         if (this.collapsable) {
2330             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331         }
2332         if (this.rotateable) {
2333             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334         }
2335         this.collapsableEl = this.el.select('.roo-collapsable').first();
2336          
2337         this.footerEl = this.el.select('.card-footer').first();
2338         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2339         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2340         this.headerEl = this.el.select('.card-header',true).first();
2341         
2342         if (this.rotated) {
2343             this.el.addClass('roo-card-rotated');
2344             this.fireEvent('rotate', this, true);
2345         }
2346         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2347         this.header_imageEl.on('load', this.onHeaderImageLoad, this);
2348         
2349     },
2350     getDragData : function(e)
2351     {
2352         var target = this.getEl();
2353         if (target) {
2354             //this.handleSelection(e);
2355             
2356             var dragData = {
2357                 source: this,
2358                 copy: false,
2359                 nodes: this.getEl(),
2360                 records: []
2361             };
2362             
2363             
2364             dragData.ddel = target.dom ;    // the div element
2365             Roo.log(target.getWidth( ));
2366             dragData.ddel.style.width = target.getWidth() + 'px';
2367             
2368             return dragData;
2369         }
2370         return false;
2371     },
2372     /**
2373     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2374     *    whole Element becomes the target, and this causes the drop gesture to append.
2375     */
2376     getTargetFromEvent : function(e, dragged_card_el)
2377     {
2378         var target = e.getTarget();
2379         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2380             target = target.parentNode;
2381         }
2382         
2383         var ret = {
2384             position: '',
2385             cards : [],
2386             card_n : -1,
2387             items_n : -1,
2388             card : false 
2389         };
2390         
2391         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2392         // see if target is one of the 'cards'...
2393         
2394         
2395         //Roo.log(this.items.length);
2396         var pos = false;
2397         
2398         var last_card_n = 0;
2399         var cards_len  = 0;
2400         for (var i = 0;i< this.items.length;i++) {
2401             
2402             if (!this.items[i].el.hasClass('card')) {
2403                  continue;
2404             }
2405             pos = this.getDropPoint(e, this.items[i].el.dom);
2406             
2407             cards_len = ret.cards.length;
2408             //Roo.log(this.items[i].el.dom.id);
2409             ret.cards.push(this.items[i]);
2410             last_card_n  = i;
2411             if (ret.card_n < 0 && pos == 'above') {
2412                 ret.position = cards_len > 0 ? 'below' : pos;
2413                 ret.items_n = i > 0 ? i - 1 : 0;
2414                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2415                 ret.card = ret.cards[ret.card_n];
2416             }
2417         }
2418         if (!ret.cards.length) {
2419             ret.card = true;
2420             ret.position = 'below';
2421             ret.items_n;
2422             return ret;
2423         }
2424         // could not find a card.. stick it at the end..
2425         if (ret.card_n < 0) {
2426             ret.card_n = last_card_n;
2427             ret.card = ret.cards[last_card_n];
2428             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2429             ret.position = 'below';
2430         }
2431         
2432         if (this.items[ret.items_n].el == dragged_card_el) {
2433             return false;
2434         }
2435         
2436         if (ret.position == 'below') {
2437             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2438             
2439             if (card_after  && card_after.el == dragged_card_el) {
2440                 return false;
2441             }
2442             return ret;
2443         }
2444         
2445         // its's after ..
2446         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2447         
2448         if (card_before  && card_before.el == dragged_card_el) {
2449             return false;
2450         }
2451         
2452         return ret;
2453     },
2454     
2455     onNodeEnter : function(n, dd, e, data){
2456         return false;
2457     },
2458     onNodeOver : function(n, dd, e, data)
2459     {
2460        
2461         var target_info = this.getTargetFromEvent(e,data.source.el);
2462         if (target_info === false) {
2463             this.dropPlaceHolder('hide');
2464             return false;
2465         }
2466         Roo.log(['getTargetFromEvent', target_info ]);
2467         
2468          
2469         this.dropPlaceHolder('show', target_info,data);
2470         
2471         return false; 
2472     },
2473     onNodeOut : function(n, dd, e, data){
2474         this.dropPlaceHolder('hide');
2475      
2476     },
2477     onNodeDrop : function(n, dd, e, data)
2478     {
2479         
2480         // call drop - return false if
2481         
2482         // this could actually fail - if the Network drops..
2483         // we will ignore this at present..- client should probably reload
2484         // the whole set of cards if stuff like that fails.
2485         
2486         
2487         var info = this.getTargetFromEvent(e,data.source.el);
2488         if (info === false) {
2489             return false;
2490         }
2491         this.dropPlaceHolder('hide');
2492   
2493          
2494     
2495     
2496     
2497         this.acceptCard(data.source, info.position, info.card, info.items_n);
2498         return true;
2499          
2500     },
2501     firstChildCard : function()
2502     {
2503         for (var i = 0;i< this.items.length;i++) {
2504             
2505             if (!this.items[i].el.hasClass('card')) {
2506                  continue;
2507             }
2508             return this.items[i];
2509         }
2510         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2511     },
2512     /**
2513      * accept card
2514      *
2515      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2516      */
2517     acceptCard : function(move_card,  position, next_to_card )
2518     {
2519         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520             return false;
2521         }
2522         
2523         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2524         
2525         move_card.parent().removeCard(move_card);
2526         
2527         
2528         var dom = move_card.el.dom;
2529         dom.style.width = ''; // clear with - which is set by drag.
2530         
2531         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2532             var cardel = next_to_card.el.dom;
2533             
2534             if (position == 'above' ) {
2535                 cardel.parentNode.insertBefore(dom, cardel);
2536             } else if (cardel.nextSibling) {
2537                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2538             } else {
2539                 cardel.parentNode.append(dom);
2540             }
2541         } else {
2542             // card container???
2543             this.containerEl.dom.append(dom);
2544         }
2545         
2546         //FIXME HANDLE card = true 
2547         
2548         // add this to the correct place in items.
2549         
2550         // remove Card from items.
2551         
2552        
2553         if (this.items.length) {
2554             var nitems = [];
2555             //Roo.log([info.items_n, info.position, this.items.length]);
2556             for (var i =0; i < this.items.length; i++) {
2557                 if (i == to_items_n && position == 'above') {
2558                     nitems.push(move_card);
2559                 }
2560                 nitems.push(this.items[i]);
2561                 if (i == to_items_n && position == 'below') {
2562                     nitems.push(move_card);
2563                 }
2564             }
2565             this.items = nitems;
2566             Roo.log(this.items);
2567         } else {
2568             this.items.push(move_card);
2569         }
2570         
2571         move_card.parentId = this.id;
2572         
2573         return true;
2574         
2575         
2576     },
2577     removeCard : function(c)
2578     {
2579         this.items = this.items.filter(function(e) { return e != c });
2580  
2581         var dom = c.el.dom;
2582         dom.parentNode.removeChild(dom);
2583         dom.style.width = ''; // clear with - which is set by drag.
2584         c.parentId = false;
2585         
2586     },
2587     
2588     /**    Decide whether to drop above or below a View node. */
2589     getDropPoint : function(e, n, dd)
2590     {
2591         if (dd) {
2592              return false;
2593         }
2594         if (n == this.containerEl.dom) {
2595             return "above";
2596         }
2597         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2598         var c = t + (b - t) / 2;
2599         var y = Roo.lib.Event.getPageY(e);
2600         if(y <= c) {
2601             return "above";
2602         }else{
2603             return "below";
2604         }
2605     },
2606     onToggleCollapse : function(e)
2607         {
2608         if (this.collapsed) {
2609             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2610             this.collapsableEl.addClass('show');
2611             this.collapsed = false;
2612             return;
2613         }
2614         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2615         this.collapsableEl.removeClass('show');
2616         this.collapsed = true;
2617         
2618     
2619     },
2620     
2621     onToggleRotate : function(e)
2622     {
2623         this.collapsableEl.removeClass('show');
2624         this.footerEl.removeClass('d-none');
2625         this.el.removeClass('roo-card-rotated');
2626         this.el.removeClass('d-none');
2627         if (this.rotated) {
2628             
2629             this.collapsableEl.addClass('show');
2630             this.rotated = false;
2631             this.fireEvent('rotate', this, this.rotated);
2632             return;
2633         }
2634         this.el.addClass('roo-card-rotated');
2635         this.footerEl.addClass('d-none');
2636         this.el.select('.roo-collapsable').removeClass('show');
2637         
2638         this.rotated = true;
2639         this.fireEvent('rotate', this, this.rotated);
2640     
2641     },
2642     
2643     dropPlaceHolder: function (action, info, data)
2644     {
2645         if (this.dropEl === false) {
2646             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647             cls : 'd-none'
2648             },true);
2649         }
2650         this.dropEl.removeClass(['d-none', 'd-block']);        
2651         if (action == 'hide') {
2652             
2653             this.dropEl.addClass('d-none');
2654             return;
2655         }
2656         // FIXME - info.card == true!!!
2657         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2658         
2659         if (info.card !== true) {
2660             var cardel = info.card.el.dom;
2661             
2662             if (info.position == 'above') {
2663                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2664             } else if (cardel.nextSibling) {
2665                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2666             } else {
2667                 cardel.parentNode.append(this.dropEl.dom);
2668             }
2669         } else {
2670             // card container???
2671             this.containerEl.dom.append(this.dropEl.dom);
2672         }
2673         
2674         this.dropEl.addClass('d-block roo-card-dropzone');
2675         
2676         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2677         
2678         
2679     
2680     
2681     
2682     },
2683     setHeaderText: function(html)
2684     {
2685         this.header = html;
2686         if (this.headerContainerEl) {
2687             this.headerContainerEl.dom.innerHTML = html;
2688         }
2689     },
2690     onHeaderImageLoad : function(he)
2691     {
2692         if (!this.header_image_fit_square) {
2693             return;
2694         }
2695         
2696         var hw = he.dom.naturalHeight / he.dom.naturalWidth;
2697         // wide image = < 0
2698         // tall image = > 1
2699         //var w = he.dom.naturalWidth;
2700         var ww = he.dom.width;
2701         if (hw > 1) {
2702             he.setSize( ww * (1/hw),  ww);
2703         }
2704
2705     }
2706
2707     
2708 });
2709
2710 /*
2711  * - LGPL
2712  *
2713  * Card header - holder for the card header elements.
2714  * 
2715  */
2716
2717 /**
2718  * @class Roo.bootstrap.CardHeader
2719  * @extends Roo.bootstrap.Element
2720  * Bootstrap CardHeader class
2721  * @constructor
2722  * Create a new Card Header - that you can embed children into
2723  * @param {Object} config The config object
2724  */
2725
2726 Roo.bootstrap.CardHeader = function(config){
2727     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2728 };
2729
2730 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2731     
2732     
2733     container_method : 'getCardHeader' 
2734     
2735      
2736     
2737     
2738    
2739 });
2740
2741  
2742
2743  /*
2744  * - LGPL
2745  *
2746  * Card footer - holder for the card footer elements.
2747  * 
2748  */
2749
2750 /**
2751  * @class Roo.bootstrap.CardFooter
2752  * @extends Roo.bootstrap.Element
2753  * Bootstrap CardFooter class
2754  * @constructor
2755  * Create a new Card Footer - that you can embed children into
2756  * @param {Object} config The config object
2757  */
2758
2759 Roo.bootstrap.CardFooter = function(config){
2760     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2761 };
2762
2763 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2764     
2765     
2766     container_method : 'getCardFooter' 
2767     
2768      
2769     
2770     
2771    
2772 });
2773
2774  
2775
2776  /*
2777  * - LGPL
2778  *
2779  * Card header - holder for the card header elements.
2780  * 
2781  */
2782
2783 /**
2784  * @class Roo.bootstrap.CardImageTop
2785  * @extends Roo.bootstrap.Element
2786  * Bootstrap CardImageTop class
2787  * @constructor
2788  * Create a new Card Image Top container
2789  * @param {Object} config The config object
2790  */
2791
2792 Roo.bootstrap.CardImageTop = function(config){
2793     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2794 };
2795
2796 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2797     
2798    
2799     container_method : 'getCardImageTop' 
2800     
2801      
2802     
2803    
2804 });
2805
2806  
2807
2808  /*
2809  * - LGPL
2810  *
2811  * image
2812  * 
2813  */
2814
2815
2816 /**
2817  * @class Roo.bootstrap.Img
2818  * @extends Roo.bootstrap.Component
2819  * Bootstrap Img class
2820  * @cfg {Boolean} imgResponsive false | true
2821  * @cfg {String} border rounded | circle | thumbnail
2822  * @cfg {String} src image source
2823  * @cfg {String} alt image alternative text
2824  * @cfg {String} href a tag href
2825  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2826  * @cfg {String} xsUrl xs image source
2827  * @cfg {String} smUrl sm image source
2828  * @cfg {String} mdUrl md image source
2829  * @cfg {String} lgUrl lg image source
2830  * 
2831  * @constructor
2832  * Create a new Input
2833  * @param {Object} config The config object
2834  */
2835
2836 Roo.bootstrap.Img = function(config){
2837     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2838     
2839     this.addEvents({
2840         // img events
2841         /**
2842          * @event click
2843          * The img click event for the img.
2844          * @param {Roo.EventObject} e
2845          */
2846         "click" : true
2847     });
2848 };
2849
2850 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2851     
2852     imgResponsive: true,
2853     border: '',
2854     src: 'about:blank',
2855     href: false,
2856     target: false,
2857     xsUrl: '',
2858     smUrl: '',
2859     mdUrl: '',
2860     lgUrl: '',
2861
2862     getAutoCreate : function()
2863     {   
2864         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2865             return this.createSingleImg();
2866         }
2867         
2868         var cfg = {
2869             tag: 'div',
2870             cls: 'roo-image-responsive-group',
2871             cn: []
2872         };
2873         var _this = this;
2874         
2875         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2876             
2877             if(!_this[size + 'Url']){
2878                 return;
2879             }
2880             
2881             var img = {
2882                 tag: 'img',
2883                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2884                 html: _this.html || cfg.html,
2885                 src: _this[size + 'Url']
2886             };
2887             
2888             img.cls += ' roo-image-responsive-' + size;
2889             
2890             var s = ['xs', 'sm', 'md', 'lg'];
2891             
2892             s.splice(s.indexOf(size), 1);
2893             
2894             Roo.each(s, function(ss){
2895                 img.cls += ' hidden-' + ss;
2896             });
2897             
2898             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2899                 cfg.cls += ' img-' + _this.border;
2900             }
2901             
2902             if(_this.alt){
2903                 cfg.alt = _this.alt;
2904             }
2905             
2906             if(_this.href){
2907                 var a = {
2908                     tag: 'a',
2909                     href: _this.href,
2910                     cn: [
2911                         img
2912                     ]
2913                 };
2914
2915                 if(this.target){
2916                     a.target = _this.target;
2917                 }
2918             }
2919             
2920             cfg.cn.push((_this.href) ? a : img);
2921             
2922         });
2923         
2924         return cfg;
2925     },
2926     
2927     createSingleImg : function()
2928     {
2929         var cfg = {
2930             tag: 'img',
2931             cls: (this.imgResponsive) ? 'img-responsive' : '',
2932             html : null,
2933             src : 'about:blank'  // just incase src get's set to undefined?!?
2934         };
2935         
2936         cfg.html = this.html || cfg.html;
2937         
2938         cfg.src = this.src || cfg.src;
2939         
2940         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2941             cfg.cls += ' img-' + this.border;
2942         }
2943         
2944         if(this.alt){
2945             cfg.alt = this.alt;
2946         }
2947         
2948         if(this.href){
2949             var a = {
2950                 tag: 'a',
2951                 href: this.href,
2952                 cn: [
2953                     cfg
2954                 ]
2955             };
2956             
2957             if(this.target){
2958                 a.target = this.target;
2959             }
2960             
2961         }
2962         
2963         return (this.href) ? a : cfg;
2964     },
2965     
2966     initEvents: function() 
2967     {
2968         if(!this.href){
2969             this.el.on('click', this.onClick, this);
2970         }
2971         
2972     },
2973     
2974     onClick : function(e)
2975     {
2976         Roo.log('img onclick');
2977         this.fireEvent('click', this, e);
2978     },
2979     /**
2980      * Sets the url of the image - used to update it
2981      * @param {String} url the url of the image
2982      */
2983     
2984     setSrc : function(url)
2985     {
2986         this.src =  url;
2987         
2988         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2989             this.el.dom.src =  url;
2990             return;
2991         }
2992         
2993         this.el.select('img', true).first().dom.src =  url;
2994     }
2995     
2996     
2997    
2998 });
2999
3000  /*
3001  * - LGPL
3002  *
3003  * image
3004  * 
3005  */
3006
3007
3008 /**
3009  * @class Roo.bootstrap.Link
3010  * @extends Roo.bootstrap.Component
3011  * Bootstrap Link Class
3012  * @cfg {String} alt image alternative text
3013  * @cfg {String} href a tag href
3014  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3015  * @cfg {String} html the content of the link.
3016  * @cfg {String} anchor name for the anchor link
3017  * @cfg {String} fa - favicon
3018
3019  * @cfg {Boolean} preventDefault (true | false) default false
3020
3021  * 
3022  * @constructor
3023  * Create a new Input
3024  * @param {Object} config The config object
3025  */
3026
3027 Roo.bootstrap.Link = function(config){
3028     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3029     
3030     this.addEvents({
3031         // img events
3032         /**
3033          * @event click
3034          * The img click event for the img.
3035          * @param {Roo.EventObject} e
3036          */
3037         "click" : true
3038     });
3039 };
3040
3041 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3042     
3043     href: false,
3044     target: false,
3045     preventDefault: false,
3046     anchor : false,
3047     alt : false,
3048     fa: false,
3049
3050
3051     getAutoCreate : function()
3052     {
3053         var html = this.html || '';
3054         
3055         if (this.fa !== false) {
3056             html = '<i class="fa fa-' + this.fa + '"></i>';
3057         }
3058         var cfg = {
3059             tag: 'a'
3060         };
3061         // anchor's do not require html/href...
3062         if (this.anchor === false) {
3063             cfg.html = html;
3064             cfg.href = this.href || '#';
3065         } else {
3066             cfg.name = this.anchor;
3067             if (this.html !== false || this.fa !== false) {
3068                 cfg.html = html;
3069             }
3070             if (this.href !== false) {
3071                 cfg.href = this.href;
3072             }
3073         }
3074         
3075         if(this.alt !== false){
3076             cfg.alt = this.alt;
3077         }
3078         
3079         
3080         if(this.target !== false) {
3081             cfg.target = this.target;
3082         }
3083         
3084         return cfg;
3085     },
3086     
3087     initEvents: function() {
3088         
3089         if(!this.href || this.preventDefault){
3090             this.el.on('click', this.onClick, this);
3091         }
3092     },
3093     
3094     onClick : function(e)
3095     {
3096         if(this.preventDefault){
3097             e.preventDefault();
3098         }
3099         //Roo.log('img onclick');
3100         this.fireEvent('click', this, e);
3101     }
3102    
3103 });
3104
3105  /*
3106  * - LGPL
3107  *
3108  * header
3109  * 
3110  */
3111
3112 /**
3113  * @class Roo.bootstrap.Header
3114  * @extends Roo.bootstrap.Component
3115  * Bootstrap Header class
3116  * @cfg {String} html content of header
3117  * @cfg {Number} level (1|2|3|4|5|6) default 1
3118  * 
3119  * @constructor
3120  * Create a new Header
3121  * @param {Object} config The config object
3122  */
3123
3124
3125 Roo.bootstrap.Header  = function(config){
3126     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3127 };
3128
3129 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3130     
3131     //href : false,
3132     html : false,
3133     level : 1,
3134     
3135     
3136     
3137     getAutoCreate : function(){
3138         
3139         
3140         
3141         var cfg = {
3142             tag: 'h' + (1 *this.level),
3143             html: this.html || ''
3144         } ;
3145         
3146         return cfg;
3147     }
3148    
3149 });
3150
3151  
3152
3153  /*
3154  * Based on:
3155  * Ext JS Library 1.1.1
3156  * Copyright(c) 2006-2007, Ext JS, LLC.
3157  *
3158  * Originally Released Under LGPL - original licence link has changed is not relivant.
3159  *
3160  * Fork - LGPL
3161  * <script type="text/javascript">
3162  */
3163  
3164 /**
3165  * @class Roo.bootstrap.MenuMgr
3166  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3167  * @singleton
3168  */
3169 Roo.bootstrap.MenuMgr = function(){
3170    var menus, active, groups = {}, attached = false, lastShow = new Date();
3171
3172    // private - called when first menu is created
3173    function init(){
3174        menus = {};
3175        active = new Roo.util.MixedCollection();
3176        Roo.get(document).addKeyListener(27, function(){
3177            if(active.length > 0){
3178                hideAll();
3179            }
3180        });
3181    }
3182
3183    // private
3184    function hideAll(){
3185        if(active && active.length > 0){
3186            var c = active.clone();
3187            c.each(function(m){
3188                m.hide();
3189            });
3190        }
3191    }
3192
3193    // private
3194    function onHide(m){
3195        active.remove(m);
3196        if(active.length < 1){
3197            Roo.get(document).un("mouseup", onMouseDown);
3198             
3199            attached = false;
3200        }
3201    }
3202
3203    // private
3204    function onShow(m){
3205        var last = active.last();
3206        lastShow = new Date();
3207        active.add(m);
3208        if(!attached){
3209           Roo.get(document).on("mouseup", onMouseDown);
3210            
3211            attached = true;
3212        }
3213        if(m.parentMenu){
3214           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3215           m.parentMenu.activeChild = m;
3216        }else if(last && last.isVisible()){
3217           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3218        }
3219    }
3220
3221    // private
3222    function onBeforeHide(m){
3223        if(m.activeChild){
3224            m.activeChild.hide();
3225        }
3226        if(m.autoHideTimer){
3227            clearTimeout(m.autoHideTimer);
3228            delete m.autoHideTimer;
3229        }
3230    }
3231
3232    // private
3233    function onBeforeShow(m){
3234        var pm = m.parentMenu;
3235        if(!pm && !m.allowOtherMenus){
3236            hideAll();
3237        }else if(pm && pm.activeChild && active != m){
3238            pm.activeChild.hide();
3239        }
3240    }
3241
3242    // private this should really trigger on mouseup..
3243    function onMouseDown(e){
3244         Roo.log("on Mouse Up");
3245         
3246         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3247             Roo.log("MenuManager hideAll");
3248             hideAll();
3249             e.stopEvent();
3250         }
3251         
3252         
3253    }
3254
3255    // private
3256    function onBeforeCheck(mi, state){
3257        if(state){
3258            var g = groups[mi.group];
3259            for(var i = 0, l = g.length; i < l; i++){
3260                if(g[i] != mi){
3261                    g[i].setChecked(false);
3262                }
3263            }
3264        }
3265    }
3266
3267    return {
3268
3269        /**
3270         * Hides all menus that are currently visible
3271         */
3272        hideAll : function(){
3273             hideAll();  
3274        },
3275
3276        // private
3277        register : function(menu){
3278            if(!menus){
3279                init();
3280            }
3281            menus[menu.id] = menu;
3282            menu.on("beforehide", onBeforeHide);
3283            menu.on("hide", onHide);
3284            menu.on("beforeshow", onBeforeShow);
3285            menu.on("show", onShow);
3286            var g = menu.group;
3287            if(g && menu.events["checkchange"]){
3288                if(!groups[g]){
3289                    groups[g] = [];
3290                }
3291                groups[g].push(menu);
3292                menu.on("checkchange", onCheck);
3293            }
3294        },
3295
3296         /**
3297          * Returns a {@link Roo.menu.Menu} object
3298          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3299          * be used to generate and return a new Menu instance.
3300          */
3301        get : function(menu){
3302            if(typeof menu == "string"){ // menu id
3303                return menus[menu];
3304            }else if(menu.events){  // menu instance
3305                return menu;
3306            }
3307            /*else if(typeof menu.length == 'number'){ // array of menu items?
3308                return new Roo.bootstrap.Menu({items:menu});
3309            }else{ // otherwise, must be a config
3310                return new Roo.bootstrap.Menu(menu);
3311            }
3312            */
3313            return false;
3314        },
3315
3316        // private
3317        unregister : function(menu){
3318            delete menus[menu.id];
3319            menu.un("beforehide", onBeforeHide);
3320            menu.un("hide", onHide);
3321            menu.un("beforeshow", onBeforeShow);
3322            menu.un("show", onShow);
3323            var g = menu.group;
3324            if(g && menu.events["checkchange"]){
3325                groups[g].remove(menu);
3326                menu.un("checkchange", onCheck);
3327            }
3328        },
3329
3330        // private
3331        registerCheckable : function(menuItem){
3332            var g = menuItem.group;
3333            if(g){
3334                if(!groups[g]){
3335                    groups[g] = [];
3336                }
3337                groups[g].push(menuItem);
3338                menuItem.on("beforecheckchange", onBeforeCheck);
3339            }
3340        },
3341
3342        // private
3343        unregisterCheckable : function(menuItem){
3344            var g = menuItem.group;
3345            if(g){
3346                groups[g].remove(menuItem);
3347                menuItem.un("beforecheckchange", onBeforeCheck);
3348            }
3349        }
3350    };
3351 }();/*
3352  * - LGPL
3353  *
3354  * menu
3355  * 
3356  */
3357
3358 /**
3359  * @class Roo.bootstrap.Menu
3360  * @extends Roo.bootstrap.Component
3361  * Bootstrap Menu class - container for MenuItems
3362  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3363  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3364  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3365  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3366  * 
3367  * @constructor
3368  * Create a new Menu
3369  * @param {Object} config The config object
3370  */
3371
3372
3373 Roo.bootstrap.Menu = function(config){
3374     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3375     if (this.registerMenu && this.type != 'treeview')  {
3376         Roo.bootstrap.MenuMgr.register(this);
3377     }
3378     
3379     
3380     this.addEvents({
3381         /**
3382          * @event beforeshow
3383          * Fires before this menu is displayed (return false to block)
3384          * @param {Roo.menu.Menu} this
3385          */
3386         beforeshow : true,
3387         /**
3388          * @event beforehide
3389          * Fires before this menu is hidden (return false to block)
3390          * @param {Roo.menu.Menu} this
3391          */
3392         beforehide : true,
3393         /**
3394          * @event show
3395          * Fires after this menu is displayed
3396          * @param {Roo.menu.Menu} this
3397          */
3398         show : true,
3399         /**
3400          * @event hide
3401          * Fires after this menu is hidden
3402          * @param {Roo.menu.Menu} this
3403          */
3404         hide : true,
3405         /**
3406          * @event click
3407          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3408          * @param {Roo.menu.Menu} this
3409          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3410          * @param {Roo.EventObject} e
3411          */
3412         click : true,
3413         /**
3414          * @event mouseover
3415          * Fires when the mouse is hovering over this menu
3416          * @param {Roo.menu.Menu} this
3417          * @param {Roo.EventObject} e
3418          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3419          */
3420         mouseover : true,
3421         /**
3422          * @event mouseout
3423          * Fires when the mouse exits this menu
3424          * @param {Roo.menu.Menu} this
3425          * @param {Roo.EventObject} e
3426          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3427          */
3428         mouseout : true,
3429         /**
3430          * @event itemclick
3431          * Fires when a menu item contained in this menu is clicked
3432          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3433          * @param {Roo.EventObject} e
3434          */
3435         itemclick: true
3436     });
3437     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3438 };
3439
3440 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3441     
3442    /// html : false,
3443     //align : '',
3444     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3445     type: false,
3446     /**
3447      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3448      */
3449     registerMenu : true,
3450     
3451     menuItems :false, // stores the menu items..
3452     
3453     hidden:true,
3454         
3455     parentMenu : false,
3456     
3457     stopEvent : true,
3458     
3459     isLink : false,
3460     
3461     getChildContainer : function() {
3462         return this.el;  
3463     },
3464     
3465     getAutoCreate : function(){
3466          
3467         //if (['right'].indexOf(this.align)!==-1) {
3468         //    cfg.cn[1].cls += ' pull-right'
3469         //}
3470         
3471         
3472         var cfg = {
3473             tag : 'ul',
3474             cls : 'dropdown-menu' ,
3475             style : 'z-index:1000'
3476             
3477         };
3478         
3479         if (this.type === 'submenu') {
3480             cfg.cls = 'submenu active';
3481         }
3482         if (this.type === 'treeview') {
3483             cfg.cls = 'treeview-menu';
3484         }
3485         
3486         return cfg;
3487     },
3488     initEvents : function() {
3489         
3490        // Roo.log("ADD event");
3491        // Roo.log(this.triggerEl.dom);
3492         
3493         this.triggerEl.on('click', this.onTriggerClick, this);
3494         
3495         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3496         
3497         
3498         if (this.triggerEl.hasClass('nav-item')) {
3499             // dropdown toggle on the 'a' in BS4?
3500             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3501         } else {
3502             this.triggerEl.addClass('dropdown-toggle');
3503         }
3504         if (Roo.isTouch) {
3505             this.el.on('touchstart'  , this.onTouch, this);
3506         }
3507         this.el.on('click' , this.onClick, this);
3508
3509         this.el.on("mouseover", this.onMouseOver, this);
3510         this.el.on("mouseout", this.onMouseOut, this);
3511         
3512     },
3513     
3514     findTargetItem : function(e)
3515     {
3516         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3517         if(!t){
3518             return false;
3519         }
3520         //Roo.log(t);         Roo.log(t.id);
3521         if(t && t.id){
3522             //Roo.log(this.menuitems);
3523             return this.menuitems.get(t.id);
3524             
3525             //return this.items.get(t.menuItemId);
3526         }
3527         
3528         return false;
3529     },
3530     
3531     onTouch : function(e) 
3532     {
3533         Roo.log("menu.onTouch");
3534         //e.stopEvent(); this make the user popdown broken
3535         this.onClick(e);
3536     },
3537     
3538     onClick : function(e)
3539     {
3540         Roo.log("menu.onClick");
3541         
3542         var t = this.findTargetItem(e);
3543         if(!t || t.isContainer){
3544             return;
3545         }
3546         Roo.log(e);
3547         /*
3548         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3549             if(t == this.activeItem && t.shouldDeactivate(e)){
3550                 this.activeItem.deactivate();
3551                 delete this.activeItem;
3552                 return;
3553             }
3554             if(t.canActivate){
3555                 this.setActiveItem(t, true);
3556             }
3557             return;
3558             
3559             
3560         }
3561         */
3562        
3563         Roo.log('pass click event');
3564         
3565         t.onClick(e);
3566         
3567         this.fireEvent("click", this, t, e);
3568         
3569         var _this = this;
3570         
3571         if(!t.href.length || t.href == '#'){
3572             (function() { _this.hide(); }).defer(100);
3573         }
3574         
3575     },
3576     
3577     onMouseOver : function(e){
3578         var t  = this.findTargetItem(e);
3579         //Roo.log(t);
3580         //if(t){
3581         //    if(t.canActivate && !t.disabled){
3582         //        this.setActiveItem(t, true);
3583         //    }
3584         //}
3585         
3586         this.fireEvent("mouseover", this, e, t);
3587     },
3588     isVisible : function(){
3589         return !this.hidden;
3590     },
3591     onMouseOut : function(e){
3592         var t  = this.findTargetItem(e);
3593         
3594         //if(t ){
3595         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3596         //        this.activeItem.deactivate();
3597         //        delete this.activeItem;
3598         //    }
3599         //}
3600         this.fireEvent("mouseout", this, e, t);
3601     },
3602     
3603     
3604     /**
3605      * Displays this menu relative to another element
3606      * @param {String/HTMLElement/Roo.Element} element The element to align to
3607      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3608      * the element (defaults to this.defaultAlign)
3609      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3610      */
3611     show : function(el, pos, parentMenu)
3612     {
3613         if (false === this.fireEvent("beforeshow", this)) {
3614             Roo.log("show canceled");
3615             return;
3616         }
3617         this.parentMenu = parentMenu;
3618         if(!this.el){
3619             this.render();
3620         }
3621         
3622         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3623     },
3624      /**
3625      * Displays this menu at a specific xy position
3626      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3627      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3628      */
3629     showAt : function(xy, parentMenu, /* private: */_e){
3630         this.parentMenu = parentMenu;
3631         if(!this.el){
3632             this.render();
3633         }
3634         if(_e !== false){
3635             this.fireEvent("beforeshow", this);
3636             //xy = this.el.adjustForConstraints(xy);
3637         }
3638         
3639         //this.el.show();
3640         this.hideMenuItems();
3641         this.hidden = false;
3642         this.triggerEl.addClass('open');
3643         this.el.addClass('show');
3644         
3645         // reassign x when hitting right
3646         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3647             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3648         }
3649         
3650         // reassign y when hitting bottom
3651         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3652             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3653         }
3654         
3655         // but the list may align on trigger left or trigger top... should it be a properity?
3656         
3657         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3658             this.el.setXY(xy);
3659         }
3660         
3661         this.focus();
3662         this.fireEvent("show", this);
3663     },
3664     
3665     focus : function(){
3666         return;
3667         if(!this.hidden){
3668             this.doFocus.defer(50, this);
3669         }
3670     },
3671
3672     doFocus : function(){
3673         if(!this.hidden){
3674             this.focusEl.focus();
3675         }
3676     },
3677
3678     /**
3679      * Hides this menu and optionally all parent menus
3680      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3681      */
3682     hide : function(deep)
3683     {
3684         if (false === this.fireEvent("beforehide", this)) {
3685             Roo.log("hide canceled");
3686             return;
3687         }
3688         this.hideMenuItems();
3689         if(this.el && this.isVisible()){
3690            
3691             if(this.activeItem){
3692                 this.activeItem.deactivate();
3693                 this.activeItem = null;
3694             }
3695             this.triggerEl.removeClass('open');;
3696             this.el.removeClass('show');
3697             this.hidden = true;
3698             this.fireEvent("hide", this);
3699         }
3700         if(deep === true && this.parentMenu){
3701             this.parentMenu.hide(true);
3702         }
3703     },
3704     
3705     onTriggerClick : function(e)
3706     {
3707         Roo.log('trigger click');
3708         
3709         var target = e.getTarget();
3710         
3711         Roo.log(target.nodeName.toLowerCase());
3712         
3713         if(target.nodeName.toLowerCase() === 'i'){
3714             e.preventDefault();
3715         }
3716         
3717     },
3718     
3719     onTriggerPress  : function(e)
3720     {
3721         Roo.log('trigger press');
3722         //Roo.log(e.getTarget());
3723        // Roo.log(this.triggerEl.dom);
3724        
3725         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3726         var pel = Roo.get(e.getTarget());
3727         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3728             Roo.log('is treeview or dropdown?');
3729             return;
3730         }
3731         
3732         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3733             return;
3734         }
3735         
3736         if (this.isVisible()) {
3737             Roo.log('hide');
3738             this.hide();
3739         } else {
3740             Roo.log('show');
3741             this.show(this.triggerEl, '?', false);
3742         }
3743         
3744         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3745             e.stopEvent();
3746         }
3747         
3748     },
3749        
3750     
3751     hideMenuItems : function()
3752     {
3753         Roo.log("hide Menu Items");
3754         if (!this.el) { 
3755             return;
3756         }
3757         
3758         this.el.select('.open',true).each(function(aa) {
3759             
3760             aa.removeClass('open');
3761          
3762         });
3763     },
3764     addxtypeChild : function (tree, cntr) {
3765         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3766           
3767         this.menuitems.add(comp);
3768         return comp;
3769
3770     },
3771     getEl : function()
3772     {
3773         Roo.log(this.el);
3774         return this.el;
3775     },
3776     
3777     clear : function()
3778     {
3779         this.getEl().dom.innerHTML = '';
3780         this.menuitems.clear();
3781     }
3782 });
3783
3784  
3785  /*
3786  * - LGPL
3787  *
3788  * menu item
3789  * 
3790  */
3791
3792
3793 /**
3794  * @class Roo.bootstrap.MenuItem
3795  * @extends Roo.bootstrap.Component
3796  * Bootstrap MenuItem class
3797  * @cfg {String} html the menu label
3798  * @cfg {String} href the link
3799  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3800  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3801  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3802  * @cfg {String} fa favicon to show on left of menu item.
3803  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3804  * 
3805  * 
3806  * @constructor
3807  * Create a new MenuItem
3808  * @param {Object} config The config object
3809  */
3810
3811
3812 Roo.bootstrap.MenuItem = function(config){
3813     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3814     this.addEvents({
3815         // raw events
3816         /**
3817          * @event click
3818          * The raw click event for the entire grid.
3819          * @param {Roo.bootstrap.MenuItem} this
3820          * @param {Roo.EventObject} e
3821          */
3822         "click" : true
3823     });
3824 };
3825
3826 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3827     
3828     href : false,
3829     html : false,
3830     preventDefault: false,
3831     isContainer : false,
3832     active : false,
3833     fa: false,
3834     
3835     getAutoCreate : function(){
3836         
3837         if(this.isContainer){
3838             return {
3839                 tag: 'li',
3840                 cls: 'dropdown-menu-item '
3841             };
3842         }
3843         var ctag = {
3844             tag: 'span',
3845             html: 'Link'
3846         };
3847         
3848         var anc = {
3849             tag : 'a',
3850             cls : 'dropdown-item',
3851             href : '#',
3852             cn : [  ]
3853         };
3854         
3855         if (this.fa !== false) {
3856             anc.cn.push({
3857                 tag : 'i',
3858                 cls : 'fa fa-' + this.fa
3859             });
3860         }
3861         
3862         anc.cn.push(ctag);
3863         
3864         
3865         var cfg= {
3866             tag: 'li',
3867             cls: 'dropdown-menu-item',
3868             cn: [ anc ]
3869         };
3870         if (this.parent().type == 'treeview') {
3871             cfg.cls = 'treeview-menu';
3872         }
3873         if (this.active) {
3874             cfg.cls += ' active';
3875         }
3876         
3877         
3878         
3879         anc.href = this.href || cfg.cn[0].href ;
3880         ctag.html = this.html || cfg.cn[0].html ;
3881         return cfg;
3882     },
3883     
3884     initEvents: function()
3885     {
3886         if (this.parent().type == 'treeview') {
3887             this.el.select('a').on('click', this.onClick, this);
3888         }
3889         
3890         if (this.menu) {
3891             this.menu.parentType = this.xtype;
3892             this.menu.triggerEl = this.el;
3893             this.menu = this.addxtype(Roo.apply({}, this.menu));
3894         }
3895         
3896     },
3897     onClick : function(e)
3898     {
3899         Roo.log('item on click ');
3900         
3901         if(this.preventDefault){
3902             e.preventDefault();
3903         }
3904         //this.parent().hideMenuItems();
3905         
3906         this.fireEvent('click', this, e);
3907     },
3908     getEl : function()
3909     {
3910         return this.el;
3911     } 
3912 });
3913
3914  
3915
3916  /*
3917  * - LGPL
3918  *
3919  * menu separator
3920  * 
3921  */
3922
3923
3924 /**
3925  * @class Roo.bootstrap.MenuSeparator
3926  * @extends Roo.bootstrap.Component
3927  * Bootstrap MenuSeparator class
3928  * 
3929  * @constructor
3930  * Create a new MenuItem
3931  * @param {Object} config The config object
3932  */
3933
3934
3935 Roo.bootstrap.MenuSeparator = function(config){
3936     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3937 };
3938
3939 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3940     
3941     getAutoCreate : function(){
3942         var cfg = {
3943             cls: 'divider',
3944             tag : 'li'
3945         };
3946         
3947         return cfg;
3948     }
3949    
3950 });
3951
3952  
3953
3954  
3955 /*
3956 * Licence: LGPL
3957 */
3958
3959 /**
3960  * @class Roo.bootstrap.Modal
3961  * @extends Roo.bootstrap.Component
3962  * Bootstrap Modal class
3963  * @cfg {String} title Title of dialog
3964  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3965  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3966  * @cfg {Boolean} specificTitle default false
3967  * @cfg {Array} buttons Array of buttons or standard button set..
3968  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3969  * @cfg {Boolean} animate default true
3970  * @cfg {Boolean} allow_close default true
3971  * @cfg {Boolean} fitwindow default false
3972  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3973  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3974  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3975  * @cfg {String} size (sm|lg|xl) default empty
3976  * @cfg {Number} max_width set the max width of modal
3977  * @cfg {Boolean} editableTitle can the title be edited
3978
3979  *
3980  *
3981  * @constructor
3982  * Create a new Modal Dialog
3983  * @param {Object} config The config object
3984  */
3985
3986 Roo.bootstrap.Modal = function(config){
3987     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3988     this.addEvents({
3989         // raw events
3990         /**
3991          * @event btnclick
3992          * The raw btnclick event for the button
3993          * @param {Roo.EventObject} e
3994          */
3995         "btnclick" : true,
3996         /**
3997          * @event resize
3998          * Fire when dialog resize
3999          * @param {Roo.bootstrap.Modal} this
4000          * @param {Roo.EventObject} e
4001          */
4002         "resize" : true,
4003         /**
4004          * @event titlechanged
4005          * Fire when the editable title has been changed
4006          * @param {Roo.bootstrap.Modal} this
4007          * @param {Roo.EventObject} value
4008          */
4009         "titlechanged" : true 
4010         
4011     });
4012     this.buttons = this.buttons || [];
4013
4014     if (this.tmpl) {
4015         this.tmpl = Roo.factory(this.tmpl);
4016     }
4017
4018 };
4019
4020 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4021
4022     title : 'test dialog',
4023
4024     buttons : false,
4025
4026     // set on load...
4027
4028     html: false,
4029
4030     tmp: false,
4031
4032     specificTitle: false,
4033
4034     buttonPosition: 'right',
4035
4036     allow_close : true,
4037
4038     animate : true,
4039
4040     fitwindow: false,
4041     
4042      // private
4043     dialogEl: false,
4044     bodyEl:  false,
4045     footerEl:  false,
4046     titleEl:  false,
4047     closeEl:  false,
4048
4049     size: '',
4050     
4051     max_width: 0,
4052     
4053     max_height: 0,
4054     
4055     fit_content: false,
4056     editableTitle  : false,
4057
4058     onRender : function(ct, position)
4059     {
4060         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4061
4062         if(!this.el){
4063             var cfg = Roo.apply({},  this.getAutoCreate());
4064             cfg.id = Roo.id();
4065             //if(!cfg.name){
4066             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4067             //}
4068             //if (!cfg.name.length) {
4069             //    delete cfg.name;
4070            // }
4071             if (this.cls) {
4072                 cfg.cls += ' ' + this.cls;
4073             }
4074             if (this.style) {
4075                 cfg.style = this.style;
4076             }
4077             this.el = Roo.get(document.body).createChild(cfg, position);
4078         }
4079         //var type = this.el.dom.type;
4080
4081
4082         if(this.tabIndex !== undefined){
4083             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4084         }
4085
4086         this.dialogEl = this.el.select('.modal-dialog',true).first();
4087         this.bodyEl = this.el.select('.modal-body',true).first();
4088         this.closeEl = this.el.select('.modal-header .close', true).first();
4089         this.headerEl = this.el.select('.modal-header',true).first();
4090         this.titleEl = this.el.select('.modal-title',true).first();
4091         this.footerEl = this.el.select('.modal-footer',true).first();
4092
4093         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4094         
4095         //this.el.addClass("x-dlg-modal");
4096
4097         if (this.buttons.length) {
4098             Roo.each(this.buttons, function(bb) {
4099                 var b = Roo.apply({}, bb);
4100                 b.xns = b.xns || Roo.bootstrap;
4101                 b.xtype = b.xtype || 'Button';
4102                 if (typeof(b.listeners) == 'undefined') {
4103                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4104                 }
4105
4106                 var btn = Roo.factory(b);
4107
4108                 btn.render(this.getButtonContainer());
4109
4110             },this);
4111         }
4112         // render the children.
4113         var nitems = [];
4114
4115         if(typeof(this.items) != 'undefined'){
4116             var items = this.items;
4117             delete this.items;
4118
4119             for(var i =0;i < items.length;i++) {
4120                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4121             }
4122         }
4123
4124         this.items = nitems;
4125
4126         // where are these used - they used to be body/close/footer
4127
4128
4129         this.initEvents();
4130         //this.el.addClass([this.fieldClass, this.cls]);
4131
4132     },
4133
4134     getAutoCreate : function()
4135     {
4136         // we will default to modal-body-overflow - might need to remove or make optional later.
4137         var bdy = {
4138                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4139                 html : this.html || ''
4140         };
4141
4142         var title = {
4143             tag: 'h5',
4144             cls : 'modal-title',
4145             html : this.title
4146         };
4147
4148         if(this.specificTitle){ // WTF is this?
4149             title = this.title;
4150         }
4151
4152         var header = [];
4153         if (this.allow_close && Roo.bootstrap.version == 3) {
4154             header.push({
4155                 tag: 'button',
4156                 cls : 'close',
4157                 html : '&times'
4158             });
4159         }
4160
4161         header.push(title);
4162
4163         if (this.editableTitle) {
4164             header.push({
4165                 cls: 'form-control roo-editable-title d-none',
4166                 tag: 'input',
4167                 type: 'text'
4168             });
4169         }
4170         
4171         if (this.allow_close && Roo.bootstrap.version == 4) {
4172             header.push({
4173                 tag: 'button',
4174                 cls : 'close',
4175                 html : '&times'
4176             });
4177         }
4178         
4179         var size = '';
4180
4181         if(this.size.length){
4182             size = 'modal-' + this.size;
4183         }
4184         
4185         var footer = Roo.bootstrap.version == 3 ?
4186             {
4187                 cls : 'modal-footer',
4188                 cn : [
4189                     {
4190                         tag: 'div',
4191                         cls: 'btn-' + this.buttonPosition
4192                     }
4193                 ]
4194
4195             } :
4196             {  // BS4 uses mr-auto on left buttons....
4197                 cls : 'modal-footer'
4198             };
4199
4200             
4201
4202         
4203         
4204         var modal = {
4205             cls: "modal",
4206              cn : [
4207                 {
4208                     cls: "modal-dialog " + size,
4209                     cn : [
4210                         {
4211                             cls : "modal-content",
4212                             cn : [
4213                                 {
4214                                     cls : 'modal-header',
4215                                     cn : header
4216                                 },
4217                                 bdy,
4218                                 footer
4219                             ]
4220
4221                         }
4222                     ]
4223
4224                 }
4225             ]
4226         };
4227
4228         if(this.animate){
4229             modal.cls += ' fade';
4230         }
4231
4232         return modal;
4233
4234     },
4235     getChildContainer : function() {
4236
4237          return this.bodyEl;
4238
4239     },
4240     getButtonContainer : function() {
4241         
4242          return Roo.bootstrap.version == 4 ?
4243             this.el.select('.modal-footer',true).first()
4244             : this.el.select('.modal-footer div',true).first();
4245
4246     },
4247     initEvents : function()
4248     {
4249         if (this.allow_close) {
4250             this.closeEl.on('click', this.hide, this);
4251         }
4252         Roo.EventManager.onWindowResize(this.resize, this, true);
4253         if (this.editableTitle) {
4254             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4255             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4256             this.headerEditEl.on('keyup', function(e) {
4257                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4258                         this.toggleHeaderInput(false)
4259                     }
4260                 }, this);
4261             this.headerEditEl.on('blur', function(e) {
4262                 this.toggleHeaderInput(false)
4263             },this);
4264         }
4265
4266     },
4267   
4268
4269     resize : function()
4270     {
4271         this.maskEl.setSize(
4272             Roo.lib.Dom.getViewWidth(true),
4273             Roo.lib.Dom.getViewHeight(true)
4274         );
4275         
4276         if (this.fitwindow) {
4277             
4278            this.dialogEl.setStyle( { 'max-width' : '100%' });
4279             this.setSize(
4280                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4281                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4282             );
4283             return;
4284         }
4285         
4286         if(this.max_width !== 0) {
4287             
4288             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4289             
4290             if(this.height) {
4291                 this.setSize(w, this.height);
4292                 return;
4293             }
4294             
4295             if(this.max_height) {
4296                 this.setSize(w,Math.min(
4297                     this.max_height,
4298                     Roo.lib.Dom.getViewportHeight(true) - 60
4299                 ));
4300                 
4301                 return;
4302             }
4303             
4304             if(!this.fit_content) {
4305                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4306                 return;
4307             }
4308             
4309             this.setSize(w, Math.min(
4310                 60 +
4311                 this.headerEl.getHeight() + 
4312                 this.footerEl.getHeight() + 
4313                 this.getChildHeight(this.bodyEl.dom.childNodes),
4314                 Roo.lib.Dom.getViewportHeight(true) - 60)
4315             );
4316         }
4317         
4318     },
4319
4320     setSize : function(w,h)
4321     {
4322         if (!w && !h) {
4323             return;
4324         }
4325         
4326         this.resizeTo(w,h);
4327     },
4328
4329     show : function() {
4330
4331         if (!this.rendered) {
4332             this.render();
4333         }
4334         this.toggleHeaderInput(false);
4335         //this.el.setStyle('display', 'block');
4336         this.el.removeClass('hideing');
4337         this.el.dom.style.display='block';
4338         
4339         Roo.get(document.body).addClass('modal-open');
4340  
4341         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4342             
4343             (function(){
4344                 this.el.addClass('show');
4345                 this.el.addClass('in');
4346             }).defer(50, this);
4347         }else{
4348             this.el.addClass('show');
4349             this.el.addClass('in');
4350         }
4351
4352         // not sure how we can show data in here..
4353         //if (this.tmpl) {
4354         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4355         //}
4356
4357         Roo.get(document.body).addClass("x-body-masked");
4358         
4359         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4360         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4361         this.maskEl.dom.style.display = 'block';
4362         this.maskEl.addClass('show');
4363         
4364         
4365         this.resize();
4366         
4367         this.fireEvent('show', this);
4368
4369         // set zindex here - otherwise it appears to be ignored...
4370         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4371
4372         (function () {
4373             this.items.forEach( function(e) {
4374                 e.layout ? e.layout() : false;
4375
4376             });
4377         }).defer(100,this);
4378
4379     },
4380     hide : function()
4381     {
4382         if(this.fireEvent("beforehide", this) !== false){
4383             
4384             this.maskEl.removeClass('show');
4385             
4386             this.maskEl.dom.style.display = '';
4387             Roo.get(document.body).removeClass("x-body-masked");
4388             this.el.removeClass('in');
4389             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4390
4391             if(this.animate){ // why
4392                 this.el.addClass('hideing');
4393                 this.el.removeClass('show');
4394                 (function(){
4395                     if (!this.el.hasClass('hideing')) {
4396                         return; // it's been shown again...
4397                     }
4398                     
4399                     this.el.dom.style.display='';
4400
4401                     Roo.get(document.body).removeClass('modal-open');
4402                     this.el.removeClass('hideing');
4403                 }).defer(150,this);
4404                 
4405             }else{
4406                 this.el.removeClass('show');
4407                 this.el.dom.style.display='';
4408                 Roo.get(document.body).removeClass('modal-open');
4409
4410             }
4411             this.fireEvent('hide', this);
4412         }
4413     },
4414     isVisible : function()
4415     {
4416         
4417         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4418         
4419     },
4420
4421     addButton : function(str, cb)
4422     {
4423
4424
4425         var b = Roo.apply({}, { html : str } );
4426         b.xns = b.xns || Roo.bootstrap;
4427         b.xtype = b.xtype || 'Button';
4428         if (typeof(b.listeners) == 'undefined') {
4429             b.listeners = { click : cb.createDelegate(this)  };
4430         }
4431
4432         var btn = Roo.factory(b);
4433
4434         btn.render(this.getButtonContainer());
4435
4436         return btn;
4437
4438     },
4439
4440     setDefaultButton : function(btn)
4441     {
4442         //this.el.select('.modal-footer').()
4443     },
4444
4445     resizeTo: function(w,h)
4446     {
4447         this.dialogEl.setWidth(w);
4448         
4449         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4450
4451         this.bodyEl.setHeight(h - diff);
4452         
4453         this.fireEvent('resize', this);
4454     },
4455     
4456     setContentSize  : function(w, h)
4457     {
4458
4459     },
4460     onButtonClick: function(btn,e)
4461     {
4462         //Roo.log([a,b,c]);
4463         this.fireEvent('btnclick', btn.name, e);
4464     },
4465      /**
4466      * Set the title of the Dialog
4467      * @param {String} str new Title
4468      */
4469     setTitle: function(str) {
4470         this.titleEl.dom.innerHTML = str;
4471         this.title = str;
4472     },
4473     /**
4474      * Set the body of the Dialog
4475      * @param {String} str new Title
4476      */
4477     setBody: function(str) {
4478         this.bodyEl.dom.innerHTML = str;
4479     },
4480     /**
4481      * Set the body of the Dialog using the template
4482      * @param {Obj} data - apply this data to the template and replace the body contents.
4483      */
4484     applyBody: function(obj)
4485     {
4486         if (!this.tmpl) {
4487             Roo.log("Error - using apply Body without a template");
4488             //code
4489         }
4490         this.tmpl.overwrite(this.bodyEl, obj);
4491     },
4492     
4493     getChildHeight : function(child_nodes)
4494     {
4495         if(
4496             !child_nodes ||
4497             child_nodes.length == 0
4498         ) {
4499             return 0;
4500         }
4501         
4502         var child_height = 0;
4503         
4504         for(var i = 0; i < child_nodes.length; i++) {
4505             
4506             /*
4507             * for modal with tabs...
4508             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4509                 
4510                 var layout_childs = child_nodes[i].childNodes;
4511                 
4512                 for(var j = 0; j < layout_childs.length; j++) {
4513                     
4514                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4515                         
4516                         var layout_body_childs = layout_childs[j].childNodes;
4517                         
4518                         for(var k = 0; k < layout_body_childs.length; k++) {
4519                             
4520                             if(layout_body_childs[k].classList.contains('navbar')) {
4521                                 child_height += layout_body_childs[k].offsetHeight;
4522                                 continue;
4523                             }
4524                             
4525                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4526                                 
4527                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4528                                 
4529                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4530                                     
4531                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4532                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4533                                         continue;
4534                                     }
4535                                     
4536                                 }
4537                                 
4538                             }
4539                             
4540                         }
4541                     }
4542                 }
4543                 continue;
4544             }
4545             */
4546             
4547             child_height += child_nodes[i].offsetHeight;
4548             // Roo.log(child_nodes[i].offsetHeight);
4549         }
4550         
4551         return child_height;
4552     },
4553     toggleHeaderInput : function(is_edit)
4554     {
4555         if (!this.editableTitle) {
4556             return; // not editable.
4557         }
4558         if (is_edit && this.is_header_editing) {
4559             return; // already editing..
4560         }
4561         if (is_edit) {
4562     
4563             this.headerEditEl.dom.value = this.title;
4564             this.headerEditEl.removeClass('d-none');
4565             this.headerEditEl.dom.focus();
4566             this.titleEl.addClass('d-none');
4567             
4568             this.is_header_editing = true;
4569             return
4570         }
4571         // flip back to not editing.
4572         this.title = this.headerEditEl.dom.value;
4573         this.headerEditEl.addClass('d-none');
4574         this.titleEl.removeClass('d-none');
4575         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4576         this.is_header_editing = false;
4577         this.fireEvent('titlechanged', this, this.title);
4578     
4579             
4580         
4581     }
4582
4583 });
4584
4585
4586 Roo.apply(Roo.bootstrap.Modal,  {
4587     /**
4588          * Button config that displays a single OK button
4589          * @type Object
4590          */
4591         OK :  [{
4592             name : 'ok',
4593             weight : 'primary',
4594             html : 'OK'
4595         }],
4596         /**
4597          * Button config that displays Yes and No buttons
4598          * @type Object
4599          */
4600         YESNO : [
4601             {
4602                 name  : 'no',
4603                 html : 'No'
4604             },
4605             {
4606                 name  :'yes',
4607                 weight : 'primary',
4608                 html : 'Yes'
4609             }
4610         ],
4611
4612         /**
4613          * Button config that displays OK and Cancel buttons
4614          * @type Object
4615          */
4616         OKCANCEL : [
4617             {
4618                name : 'cancel',
4619                 html : 'Cancel'
4620             },
4621             {
4622                 name : 'ok',
4623                 weight : 'primary',
4624                 html : 'OK'
4625             }
4626         ],
4627         /**
4628          * Button config that displays Yes, No and Cancel buttons
4629          * @type Object
4630          */
4631         YESNOCANCEL : [
4632             {
4633                 name : 'yes',
4634                 weight : 'primary',
4635                 html : 'Yes'
4636             },
4637             {
4638                 name : 'no',
4639                 html : 'No'
4640             },
4641             {
4642                 name : 'cancel',
4643                 html : 'Cancel'
4644             }
4645         ],
4646         
4647         zIndex : 10001
4648 });
4649
4650 /*
4651  * - LGPL
4652  *
4653  * messagebox - can be used as a replace
4654  * 
4655  */
4656 /**
4657  * @class Roo.MessageBox
4658  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4659  * Example usage:
4660  *<pre><code>
4661 // Basic alert:
4662 Roo.Msg.alert('Status', 'Changes saved successfully.');
4663
4664 // Prompt for user data:
4665 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4666     if (btn == 'ok'){
4667         // process text value...
4668     }
4669 });
4670
4671 // Show a dialog using config options:
4672 Roo.Msg.show({
4673    title:'Save Changes?',
4674    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4675    buttons: Roo.Msg.YESNOCANCEL,
4676    fn: processResult,
4677    animEl: 'elId'
4678 });
4679 </code></pre>
4680  * @singleton
4681  */
4682 Roo.bootstrap.MessageBox = function(){
4683     var dlg, opt, mask, waitTimer;
4684     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4685     var buttons, activeTextEl, bwidth;
4686
4687     
4688     // private
4689     var handleButton = function(button){
4690         dlg.hide();
4691         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4692     };
4693
4694     // private
4695     var handleHide = function(){
4696         if(opt && opt.cls){
4697             dlg.el.removeClass(opt.cls);
4698         }
4699         //if(waitTimer){
4700         //    Roo.TaskMgr.stop(waitTimer);
4701         //    waitTimer = null;
4702         //}
4703     };
4704
4705     // private
4706     var updateButtons = function(b){
4707         var width = 0;
4708         if(!b){
4709             buttons["ok"].hide();
4710             buttons["cancel"].hide();
4711             buttons["yes"].hide();
4712             buttons["no"].hide();
4713             dlg.footerEl.hide();
4714             
4715             return width;
4716         }
4717         dlg.footerEl.show();
4718         for(var k in buttons){
4719             if(typeof buttons[k] != "function"){
4720                 if(b[k]){
4721                     buttons[k].show();
4722                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4723                     width += buttons[k].el.getWidth()+15;
4724                 }else{
4725                     buttons[k].hide();
4726                 }
4727             }
4728         }
4729         return width;
4730     };
4731
4732     // private
4733     var handleEsc = function(d, k, e){
4734         if(opt && opt.closable !== false){
4735             dlg.hide();
4736         }
4737         if(e){
4738             e.stopEvent();
4739         }
4740     };
4741
4742     return {
4743         /**
4744          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4745          * @return {Roo.BasicDialog} The BasicDialog element
4746          */
4747         getDialog : function(){
4748            if(!dlg){
4749                 dlg = new Roo.bootstrap.Modal( {
4750                     //draggable: true,
4751                     //resizable:false,
4752                     //constraintoviewport:false,
4753                     //fixedcenter:true,
4754                     //collapsible : false,
4755                     //shim:true,
4756                     //modal: true,
4757                 //    width: 'auto',
4758                   //  height:100,
4759                     //buttonAlign:"center",
4760                     closeClick : function(){
4761                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4762                             handleButton("no");
4763                         }else{
4764                             handleButton("cancel");
4765                         }
4766                     }
4767                 });
4768                 dlg.render();
4769                 dlg.on("hide", handleHide);
4770                 mask = dlg.mask;
4771                 //dlg.addKeyListener(27, handleEsc);
4772                 buttons = {};
4773                 this.buttons = buttons;
4774                 var bt = this.buttonText;
4775                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4776                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4777                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4778                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4779                 //Roo.log(buttons);
4780                 bodyEl = dlg.bodyEl.createChild({
4781
4782                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4783                         '<textarea class="roo-mb-textarea"></textarea>' +
4784                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4785                 });
4786                 msgEl = bodyEl.dom.firstChild;
4787                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4788                 textboxEl.enableDisplayMode();
4789                 textboxEl.addKeyListener([10,13], function(){
4790                     if(dlg.isVisible() && opt && opt.buttons){
4791                         if(opt.buttons.ok){
4792                             handleButton("ok");
4793                         }else if(opt.buttons.yes){
4794                             handleButton("yes");
4795                         }
4796                     }
4797                 });
4798                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4799                 textareaEl.enableDisplayMode();
4800                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4801                 progressEl.enableDisplayMode();
4802                 
4803                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4804                 var pf = progressEl.dom.firstChild;
4805                 if (pf) {
4806                     pp = Roo.get(pf.firstChild);
4807                     pp.setHeight(pf.offsetHeight);
4808                 }
4809                 
4810             }
4811             return dlg;
4812         },
4813
4814         /**
4815          * Updates the message box body text
4816          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4817          * the XHTML-compliant non-breaking space character '&amp;#160;')
4818          * @return {Roo.MessageBox} This message box
4819          */
4820         updateText : function(text)
4821         {
4822             if(!dlg.isVisible() && !opt.width){
4823                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4824                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4825             }
4826             msgEl.innerHTML = text || '&#160;';
4827       
4828             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4829             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4830             var w = Math.max(
4831                     Math.min(opt.width || cw , this.maxWidth), 
4832                     Math.max(opt.minWidth || this.minWidth, bwidth)
4833             );
4834             if(opt.prompt){
4835                 activeTextEl.setWidth(w);
4836             }
4837             if(dlg.isVisible()){
4838                 dlg.fixedcenter = false;
4839             }
4840             // to big, make it scroll. = But as usual stupid IE does not support
4841             // !important..
4842             
4843             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4844                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4845                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4846             } else {
4847                 bodyEl.dom.style.height = '';
4848                 bodyEl.dom.style.overflowY = '';
4849             }
4850             if (cw > w) {
4851                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4852             } else {
4853                 bodyEl.dom.style.overflowX = '';
4854             }
4855             
4856             dlg.setContentSize(w, bodyEl.getHeight());
4857             if(dlg.isVisible()){
4858                 dlg.fixedcenter = true;
4859             }
4860             return this;
4861         },
4862
4863         /**
4864          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4865          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4866          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4867          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4868          * @return {Roo.MessageBox} This message box
4869          */
4870         updateProgress : function(value, text){
4871             if(text){
4872                 this.updateText(text);
4873             }
4874             
4875             if (pp) { // weird bug on my firefox - for some reason this is not defined
4876                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4877                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4878             }
4879             return this;
4880         },        
4881
4882         /**
4883          * Returns true if the message box is currently displayed
4884          * @return {Boolean} True if the message box is visible, else false
4885          */
4886         isVisible : function(){
4887             return dlg && dlg.isVisible();  
4888         },
4889
4890         /**
4891          * Hides the message box if it is displayed
4892          */
4893         hide : function(){
4894             if(this.isVisible()){
4895                 dlg.hide();
4896             }  
4897         },
4898
4899         /**
4900          * Displays a new message box, or reinitializes an existing message box, based on the config options
4901          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4902          * The following config object properties are supported:
4903          * <pre>
4904 Property    Type             Description
4905 ----------  ---------------  ------------------------------------------------------------------------------------
4906 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4907                                    closes (defaults to undefined)
4908 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4909                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4910 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4911                                    progress and wait dialogs will ignore this property and always hide the
4912                                    close button as they can only be closed programmatically.
4913 cls               String           A custom CSS class to apply to the message box element
4914 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4915                                    displayed (defaults to 75)
4916 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4917                                    function will be btn (the name of the button that was clicked, if applicable,
4918                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4919                                    Progress and wait dialogs will ignore this option since they do not respond to
4920                                    user actions and can only be closed programmatically, so any required function
4921                                    should be called by the same code after it closes the dialog.
4922 icon              String           A CSS class that provides a background image to be used as an icon for
4923                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4924 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4925 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4926 modal             Boolean          False to allow user interaction with the page while the message box is
4927                                    displayed (defaults to true)
4928 msg               String           A string that will replace the existing message box body text (defaults
4929                                    to the XHTML-compliant non-breaking space character '&#160;')
4930 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4931 progress          Boolean          True to display a progress bar (defaults to false)
4932 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4933 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4934 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4935 title             String           The title text
4936 value             String           The string value to set into the active textbox element if displayed
4937 wait              Boolean          True to display a progress bar (defaults to false)
4938 width             Number           The width of the dialog in pixels
4939 </pre>
4940          *
4941          * Example usage:
4942          * <pre><code>
4943 Roo.Msg.show({
4944    title: 'Address',
4945    msg: 'Please enter your address:',
4946    width: 300,
4947    buttons: Roo.MessageBox.OKCANCEL,
4948    multiline: true,
4949    fn: saveAddress,
4950    animEl: 'addAddressBtn'
4951 });
4952 </code></pre>
4953          * @param {Object} config Configuration options
4954          * @return {Roo.MessageBox} This message box
4955          */
4956         show : function(options)
4957         {
4958             
4959             // this causes nightmares if you show one dialog after another
4960             // especially on callbacks..
4961              
4962             if(this.isVisible()){
4963                 
4964                 this.hide();
4965                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4966                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4967                 Roo.log("New Dialog Message:" +  options.msg )
4968                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4969                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4970                 
4971             }
4972             var d = this.getDialog();
4973             opt = options;
4974             d.setTitle(opt.title || "&#160;");
4975             d.closeEl.setDisplayed(opt.closable !== false);
4976             activeTextEl = textboxEl;
4977             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4978             if(opt.prompt){
4979                 if(opt.multiline){
4980                     textboxEl.hide();
4981                     textareaEl.show();
4982                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4983                         opt.multiline : this.defaultTextHeight);
4984                     activeTextEl = textareaEl;
4985                 }else{
4986                     textboxEl.show();
4987                     textareaEl.hide();
4988                 }
4989             }else{
4990                 textboxEl.hide();
4991                 textareaEl.hide();
4992             }
4993             progressEl.setDisplayed(opt.progress === true);
4994             if (opt.progress) {
4995                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4996             }
4997             this.updateProgress(0);
4998             activeTextEl.dom.value = opt.value || "";
4999             if(opt.prompt){
5000                 dlg.setDefaultButton(activeTextEl);
5001             }else{
5002                 var bs = opt.buttons;
5003                 var db = null;
5004                 if(bs && bs.ok){
5005                     db = buttons["ok"];
5006                 }else if(bs && bs.yes){
5007                     db = buttons["yes"];
5008                 }
5009                 dlg.setDefaultButton(db);
5010             }
5011             bwidth = updateButtons(opt.buttons);
5012             this.updateText(opt.msg);
5013             if(opt.cls){
5014                 d.el.addClass(opt.cls);
5015             }
5016             d.proxyDrag = opt.proxyDrag === true;
5017             d.modal = opt.modal !== false;
5018             d.mask = opt.modal !== false ? mask : false;
5019             if(!d.isVisible()){
5020                 // force it to the end of the z-index stack so it gets a cursor in FF
5021                 document.body.appendChild(dlg.el.dom);
5022                 d.animateTarget = null;
5023                 d.show(options.animEl);
5024             }
5025             return this;
5026         },
5027
5028         /**
5029          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5030          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5031          * and closing the message box when the process is complete.
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @return {Roo.MessageBox} This message box
5035          */
5036         progress : function(title, msg){
5037             this.show({
5038                 title : title,
5039                 msg : msg,
5040                 buttons: false,
5041                 progress:true,
5042                 closable:false,
5043                 minWidth: this.minProgressWidth,
5044                 modal : true
5045             });
5046             return this;
5047         },
5048
5049         /**
5050          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5051          * If a callback function is passed it will be called after the user clicks the button, and the
5052          * id of the button that was clicked will be passed as the only parameter to the callback
5053          * (could also be the top-right close button).
5054          * @param {String} title The title bar text
5055          * @param {String} msg The message box body text
5056          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5057          * @param {Object} scope (optional) The scope of the callback function
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         alert : function(title, msg, fn, scope)
5061         {
5062             this.show({
5063                 title : title,
5064                 msg : msg,
5065                 buttons: this.OK,
5066                 fn: fn,
5067                 closable : false,
5068                 scope : scope,
5069                 modal : true
5070             });
5071             return this;
5072         },
5073
5074         /**
5075          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5076          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5077          * You are responsible for closing the message box when the process is complete.
5078          * @param {String} msg The message box body text
5079          * @param {String} title (optional) The title bar text
5080          * @return {Roo.MessageBox} This message box
5081          */
5082         wait : function(msg, title){
5083             this.show({
5084                 title : title,
5085                 msg : msg,
5086                 buttons: false,
5087                 closable:false,
5088                 progress:true,
5089                 modal:true,
5090                 width:300,
5091                 wait:true
5092             });
5093             waitTimer = Roo.TaskMgr.start({
5094                 run: function(i){
5095                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5096                 },
5097                 interval: 1000
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5104          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5105          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5106          * @param {String} title The title bar text
5107          * @param {String} msg The message box body text
5108          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5109          * @param {Object} scope (optional) The scope of the callback function
5110          * @return {Roo.MessageBox} This message box
5111          */
5112         confirm : function(title, msg, fn, scope){
5113             this.show({
5114                 title : title,
5115                 msg : msg,
5116                 buttons: this.YESNO,
5117                 fn: fn,
5118                 scope : scope,
5119                 modal : true
5120             });
5121             return this;
5122         },
5123
5124         /**
5125          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5126          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5127          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5128          * (could also be the top-right close button) and the text that was entered will be passed as the two
5129          * parameters to the callback.
5130          * @param {String} title The title bar text
5131          * @param {String} msg The message box body text
5132          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5133          * @param {Object} scope (optional) The scope of the callback function
5134          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5135          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5136          * @return {Roo.MessageBox} This message box
5137          */
5138         prompt : function(title, msg, fn, scope, multiline){
5139             this.show({
5140                 title : title,
5141                 msg : msg,
5142                 buttons: this.OKCANCEL,
5143                 fn: fn,
5144                 minWidth:250,
5145                 scope : scope,
5146                 prompt:true,
5147                 multiline: multiline,
5148                 modal : true
5149             });
5150             return this;
5151         },
5152
5153         /**
5154          * Button config that displays a single OK button
5155          * @type Object
5156          */
5157         OK : {ok:true},
5158         /**
5159          * Button config that displays Yes and No buttons
5160          * @type Object
5161          */
5162         YESNO : {yes:true, no:true},
5163         /**
5164          * Button config that displays OK and Cancel buttons
5165          * @type Object
5166          */
5167         OKCANCEL : {ok:true, cancel:true},
5168         /**
5169          * Button config that displays Yes, No and Cancel buttons
5170          * @type Object
5171          */
5172         YESNOCANCEL : {yes:true, no:true, cancel:true},
5173
5174         /**
5175          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5176          * @type Number
5177          */
5178         defaultTextHeight : 75,
5179         /**
5180          * The maximum width in pixels of the message box (defaults to 600)
5181          * @type Number
5182          */
5183         maxWidth : 600,
5184         /**
5185          * The minimum width in pixels of the message box (defaults to 100)
5186          * @type Number
5187          */
5188         minWidth : 100,
5189         /**
5190          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5191          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5192          * @type Number
5193          */
5194         minProgressWidth : 250,
5195         /**
5196          * An object containing the default button text strings that can be overriden for localized language support.
5197          * Supported properties are: ok, cancel, yes and no.
5198          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5199          * @type Object
5200          */
5201         buttonText : {
5202             ok : "OK",
5203             cancel : "Cancel",
5204             yes : "Yes",
5205             no : "No"
5206         }
5207     };
5208 }();
5209
5210 /**
5211  * Shorthand for {@link Roo.MessageBox}
5212  */
5213 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5214 Roo.Msg = Roo.Msg || Roo.MessageBox;
5215 /*
5216  * - LGPL
5217  *
5218  * navbar
5219  * 
5220  */
5221
5222 /**
5223  * @class Roo.bootstrap.Navbar
5224  * @extends Roo.bootstrap.Component
5225  * Bootstrap Navbar class
5226
5227  * @constructor
5228  * Create a new Navbar
5229  * @param {Object} config The config object
5230  */
5231
5232
5233 Roo.bootstrap.Navbar = function(config){
5234     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5235     this.addEvents({
5236         // raw events
5237         /**
5238          * @event beforetoggle
5239          * Fire before toggle the menu
5240          * @param {Roo.EventObject} e
5241          */
5242         "beforetoggle" : true
5243     });
5244 };
5245
5246 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5247     
5248     
5249    
5250     // private
5251     navItems : false,
5252     loadMask : false,
5253     
5254     
5255     getAutoCreate : function(){
5256         
5257         
5258         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5259         
5260     },
5261     
5262     initEvents :function ()
5263     {
5264         //Roo.log(this.el.select('.navbar-toggle',true));
5265         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5266         
5267         var mark = {
5268             tag: "div",
5269             cls:"x-dlg-mask"
5270         };
5271         
5272         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5273         
5274         var size = this.el.getSize();
5275         this.maskEl.setSize(size.width, size.height);
5276         this.maskEl.enableDisplayMode("block");
5277         this.maskEl.hide();
5278         
5279         if(this.loadMask){
5280             this.maskEl.show();
5281         }
5282     },
5283     
5284     
5285     getChildContainer : function()
5286     {
5287         if (this.el && this.el.select('.collapse').getCount()) {
5288             return this.el.select('.collapse',true).first();
5289         }
5290         
5291         return this.el;
5292     },
5293     
5294     mask : function()
5295     {
5296         this.maskEl.show();
5297     },
5298     
5299     unmask : function()
5300     {
5301         this.maskEl.hide();
5302     },
5303     onToggle : function()
5304     {
5305         
5306         if(this.fireEvent('beforetoggle', this) === false){
5307             return;
5308         }
5309         var ce = this.el.select('.navbar-collapse',true).first();
5310       
5311         if (!ce.hasClass('show')) {
5312            this.expand();
5313         } else {
5314             this.collapse();
5315         }
5316         
5317         
5318     
5319     },
5320     /**
5321      * Expand the navbar pulldown 
5322      */
5323     expand : function ()
5324     {
5325        
5326         var ce = this.el.select('.navbar-collapse',true).first();
5327         if (ce.hasClass('collapsing')) {
5328             return;
5329         }
5330         ce.dom.style.height = '';
5331                // show it...
5332         ce.addClass('in'); // old...
5333         ce.removeClass('collapse');
5334         ce.addClass('show');
5335         var h = ce.getHeight();
5336         Roo.log(h);
5337         ce.removeClass('show');
5338         // at this point we should be able to see it..
5339         ce.addClass('collapsing');
5340         
5341         ce.setHeight(0); // resize it ...
5342         ce.on('transitionend', function() {
5343             //Roo.log('done transition');
5344             ce.removeClass('collapsing');
5345             ce.addClass('show');
5346             ce.removeClass('collapse');
5347
5348             ce.dom.style.height = '';
5349         }, this, { single: true} );
5350         ce.setHeight(h);
5351         ce.dom.scrollTop = 0;
5352     },
5353     /**
5354      * Collapse the navbar pulldown 
5355      */
5356     collapse : function()
5357     {
5358          var ce = this.el.select('.navbar-collapse',true).first();
5359        
5360         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5361             // it's collapsed or collapsing..
5362             return;
5363         }
5364         ce.removeClass('in'); // old...
5365         ce.setHeight(ce.getHeight());
5366         ce.removeClass('show');
5367         ce.addClass('collapsing');
5368         
5369         ce.on('transitionend', function() {
5370             ce.dom.style.height = '';
5371             ce.removeClass('collapsing');
5372             ce.addClass('collapse');
5373         }, this, { single: true} );
5374         ce.setHeight(0);
5375     }
5376     
5377     
5378     
5379 });
5380
5381
5382
5383  
5384
5385  /*
5386  * - LGPL
5387  *
5388  * navbar
5389  * 
5390  */
5391
5392 /**
5393  * @class Roo.bootstrap.NavSimplebar
5394  * @extends Roo.bootstrap.Navbar
5395  * Bootstrap Sidebar class
5396  *
5397  * @cfg {Boolean} inverse is inverted color
5398  * 
5399  * @cfg {String} type (nav | pills | tabs)
5400  * @cfg {Boolean} arrangement stacked | justified
5401  * @cfg {String} align (left | right) alignment
5402  * 
5403  * @cfg {Boolean} main (true|false) main nav bar? default false
5404  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5405  * 
5406  * @cfg {String} tag (header|footer|nav|div) default is nav 
5407
5408  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5409  * 
5410  * 
5411  * @constructor
5412  * Create a new Sidebar
5413  * @param {Object} config The config object
5414  */
5415
5416
5417 Roo.bootstrap.NavSimplebar = function(config){
5418     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5419 };
5420
5421 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5422     
5423     inverse: false,
5424     
5425     type: false,
5426     arrangement: '',
5427     align : false,
5428     
5429     weight : 'light',
5430     
5431     main : false,
5432     
5433     
5434     tag : false,
5435     
5436     
5437     getAutoCreate : function(){
5438         
5439         
5440         var cfg = {
5441             tag : this.tag || 'div',
5442             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5443         };
5444         if (['light','white'].indexOf(this.weight) > -1) {
5445             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5446         }
5447         cfg.cls += ' bg-' + this.weight;
5448         
5449         if (this.inverse) {
5450             cfg.cls += ' navbar-inverse';
5451             
5452         }
5453         
5454         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5455         
5456         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5457             return cfg;
5458         }
5459         
5460         
5461     
5462         
5463         cfg.cn = [
5464             {
5465                 cls: 'nav nav-' + this.xtype,
5466                 tag : 'ul'
5467             }
5468         ];
5469         
5470          
5471         this.type = this.type || 'nav';
5472         if (['tabs','pills'].indexOf(this.type) != -1) {
5473             cfg.cn[0].cls += ' nav-' + this.type
5474         
5475         
5476         } else {
5477             if (this.type!=='nav') {
5478                 Roo.log('nav type must be nav/tabs/pills')
5479             }
5480             cfg.cn[0].cls += ' navbar-nav'
5481         }
5482         
5483         
5484         
5485         
5486         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5487             cfg.cn[0].cls += ' nav-' + this.arrangement;
5488         }
5489         
5490         
5491         if (this.align === 'right') {
5492             cfg.cn[0].cls += ' navbar-right';
5493         }
5494         
5495         
5496         
5497         
5498         return cfg;
5499     
5500         
5501     }
5502     
5503     
5504     
5505 });
5506
5507
5508
5509  
5510
5511  
5512        /*
5513  * - LGPL
5514  *
5515  * navbar
5516  * navbar-fixed-top
5517  * navbar-expand-md  fixed-top 
5518  */
5519
5520 /**
5521  * @class Roo.bootstrap.NavHeaderbar
5522  * @extends Roo.bootstrap.NavSimplebar
5523  * Bootstrap Sidebar class
5524  *
5525  * @cfg {String} brand what is brand
5526  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5527  * @cfg {String} brand_href href of the brand
5528  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5529  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5530  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5531  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5532  * 
5533  * @constructor
5534  * Create a new Sidebar
5535  * @param {Object} config The config object
5536  */
5537
5538
5539 Roo.bootstrap.NavHeaderbar = function(config){
5540     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5541       
5542 };
5543
5544 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5545     
5546     position: '',
5547     brand: '',
5548     brand_href: false,
5549     srButton : true,
5550     autohide : false,
5551     desktopCenter : false,
5552    
5553     
5554     getAutoCreate : function(){
5555         
5556         var   cfg = {
5557             tag: this.nav || 'nav',
5558             cls: 'navbar navbar-expand-md',
5559             role: 'navigation',
5560             cn: []
5561         };
5562         
5563         var cn = cfg.cn;
5564         if (this.desktopCenter) {
5565             cn.push({cls : 'container', cn : []});
5566             cn = cn[0].cn;
5567         }
5568         
5569         if(this.srButton){
5570             var btn = {
5571                 tag: 'button',
5572                 type: 'button',
5573                 cls: 'navbar-toggle navbar-toggler',
5574                 'data-toggle': 'collapse',
5575                 cn: [
5576                     {
5577                         tag: 'span',
5578                         cls: 'sr-only',
5579                         html: 'Toggle navigation'
5580                     },
5581                     {
5582                         tag: 'span',
5583                         cls: 'icon-bar navbar-toggler-icon'
5584                     },
5585                     {
5586                         tag: 'span',
5587                         cls: 'icon-bar'
5588                     },
5589                     {
5590                         tag: 'span',
5591                         cls: 'icon-bar'
5592                     }
5593                 ]
5594             };
5595             
5596             cn.push( Roo.bootstrap.version == 4 ? btn : {
5597                 tag: 'div',
5598                 cls: 'navbar-header',
5599                 cn: [
5600                     btn
5601                 ]
5602             });
5603         }
5604         
5605         cn.push({
5606             tag: 'div',
5607             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5608             cn : []
5609         });
5610         
5611         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5612         
5613         if (['light','white'].indexOf(this.weight) > -1) {
5614             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5615         }
5616         cfg.cls += ' bg-' + this.weight;
5617         
5618         
5619         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5620             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5621             
5622             // tag can override this..
5623             
5624             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5625         }
5626         
5627         if (this.brand !== '') {
5628             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5629             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5630                 tag: 'a',
5631                 href: this.brand_href ? this.brand_href : '#',
5632                 cls: 'navbar-brand',
5633                 cn: [
5634                 this.brand
5635                 ]
5636             });
5637         }
5638         
5639         if(this.main){
5640             cfg.cls += ' main-nav';
5641         }
5642         
5643         
5644         return cfg;
5645
5646         
5647     },
5648     getHeaderChildContainer : function()
5649     {
5650         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5651             return this.el.select('.navbar-header',true).first();
5652         }
5653         
5654         return this.getChildContainer();
5655     },
5656     
5657     getChildContainer : function()
5658     {
5659          
5660         return this.el.select('.roo-navbar-collapse',true).first();
5661          
5662         
5663     },
5664     
5665     initEvents : function()
5666     {
5667         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5668         
5669         if (this.autohide) {
5670             
5671             var prevScroll = 0;
5672             var ft = this.el;
5673             
5674             Roo.get(document).on('scroll',function(e) {
5675                 var ns = Roo.get(document).getScroll().top;
5676                 var os = prevScroll;
5677                 prevScroll = ns;
5678                 
5679                 if(ns > os){
5680                     ft.removeClass('slideDown');
5681                     ft.addClass('slideUp');
5682                     return;
5683                 }
5684                 ft.removeClass('slideUp');
5685                 ft.addClass('slideDown');
5686                  
5687               
5688           },this);
5689         }
5690     }    
5691     
5692 });
5693
5694
5695
5696  
5697
5698  /*
5699  * - LGPL
5700  *
5701  * navbar
5702  * 
5703  */
5704
5705 /**
5706  * @class Roo.bootstrap.NavSidebar
5707  * @extends Roo.bootstrap.Navbar
5708  * Bootstrap Sidebar class
5709  * 
5710  * @constructor
5711  * Create a new Sidebar
5712  * @param {Object} config The config object
5713  */
5714
5715
5716 Roo.bootstrap.NavSidebar = function(config){
5717     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5718 };
5719
5720 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5721     
5722     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5723     
5724     getAutoCreate : function(){
5725         
5726         
5727         return  {
5728             tag: 'div',
5729             cls: 'sidebar sidebar-nav'
5730         };
5731     
5732         
5733     }
5734     
5735     
5736     
5737 });
5738
5739
5740
5741  
5742
5743  /*
5744  * - LGPL
5745  *
5746  * nav group
5747  * 
5748  */
5749
5750 /**
5751  * @class Roo.bootstrap.NavGroup
5752  * @extends Roo.bootstrap.Component
5753  * Bootstrap NavGroup class
5754  * @cfg {String} align (left|right)
5755  * @cfg {Boolean} inverse
5756  * @cfg {String} type (nav|pills|tab) default nav
5757  * @cfg {String} navId - reference Id for navbar.
5758  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5759  * 
5760  * @constructor
5761  * Create a new nav group
5762  * @param {Object} config The config object
5763  */
5764
5765 Roo.bootstrap.NavGroup = function(config){
5766     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5767     this.navItems = [];
5768    
5769     Roo.bootstrap.NavGroup.register(this);
5770      this.addEvents({
5771         /**
5772              * @event changed
5773              * Fires when the active item changes
5774              * @param {Roo.bootstrap.NavGroup} this
5775              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5776              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5777          */
5778         'changed': true
5779      });
5780     
5781 };
5782
5783 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5784     
5785     align: '',
5786     inverse: false,
5787     form: false,
5788     type: 'nav',
5789     navId : '',
5790     // private
5791     pilltype : true,
5792     
5793     navItems : false, 
5794     
5795     getAutoCreate : function()
5796     {
5797         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5798         
5799         cfg = {
5800             tag : 'ul',
5801             cls: 'nav' 
5802         };
5803         if (Roo.bootstrap.version == 4) {
5804             if (['tabs','pills'].indexOf(this.type) != -1) {
5805                 cfg.cls += ' nav-' + this.type; 
5806             } else {
5807                 // trying to remove so header bar can right align top?
5808                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5809                     // do not use on header bar... 
5810                     cfg.cls += ' navbar-nav';
5811                 }
5812             }
5813             
5814         } else {
5815             if (['tabs','pills'].indexOf(this.type) != -1) {
5816                 cfg.cls += ' nav-' + this.type
5817             } else {
5818                 if (this.type !== 'nav') {
5819                     Roo.log('nav type must be nav/tabs/pills')
5820                 }
5821                 cfg.cls += ' navbar-nav'
5822             }
5823         }
5824         
5825         if (this.parent() && this.parent().sidebar) {
5826             cfg = {
5827                 tag: 'ul',
5828                 cls: 'dashboard-menu sidebar-menu'
5829             };
5830             
5831             return cfg;
5832         }
5833         
5834         if (this.form === true) {
5835             cfg = {
5836                 tag: 'form',
5837                 cls: 'navbar-form form-inline'
5838             };
5839             //nav navbar-right ml-md-auto
5840             if (this.align === 'right') {
5841                 cfg.cls += ' navbar-right ml-md-auto';
5842             } else {
5843                 cfg.cls += ' navbar-left';
5844             }
5845         }
5846         
5847         if (this.align === 'right') {
5848             cfg.cls += ' navbar-right ml-md-auto';
5849         } else {
5850             cfg.cls += ' mr-auto';
5851         }
5852         
5853         if (this.inverse) {
5854             cfg.cls += ' navbar-inverse';
5855             
5856         }
5857         
5858         
5859         return cfg;
5860     },
5861     /**
5862     * sets the active Navigation item
5863     * @param {Roo.bootstrap.NavItem} the new current navitem
5864     */
5865     setActiveItem : function(item)
5866     {
5867         var prev = false;
5868         Roo.each(this.navItems, function(v){
5869             if (v == item) {
5870                 return ;
5871             }
5872             if (v.isActive()) {
5873                 v.setActive(false, true);
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879
5880         item.setActive(true, true);
5881         this.fireEvent('changed', this, item, prev);
5882         
5883         
5884     },
5885     /**
5886     * gets the active Navigation item
5887     * @return {Roo.bootstrap.NavItem} the current navitem
5888     */
5889     getActive : function()
5890     {
5891         
5892         var prev = false;
5893         Roo.each(this.navItems, function(v){
5894             
5895             if (v.isActive()) {
5896                 prev = v;
5897                 
5898             }
5899             
5900         });
5901         return prev;
5902     },
5903     
5904     indexOfNav : function()
5905     {
5906         
5907         var prev = false;
5908         Roo.each(this.navItems, function(v,i){
5909             
5910             if (v.isActive()) {
5911                 prev = i;
5912                 
5913             }
5914             
5915         });
5916         return prev;
5917     },
5918     /**
5919     * adds a Navigation item
5920     * @param {Roo.bootstrap.NavItem} the navitem to add
5921     */
5922     addItem : function(cfg)
5923     {
5924         if (this.form && Roo.bootstrap.version == 4) {
5925             cfg.tag = 'div';
5926         }
5927         var cn = new Roo.bootstrap.NavItem(cfg);
5928         this.register(cn);
5929         cn.parentId = this.id;
5930         cn.onRender(this.el, null);
5931         return cn;
5932     },
5933     /**
5934     * register a Navigation item
5935     * @param {Roo.bootstrap.NavItem} the navitem to add
5936     */
5937     register : function(item)
5938     {
5939         this.navItems.push( item);
5940         item.navId = this.navId;
5941     
5942     },
5943     
5944     /**
5945     * clear all the Navigation item
5946     */
5947    
5948     clearAll : function()
5949     {
5950         this.navItems = [];
5951         this.el.dom.innerHTML = '';
5952     },
5953     
5954     getNavItem: function(tabId)
5955     {
5956         var ret = false;
5957         Roo.each(this.navItems, function(e) {
5958             if (e.tabId == tabId) {
5959                ret =  e;
5960                return false;
5961             }
5962             return true;
5963             
5964         });
5965         return ret;
5966     },
5967     
5968     setActiveNext : function()
5969     {
5970         var i = this.indexOfNav(this.getActive());
5971         if (i > this.navItems.length) {
5972             return;
5973         }
5974         this.setActiveItem(this.navItems[i+1]);
5975     },
5976     setActivePrev : function()
5977     {
5978         var i = this.indexOfNav(this.getActive());
5979         if (i  < 1) {
5980             return;
5981         }
5982         this.setActiveItem(this.navItems[i-1]);
5983     },
5984     clearWasActive : function(except) {
5985         Roo.each(this.navItems, function(e) {
5986             if (e.tabId != except.tabId && e.was_active) {
5987                e.was_active = false;
5988                return false;
5989             }
5990             return true;
5991             
5992         });
5993     },
5994     getWasActive : function ()
5995     {
5996         var r = false;
5997         Roo.each(this.navItems, function(e) {
5998             if (e.was_active) {
5999                r = e;
6000                return false;
6001             }
6002             return true;
6003             
6004         });
6005         return r;
6006     }
6007     
6008     
6009 });
6010
6011  
6012 Roo.apply(Roo.bootstrap.NavGroup, {
6013     
6014     groups: {},
6015      /**
6016     * register a Navigation Group
6017     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6018     */
6019     register : function(navgrp)
6020     {
6021         this.groups[navgrp.navId] = navgrp;
6022         
6023     },
6024     /**
6025     * fetch a Navigation Group based on the navigation ID
6026     * @param {string} the navgroup to add
6027     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6028     */
6029     get: function(navId) {
6030         if (typeof(this.groups[navId]) == 'undefined') {
6031             return false;
6032             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6033         }
6034         return this.groups[navId] ;
6035     }
6036     
6037     
6038     
6039 });
6040
6041  /*
6042  * - LGPL
6043  *
6044  * row
6045  * 
6046  */
6047
6048 /**
6049  * @class Roo.bootstrap.NavItem
6050  * @extends Roo.bootstrap.Component
6051  * Bootstrap Navbar.NavItem class
6052  * @cfg {String} href  link to
6053  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6054  * @cfg {Boolean} button_outline show and outlined button
6055  * @cfg {String} html content of button
6056  * @cfg {String} badge text inside badge
6057  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6058  * @cfg {String} glyphicon DEPRICATED - use fa
6059  * @cfg {String} icon DEPRICATED - use fa
6060  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6061  * @cfg {Boolean} active Is item active
6062  * @cfg {Boolean} disabled Is item disabled
6063  * @cfg {String} linkcls  Link Class
6064  * @cfg {Boolean} preventDefault (true | false) default false
6065  * @cfg {String} tabId the tab that this item activates.
6066  * @cfg {String} tagtype (a|span) render as a href or span?
6067  * @cfg {Boolean} animateRef (true|false) link to element default false  
6068   
6069  * @constructor
6070  * Create a new Navbar Item
6071  * @param {Object} config The config object
6072  */
6073 Roo.bootstrap.NavItem = function(config){
6074     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6075     this.addEvents({
6076         // raw events
6077         /**
6078          * @event click
6079          * The raw click event for the entire grid.
6080          * @param {Roo.EventObject} e
6081          */
6082         "click" : true,
6083          /**
6084             * @event changed
6085             * Fires when the active item active state changes
6086             * @param {Roo.bootstrap.NavItem} this
6087             * @param {boolean} state the new state
6088              
6089          */
6090         'changed': true,
6091         /**
6092             * @event scrollto
6093             * Fires when scroll to element
6094             * @param {Roo.bootstrap.NavItem} this
6095             * @param {Object} options
6096             * @param {Roo.EventObject} e
6097              
6098          */
6099         'scrollto': true
6100     });
6101    
6102 };
6103
6104 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6105     
6106     href: false,
6107     html: '',
6108     badge: '',
6109     icon: false,
6110     fa : false,
6111     glyphicon: false,
6112     active: false,
6113     preventDefault : false,
6114     tabId : false,
6115     tagtype : 'a',
6116     tag: 'li',
6117     disabled : false,
6118     animateRef : false,
6119     was_active : false,
6120     button_weight : '',
6121     button_outline : false,
6122     linkcls : '',
6123     navLink: false,
6124     
6125     getAutoCreate : function(){
6126          
6127         var cfg = {
6128             tag: this.tag,
6129             cls: 'nav-item'
6130         };
6131         
6132         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6133         
6134         if (this.active) {
6135             cfg.cls +=  ' active' ;
6136         }
6137         if (this.disabled) {
6138             cfg.cls += ' disabled';
6139         }
6140         
6141         // BS4 only?
6142         if (this.button_weight.length) {
6143             cfg.tag = this.href ? 'a' : 'button';
6144             cfg.html = this.html || '';
6145             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6146             if (this.href) {
6147                 cfg.href = this.href;
6148             }
6149             if (this.fa) {
6150                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6151             }
6152             
6153             // menu .. should add dropdown-menu class - so no need for carat..
6154             
6155             if (this.badge !== '') {
6156                  
6157                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6158             }
6159             return cfg;
6160         }
6161         
6162         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6163             cfg.cn = [
6164                 {
6165                     tag: this.tagtype,
6166                     href : this.href || "#",
6167                     html: this.html || ''
6168                 }
6169             ];
6170             if (this.tagtype == 'a') {
6171                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6172         
6173             }
6174             if (this.icon) {
6175                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6176             }
6177             if (this.fa) {
6178                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6179             }
6180             if(this.glyphicon) {
6181                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6182             }
6183             
6184             if (this.menu) {
6185                 
6186                 cfg.cn[0].html += " <span class='caret'></span>";
6187              
6188             }
6189             
6190             if (this.badge !== '') {
6191                  
6192                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6193             }
6194         }
6195         
6196         
6197         
6198         return cfg;
6199     },
6200     onRender : function(ct, position)
6201     {
6202        // Roo.log("Call onRender: " + this.xtype);
6203         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6204             this.tag = 'div';
6205         }
6206         
6207         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6208         this.navLink = this.el.select('.nav-link',true).first();
6209         return ret;
6210     },
6211       
6212     
6213     initEvents: function() 
6214     {
6215         if (typeof (this.menu) != 'undefined') {
6216             this.menu.parentType = this.xtype;
6217             this.menu.triggerEl = this.el;
6218             this.menu = this.addxtype(Roo.apply({}, this.menu));
6219         }
6220         
6221         this.el.on('click', this.onClick, this);
6222         
6223         //if(this.tagtype == 'span'){
6224         //    this.el.select('span',true).on('click', this.onClick, this);
6225         //}
6226        
6227         // at this point parent should be available..
6228         this.parent().register(this);
6229     },
6230     
6231     onClick : function(e)
6232     {
6233         if (e.getTarget('.dropdown-menu-item')) {
6234             // did you click on a menu itemm.... - then don't trigger onclick..
6235             return;
6236         }
6237         
6238         if(
6239                 this.preventDefault || 
6240                 this.href == '#' 
6241         ){
6242             Roo.log("NavItem - prevent Default?");
6243             e.preventDefault();
6244         }
6245         
6246         if (this.disabled) {
6247             return;
6248         }
6249         
6250         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6251         if (tg && tg.transition) {
6252             Roo.log("waiting for the transitionend");
6253             return;
6254         }
6255         
6256         
6257         
6258         //Roo.log("fire event clicked");
6259         if(this.fireEvent('click', this, e) === false){
6260             return;
6261         };
6262         
6263         if(this.tagtype == 'span'){
6264             return;
6265         }
6266         
6267         //Roo.log(this.href);
6268         var ael = this.el.select('a',true).first();
6269         //Roo.log(ael);
6270         
6271         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6272             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6273             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6274                 return; // ignore... - it's a 'hash' to another page.
6275             }
6276             Roo.log("NavItem - prevent Default?");
6277             e.preventDefault();
6278             this.scrollToElement(e);
6279         }
6280         
6281         
6282         var p =  this.parent();
6283    
6284         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6285             if (typeof(p.setActiveItem) !== 'undefined') {
6286                 p.setActiveItem(this);
6287             }
6288         }
6289         
6290         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6291         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6292             // remove the collapsed menu expand...
6293             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6294         }
6295     },
6296     
6297     isActive: function () {
6298         return this.active
6299     },
6300     setActive : function(state, fire, is_was_active)
6301     {
6302         if (this.active && !state && this.navId) {
6303             this.was_active = true;
6304             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6305             if (nv) {
6306                 nv.clearWasActive(this);
6307             }
6308             
6309         }
6310         this.active = state;
6311         
6312         if (!state ) {
6313             this.el.removeClass('active');
6314             this.navLink ? this.navLink.removeClass('active') : false;
6315         } else if (!this.el.hasClass('active')) {
6316             
6317             this.el.addClass('active');
6318             if (Roo.bootstrap.version == 4 && this.navLink ) {
6319                 this.navLink.addClass('active');
6320             }
6321             
6322         }
6323         if (fire) {
6324             this.fireEvent('changed', this, state);
6325         }
6326         
6327         // show a panel if it's registered and related..
6328         
6329         if (!this.navId || !this.tabId || !state || is_was_active) {
6330             return;
6331         }
6332         
6333         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6334         if (!tg) {
6335             return;
6336         }
6337         var pan = tg.getPanelByName(this.tabId);
6338         if (!pan) {
6339             return;
6340         }
6341         // if we can not flip to new panel - go back to old nav highlight..
6342         if (false == tg.showPanel(pan)) {
6343             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6344             if (nv) {
6345                 var onav = nv.getWasActive();
6346                 if (onav) {
6347                     onav.setActive(true, false, true);
6348                 }
6349             }
6350             
6351         }
6352         
6353         
6354         
6355     },
6356      // this should not be here...
6357     setDisabled : function(state)
6358     {
6359         this.disabled = state;
6360         if (!state ) {
6361             this.el.removeClass('disabled');
6362         } else if (!this.el.hasClass('disabled')) {
6363             this.el.addClass('disabled');
6364         }
6365         
6366     },
6367     
6368     /**
6369      * Fetch the element to display the tooltip on.
6370      * @return {Roo.Element} defaults to this.el
6371      */
6372     tooltipEl : function()
6373     {
6374         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6375     },
6376     
6377     scrollToElement : function(e)
6378     {
6379         var c = document.body;
6380         
6381         /*
6382          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6383          */
6384         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6385             c = document.documentElement;
6386         }
6387         
6388         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6389         
6390         if(!target){
6391             return;
6392         }
6393
6394         var o = target.calcOffsetsTo(c);
6395         
6396         var options = {
6397             target : target,
6398             value : o[1]
6399         };
6400         
6401         this.fireEvent('scrollto', this, options, e);
6402         
6403         Roo.get(c).scrollTo('top', options.value, true);
6404         
6405         return;
6406     }
6407 });
6408  
6409
6410  /*
6411  * - LGPL
6412  *
6413  * sidebar item
6414  *
6415  *  li
6416  *    <span> icon </span>
6417  *    <span> text </span>
6418  *    <span>badge </span>
6419  */
6420
6421 /**
6422  * @class Roo.bootstrap.NavSidebarItem
6423  * @extends Roo.bootstrap.NavItem
6424  * Bootstrap Navbar.NavSidebarItem class
6425  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6426  * {Boolean} open is the menu open
6427  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6428  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6429  * {String} buttonSize (sm|md|lg)the extra classes for the button
6430  * {Boolean} showArrow show arrow next to the text (default true)
6431  * @constructor
6432  * Create a new Navbar Button
6433  * @param {Object} config The config object
6434  */
6435 Roo.bootstrap.NavSidebarItem = function(config){
6436     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6437     this.addEvents({
6438         // raw events
6439         /**
6440          * @event click
6441          * The raw click event for the entire grid.
6442          * @param {Roo.EventObject} e
6443          */
6444         "click" : true,
6445          /**
6446             * @event changed
6447             * Fires when the active item active state changes
6448             * @param {Roo.bootstrap.NavSidebarItem} this
6449             * @param {boolean} state the new state
6450              
6451          */
6452         'changed': true
6453     });
6454    
6455 };
6456
6457 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6458     
6459     badgeWeight : 'default',
6460     
6461     open: false,
6462     
6463     buttonView : false,
6464     
6465     buttonWeight : 'default',
6466     
6467     buttonSize : 'md',
6468     
6469     showArrow : true,
6470     
6471     getAutoCreate : function(){
6472         
6473         
6474         var a = {
6475                 tag: 'a',
6476                 href : this.href || '#',
6477                 cls: '',
6478                 html : '',
6479                 cn : []
6480         };
6481         
6482         if(this.buttonView){
6483             a = {
6484                 tag: 'button',
6485                 href : this.href || '#',
6486                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6487                 html : this.html,
6488                 cn : []
6489             };
6490         }
6491         
6492         var cfg = {
6493             tag: 'li',
6494             cls: '',
6495             cn: [ a ]
6496         };
6497         
6498         if (this.active) {
6499             cfg.cls += ' active';
6500         }
6501         
6502         if (this.disabled) {
6503             cfg.cls += ' disabled';
6504         }
6505         if (this.open) {
6506             cfg.cls += ' open x-open';
6507         }
6508         // left icon..
6509         if (this.glyphicon || this.icon) {
6510             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6511             a.cn.push({ tag : 'i', cls : c }) ;
6512         }
6513         
6514         if(!this.buttonView){
6515             var span = {
6516                 tag: 'span',
6517                 html : this.html || ''
6518             };
6519
6520             a.cn.push(span);
6521             
6522         }
6523         
6524         if (this.badge !== '') {
6525             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6526         }
6527         
6528         if (this.menu) {
6529             
6530             if(this.showArrow){
6531                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6532             }
6533             
6534             a.cls += ' dropdown-toggle treeview' ;
6535         }
6536         
6537         return cfg;
6538     },
6539     
6540     initEvents : function()
6541     { 
6542         if (typeof (this.menu) != 'undefined') {
6543             this.menu.parentType = this.xtype;
6544             this.menu.triggerEl = this.el;
6545             this.menu = this.addxtype(Roo.apply({}, this.menu));
6546         }
6547         
6548         this.el.on('click', this.onClick, this);
6549         
6550         if(this.badge !== ''){
6551             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6552         }
6553         
6554     },
6555     
6556     onClick : function(e)
6557     {
6558         if(this.disabled){
6559             e.preventDefault();
6560             return;
6561         }
6562         
6563         if(this.preventDefault){
6564             e.preventDefault();
6565         }
6566         
6567         this.fireEvent('click', this, e);
6568     },
6569     
6570     disable : function()
6571     {
6572         this.setDisabled(true);
6573     },
6574     
6575     enable : function()
6576     {
6577         this.setDisabled(false);
6578     },
6579     
6580     setDisabled : function(state)
6581     {
6582         if(this.disabled == state){
6583             return;
6584         }
6585         
6586         this.disabled = state;
6587         
6588         if (state) {
6589             this.el.addClass('disabled');
6590             return;
6591         }
6592         
6593         this.el.removeClass('disabled');
6594         
6595         return;
6596     },
6597     
6598     setActive : function(state)
6599     {
6600         if(this.active == state){
6601             return;
6602         }
6603         
6604         this.active = state;
6605         
6606         if (state) {
6607             this.el.addClass('active');
6608             return;
6609         }
6610         
6611         this.el.removeClass('active');
6612         
6613         return;
6614     },
6615     
6616     isActive: function () 
6617     {
6618         return this.active;
6619     },
6620     
6621     setBadge : function(str)
6622     {
6623         if(!this.badgeEl){
6624             return;
6625         }
6626         
6627         this.badgeEl.dom.innerHTML = str;
6628     }
6629     
6630    
6631      
6632  
6633 });
6634  
6635
6636  /*
6637  * - LGPL
6638  *
6639  *  Breadcrumb Nav
6640  * 
6641  */
6642 Roo.namespace('Roo.bootstrap.breadcrumb');
6643
6644
6645 /**
6646  * @class Roo.bootstrap.breadcrumb.Nav
6647  * @extends Roo.bootstrap.Component
6648  * Bootstrap Breadcrumb Nav Class
6649  *  
6650  * @children Roo.bootstrap.breadcrumb.Item
6651  * 
6652  * @constructor
6653  * Create a new breadcrumb.Nav
6654  * @param {Object} config The config object
6655  */
6656
6657
6658 Roo.bootstrap.breadcrumb.Nav = function(config){
6659     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6660     
6661     
6662 };
6663
6664 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6665     
6666     getAutoCreate : function()
6667     {
6668
6669         var cfg = {
6670             tag: 'nav',
6671             cn : [
6672                 {
6673                     tag : 'ol',
6674                     cls : 'breadcrumb'
6675                 }
6676             ]
6677             
6678         };
6679           
6680         return cfg;
6681     },
6682     
6683     initEvents: function()
6684     {
6685         this.olEl = this.el.select('ol',true).first();    
6686     },
6687     getChildContainer : function()
6688     {
6689         return this.olEl;  
6690     }
6691     
6692 });
6693
6694  /*
6695  * - LGPL
6696  *
6697  *  Breadcrumb Item
6698  * 
6699  */
6700
6701
6702 /**
6703  * @class Roo.bootstrap.breadcrumb.Nav
6704  * @extends Roo.bootstrap.Component
6705  * Bootstrap Breadcrumb Nav Class
6706  *  
6707  * @children Roo.bootstrap.breadcrumb.Component
6708  * @cfg {String} html the content of the link.
6709  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6710  * @cfg {Boolean} active is it active
6711
6712  * 
6713  * @constructor
6714  * Create a new breadcrumb.Nav
6715  * @param {Object} config The config object
6716  */
6717
6718 Roo.bootstrap.breadcrumb.Item = function(config){
6719     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6720     this.addEvents({
6721         // img events
6722         /**
6723          * @event click
6724          * The img click event for the img.
6725          * @param {Roo.EventObject} e
6726          */
6727         "click" : true
6728     });
6729     
6730 };
6731
6732 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6733     
6734     href: false,
6735     html : '',
6736     
6737     getAutoCreate : function()
6738     {
6739
6740         var cfg = {
6741             tag: 'li',
6742             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6743         };
6744         if (this.href !== false) {
6745             cfg.cn = [{
6746                 tag : 'a',
6747                 href : this.href,
6748                 html : this.html
6749             }];
6750         } else {
6751             cfg.html = this.html;
6752         }
6753         
6754         return cfg;
6755     },
6756     
6757     initEvents: function()
6758     {
6759         if (this.href) {
6760             this.el.select('a', true).first().on('click',this.onClick, this)
6761         }
6762         
6763     },
6764     onClick : function(e)
6765     {
6766         e.preventDefault();
6767         this.fireEvent('click',this,  e);
6768     }
6769     
6770 });
6771
6772  /*
6773  * - LGPL
6774  *
6775  * row
6776  * 
6777  */
6778
6779 /**
6780  * @class Roo.bootstrap.Row
6781  * @extends Roo.bootstrap.Component
6782  * Bootstrap Row class (contains columns...)
6783  * 
6784  * @constructor
6785  * Create a new Row
6786  * @param {Object} config The config object
6787  */
6788
6789 Roo.bootstrap.Row = function(config){
6790     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6791 };
6792
6793 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6794     
6795     getAutoCreate : function(){
6796        return {
6797             cls: 'row clearfix'
6798        };
6799     }
6800     
6801     
6802 });
6803
6804  
6805
6806  /*
6807  * - LGPL
6808  *
6809  * pagination
6810  * 
6811  */
6812
6813 /**
6814  * @class Roo.bootstrap.Pagination
6815  * @extends Roo.bootstrap.Component
6816  * Bootstrap Pagination class
6817  * @cfg {String} size xs | sm | md | lg
6818  * @cfg {Boolean} inverse false | true
6819  * 
6820  * @constructor
6821  * Create a new Pagination
6822  * @param {Object} config The config object
6823  */
6824
6825 Roo.bootstrap.Pagination = function(config){
6826     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6827 };
6828
6829 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6830     
6831     cls: false,
6832     size: false,
6833     inverse: false,
6834     
6835     getAutoCreate : function(){
6836         var cfg = {
6837             tag: 'ul',
6838                 cls: 'pagination'
6839         };
6840         if (this.inverse) {
6841             cfg.cls += ' inverse';
6842         }
6843         if (this.html) {
6844             cfg.html=this.html;
6845         }
6846         if (this.cls) {
6847             cfg.cls += " " + this.cls;
6848         }
6849         return cfg;
6850     }
6851    
6852 });
6853
6854  
6855
6856  /*
6857  * - LGPL
6858  *
6859  * Pagination item
6860  * 
6861  */
6862
6863
6864 /**
6865  * @class Roo.bootstrap.PaginationItem
6866  * @extends Roo.bootstrap.Component
6867  * Bootstrap PaginationItem class
6868  * @cfg {String} html text
6869  * @cfg {String} href the link
6870  * @cfg {Boolean} preventDefault (true | false) default true
6871  * @cfg {Boolean} active (true | false) default false
6872  * @cfg {Boolean} disabled default false
6873  * 
6874  * 
6875  * @constructor
6876  * Create a new PaginationItem
6877  * @param {Object} config The config object
6878  */
6879
6880
6881 Roo.bootstrap.PaginationItem = function(config){
6882     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6883     this.addEvents({
6884         // raw events
6885         /**
6886          * @event click
6887          * The raw click event for the entire grid.
6888          * @param {Roo.EventObject} e
6889          */
6890         "click" : true
6891     });
6892 };
6893
6894 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6895     
6896     href : false,
6897     html : false,
6898     preventDefault: true,
6899     active : false,
6900     cls : false,
6901     disabled: false,
6902     
6903     getAutoCreate : function(){
6904         var cfg= {
6905             tag: 'li',
6906             cn: [
6907                 {
6908                     tag : 'a',
6909                     href : this.href ? this.href : '#',
6910                     html : this.html ? this.html : ''
6911                 }
6912             ]
6913         };
6914         
6915         if(this.cls){
6916             cfg.cls = this.cls;
6917         }
6918         
6919         if(this.disabled){
6920             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6921         }
6922         
6923         if(this.active){
6924             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6925         }
6926         
6927         return cfg;
6928     },
6929     
6930     initEvents: function() {
6931         
6932         this.el.on('click', this.onClick, this);
6933         
6934     },
6935     onClick : function(e)
6936     {
6937         Roo.log('PaginationItem on click ');
6938         if(this.preventDefault){
6939             e.preventDefault();
6940         }
6941         
6942         if(this.disabled){
6943             return;
6944         }
6945         
6946         this.fireEvent('click', this, e);
6947     }
6948    
6949 });
6950
6951  
6952
6953  /*
6954  * - LGPL
6955  *
6956  * slider
6957  * 
6958  */
6959
6960
6961 /**
6962  * @class Roo.bootstrap.Slider
6963  * @extends Roo.bootstrap.Component
6964  * Bootstrap Slider class
6965  *    
6966  * @constructor
6967  * Create a new Slider
6968  * @param {Object} config The config object
6969  */
6970
6971 Roo.bootstrap.Slider = function(config){
6972     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6973 };
6974
6975 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6976     
6977     getAutoCreate : function(){
6978         
6979         var cfg = {
6980             tag: 'div',
6981             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6982             cn: [
6983                 {
6984                     tag: 'a',
6985                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6986                 }
6987             ]
6988         };
6989         
6990         return cfg;
6991     }
6992    
6993 });
6994
6995  /*
6996  * Based on:
6997  * Ext JS Library 1.1.1
6998  * Copyright(c) 2006-2007, Ext JS, LLC.
6999  *
7000  * Originally Released Under LGPL - original licence link has changed is not relivant.
7001  *
7002  * Fork - LGPL
7003  * <script type="text/javascript">
7004  */
7005  
7006
7007 /**
7008  * @class Roo.grid.ColumnModel
7009  * @extends Roo.util.Observable
7010  * This is the default implementation of a ColumnModel used by the Grid. It defines
7011  * the columns in the grid.
7012  * <br>Usage:<br>
7013  <pre><code>
7014  var colModel = new Roo.grid.ColumnModel([
7015         {header: "Ticker", width: 60, sortable: true, locked: true},
7016         {header: "Company Name", width: 150, sortable: true},
7017         {header: "Market Cap.", width: 100, sortable: true},
7018         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7019         {header: "Employees", width: 100, sortable: true, resizable: false}
7020  ]);
7021  </code></pre>
7022  * <p>
7023  
7024  * The config options listed for this class are options which may appear in each
7025  * individual column definition.
7026  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7027  * @constructor
7028  * @param {Object} config An Array of column config objects. See this class's
7029  * config objects for details.
7030 */
7031 Roo.grid.ColumnModel = function(config){
7032         /**
7033      * The config passed into the constructor
7034      */
7035     this.config = config;
7036     this.lookup = {};
7037
7038     // if no id, create one
7039     // if the column does not have a dataIndex mapping,
7040     // map it to the order it is in the config
7041     for(var i = 0, len = config.length; i < len; i++){
7042         var c = config[i];
7043         if(typeof c.dataIndex == "undefined"){
7044             c.dataIndex = i;
7045         }
7046         if(typeof c.renderer == "string"){
7047             c.renderer = Roo.util.Format[c.renderer];
7048         }
7049         if(typeof c.id == "undefined"){
7050             c.id = Roo.id();
7051         }
7052         if(c.editor && c.editor.xtype){
7053             c.editor  = Roo.factory(c.editor, Roo.grid);
7054         }
7055         if(c.editor && c.editor.isFormField){
7056             c.editor = new Roo.grid.GridEditor(c.editor);
7057         }
7058         this.lookup[c.id] = c;
7059     }
7060
7061     /**
7062      * The width of columns which have no width specified (defaults to 100)
7063      * @type Number
7064      */
7065     this.defaultWidth = 100;
7066
7067     /**
7068      * Default sortable of columns which have no sortable specified (defaults to false)
7069      * @type Boolean
7070      */
7071     this.defaultSortable = false;
7072
7073     this.addEvents({
7074         /**
7075              * @event widthchange
7076              * Fires when the width of a column changes.
7077              * @param {ColumnModel} this
7078              * @param {Number} columnIndex The column index
7079              * @param {Number} newWidth The new width
7080              */
7081             "widthchange": true,
7082         /**
7083              * @event headerchange
7084              * Fires when the text of a header changes.
7085              * @param {ColumnModel} this
7086              * @param {Number} columnIndex The column index
7087              * @param {Number} newText The new header text
7088              */
7089             "headerchange": true,
7090         /**
7091              * @event hiddenchange
7092              * Fires when a column is hidden or "unhidden".
7093              * @param {ColumnModel} this
7094              * @param {Number} columnIndex The column index
7095              * @param {Boolean} hidden true if hidden, false otherwise
7096              */
7097             "hiddenchange": true,
7098             /**
7099          * @event columnmoved
7100          * Fires when a column is moved.
7101          * @param {ColumnModel} this
7102          * @param {Number} oldIndex
7103          * @param {Number} newIndex
7104          */
7105         "columnmoved" : true,
7106         /**
7107          * @event columlockchange
7108          * Fires when a column's locked state is changed
7109          * @param {ColumnModel} this
7110          * @param {Number} colIndex
7111          * @param {Boolean} locked true if locked
7112          */
7113         "columnlockchange" : true
7114     });
7115     Roo.grid.ColumnModel.superclass.constructor.call(this);
7116 };
7117 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7118     /**
7119      * @cfg {String} header The header text to display in the Grid view.
7120      */
7121     /**
7122      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7123      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7124      * specified, the column's index is used as an index into the Record's data Array.
7125      */
7126     /**
7127      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7128      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7129      */
7130     /**
7131      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7132      * Defaults to the value of the {@link #defaultSortable} property.
7133      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7134      */
7135     /**
7136      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7137      */
7138     /**
7139      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7140      */
7141     /**
7142      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7143      */
7144     /**
7145      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7146      */
7147     /**
7148      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7149      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7150      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7151      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7152      */
7153        /**
7154      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7155      */
7156     /**
7157      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7158      */
7159     /**
7160      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7161      */
7162     /**
7163      * @cfg {String} cursor (Optional)
7164      */
7165     /**
7166      * @cfg {String} tooltip (Optional)
7167      */
7168     /**
7169      * @cfg {Number} xs (Optional)
7170      */
7171     /**
7172      * @cfg {Number} sm (Optional)
7173      */
7174     /**
7175      * @cfg {Number} md (Optional)
7176      */
7177     /**
7178      * @cfg {Number} lg (Optional)
7179      */
7180     /**
7181      * Returns the id of the column at the specified index.
7182      * @param {Number} index The column index
7183      * @return {String} the id
7184      */
7185     getColumnId : function(index){
7186         return this.config[index].id;
7187     },
7188
7189     /**
7190      * Returns the column for a specified id.
7191      * @param {String} id The column id
7192      * @return {Object} the column
7193      */
7194     getColumnById : function(id){
7195         return this.lookup[id];
7196     },
7197
7198     
7199     /**
7200      * Returns the column for a specified dataIndex.
7201      * @param {String} dataIndex The column dataIndex
7202      * @return {Object|Boolean} the column or false if not found
7203      */
7204     getColumnByDataIndex: function(dataIndex){
7205         var index = this.findColumnIndex(dataIndex);
7206         return index > -1 ? this.config[index] : false;
7207     },
7208     
7209     /**
7210      * Returns the index for a specified column id.
7211      * @param {String} id The column id
7212      * @return {Number} the index, or -1 if not found
7213      */
7214     getIndexById : function(id){
7215         for(var i = 0, len = this.config.length; i < len; i++){
7216             if(this.config[i].id == id){
7217                 return i;
7218             }
7219         }
7220         return -1;
7221     },
7222     
7223     /**
7224      * Returns the index for a specified column dataIndex.
7225      * @param {String} dataIndex The column dataIndex
7226      * @return {Number} the index, or -1 if not found
7227      */
7228     
7229     findColumnIndex : function(dataIndex){
7230         for(var i = 0, len = this.config.length; i < len; i++){
7231             if(this.config[i].dataIndex == dataIndex){
7232                 return i;
7233             }
7234         }
7235         return -1;
7236     },
7237     
7238     
7239     moveColumn : function(oldIndex, newIndex){
7240         var c = this.config[oldIndex];
7241         this.config.splice(oldIndex, 1);
7242         this.config.splice(newIndex, 0, c);
7243         this.dataMap = null;
7244         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7245     },
7246
7247     isLocked : function(colIndex){
7248         return this.config[colIndex].locked === true;
7249     },
7250
7251     setLocked : function(colIndex, value, suppressEvent){
7252         if(this.isLocked(colIndex) == value){
7253             return;
7254         }
7255         this.config[colIndex].locked = value;
7256         if(!suppressEvent){
7257             this.fireEvent("columnlockchange", this, colIndex, value);
7258         }
7259     },
7260
7261     getTotalLockedWidth : function(){
7262         var totalWidth = 0;
7263         for(var i = 0; i < this.config.length; i++){
7264             if(this.isLocked(i) && !this.isHidden(i)){
7265                 this.totalWidth += this.getColumnWidth(i);
7266             }
7267         }
7268         return totalWidth;
7269     },
7270
7271     getLockedCount : function(){
7272         for(var i = 0, len = this.config.length; i < len; i++){
7273             if(!this.isLocked(i)){
7274                 return i;
7275             }
7276         }
7277         
7278         return this.config.length;
7279     },
7280
7281     /**
7282      * Returns the number of columns.
7283      * @return {Number}
7284      */
7285     getColumnCount : function(visibleOnly){
7286         if(visibleOnly === true){
7287             var c = 0;
7288             for(var i = 0, len = this.config.length; i < len; i++){
7289                 if(!this.isHidden(i)){
7290                     c++;
7291                 }
7292             }
7293             return c;
7294         }
7295         return this.config.length;
7296     },
7297
7298     /**
7299      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7300      * @param {Function} fn
7301      * @param {Object} scope (optional)
7302      * @return {Array} result
7303      */
7304     getColumnsBy : function(fn, scope){
7305         var r = [];
7306         for(var i = 0, len = this.config.length; i < len; i++){
7307             var c = this.config[i];
7308             if(fn.call(scope||this, c, i) === true){
7309                 r[r.length] = c;
7310             }
7311         }
7312         return r;
7313     },
7314
7315     /**
7316      * Returns true if the specified column is sortable.
7317      * @param {Number} col The column index
7318      * @return {Boolean}
7319      */
7320     isSortable : function(col){
7321         if(typeof this.config[col].sortable == "undefined"){
7322             return this.defaultSortable;
7323         }
7324         return this.config[col].sortable;
7325     },
7326
7327     /**
7328      * Returns the rendering (formatting) function defined for the column.
7329      * @param {Number} col The column index.
7330      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7331      */
7332     getRenderer : function(col){
7333         if(!this.config[col].renderer){
7334             return Roo.grid.ColumnModel.defaultRenderer;
7335         }
7336         return this.config[col].renderer;
7337     },
7338
7339     /**
7340      * Sets the rendering (formatting) function for a column.
7341      * @param {Number} col The column index
7342      * @param {Function} fn The function to use to process the cell's raw data
7343      * to return HTML markup for the grid view. The render function is called with
7344      * the following parameters:<ul>
7345      * <li>Data value.</li>
7346      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7347      * <li>css A CSS style string to apply to the table cell.</li>
7348      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7349      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7350      * <li>Row index</li>
7351      * <li>Column index</li>
7352      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7353      */
7354     setRenderer : function(col, fn){
7355         this.config[col].renderer = fn;
7356     },
7357
7358     /**
7359      * Returns the width for the specified column.
7360      * @param {Number} col The column index
7361      * @return {Number}
7362      */
7363     getColumnWidth : function(col){
7364         return this.config[col].width * 1 || this.defaultWidth;
7365     },
7366
7367     /**
7368      * Sets the width for a column.
7369      * @param {Number} col The column index
7370      * @param {Number} width The new width
7371      */
7372     setColumnWidth : function(col, width, suppressEvent){
7373         this.config[col].width = width;
7374         this.totalWidth = null;
7375         if(!suppressEvent){
7376              this.fireEvent("widthchange", this, col, width);
7377         }
7378     },
7379
7380     /**
7381      * Returns the total width of all columns.
7382      * @param {Boolean} includeHidden True to include hidden column widths
7383      * @return {Number}
7384      */
7385     getTotalWidth : function(includeHidden){
7386         if(!this.totalWidth){
7387             this.totalWidth = 0;
7388             for(var i = 0, len = this.config.length; i < len; i++){
7389                 if(includeHidden || !this.isHidden(i)){
7390                     this.totalWidth += this.getColumnWidth(i);
7391                 }
7392             }
7393         }
7394         return this.totalWidth;
7395     },
7396
7397     /**
7398      * Returns the header for the specified column.
7399      * @param {Number} col The column index
7400      * @return {String}
7401      */
7402     getColumnHeader : function(col){
7403         return this.config[col].header;
7404     },
7405
7406     /**
7407      * Sets the header for a column.
7408      * @param {Number} col The column index
7409      * @param {String} header The new header
7410      */
7411     setColumnHeader : function(col, header){
7412         this.config[col].header = header;
7413         this.fireEvent("headerchange", this, col, header);
7414     },
7415
7416     /**
7417      * Returns the tooltip for the specified column.
7418      * @param {Number} col The column index
7419      * @return {String}
7420      */
7421     getColumnTooltip : function(col){
7422             return this.config[col].tooltip;
7423     },
7424     /**
7425      * Sets the tooltip for a column.
7426      * @param {Number} col The column index
7427      * @param {String} tooltip The new tooltip
7428      */
7429     setColumnTooltip : function(col, tooltip){
7430             this.config[col].tooltip = tooltip;
7431     },
7432
7433     /**
7434      * Returns the dataIndex for the specified column.
7435      * @param {Number} col The column index
7436      * @return {Number}
7437      */
7438     getDataIndex : function(col){
7439         return this.config[col].dataIndex;
7440     },
7441
7442     /**
7443      * Sets the dataIndex for a column.
7444      * @param {Number} col The column index
7445      * @param {Number} dataIndex The new dataIndex
7446      */
7447     setDataIndex : function(col, dataIndex){
7448         this.config[col].dataIndex = dataIndex;
7449     },
7450
7451     
7452     
7453     /**
7454      * Returns true if the cell is editable.
7455      * @param {Number} colIndex The column index
7456      * @param {Number} rowIndex The row index - this is nto actually used..?
7457      * @return {Boolean}
7458      */
7459     isCellEditable : function(colIndex, rowIndex){
7460         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7461     },
7462
7463     /**
7464      * Returns the editor defined for the cell/column.
7465      * return false or null to disable editing.
7466      * @param {Number} colIndex The column index
7467      * @param {Number} rowIndex The row index
7468      * @return {Object}
7469      */
7470     getCellEditor : function(colIndex, rowIndex){
7471         return this.config[colIndex].editor;
7472     },
7473
7474     /**
7475      * Sets if a column is editable.
7476      * @param {Number} col The column index
7477      * @param {Boolean} editable True if the column is editable
7478      */
7479     setEditable : function(col, editable){
7480         this.config[col].editable = editable;
7481     },
7482
7483
7484     /**
7485      * Returns true if the column is hidden.
7486      * @param {Number} colIndex The column index
7487      * @return {Boolean}
7488      */
7489     isHidden : function(colIndex){
7490         return this.config[colIndex].hidden;
7491     },
7492
7493
7494     /**
7495      * Returns true if the column width cannot be changed
7496      */
7497     isFixed : function(colIndex){
7498         return this.config[colIndex].fixed;
7499     },
7500
7501     /**
7502      * Returns true if the column can be resized
7503      * @return {Boolean}
7504      */
7505     isResizable : function(colIndex){
7506         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7507     },
7508     /**
7509      * Sets if a column is hidden.
7510      * @param {Number} colIndex The column index
7511      * @param {Boolean} hidden True if the column is hidden
7512      */
7513     setHidden : function(colIndex, hidden){
7514         this.config[colIndex].hidden = hidden;
7515         this.totalWidth = null;
7516         this.fireEvent("hiddenchange", this, colIndex, hidden);
7517     },
7518
7519     /**
7520      * Sets the editor for a column.
7521      * @param {Number} col The column index
7522      * @param {Object} editor The editor object
7523      */
7524     setEditor : function(col, editor){
7525         this.config[col].editor = editor;
7526     }
7527 });
7528
7529 Roo.grid.ColumnModel.defaultRenderer = function(value)
7530 {
7531     if(typeof value == "object") {
7532         return value;
7533     }
7534         if(typeof value == "string" && value.length < 1){
7535             return "&#160;";
7536         }
7537     
7538         return String.format("{0}", value);
7539 };
7540
7541 // Alias for backwards compatibility
7542 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7543 /*
7544  * Based on:
7545  * Ext JS Library 1.1.1
7546  * Copyright(c) 2006-2007, Ext JS, LLC.
7547  *
7548  * Originally Released Under LGPL - original licence link has changed is not relivant.
7549  *
7550  * Fork - LGPL
7551  * <script type="text/javascript">
7552  */
7553  
7554 /**
7555  * @class Roo.LoadMask
7556  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7557  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7558  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7559  * element's UpdateManager load indicator and will be destroyed after the initial load.
7560  * @constructor
7561  * Create a new LoadMask
7562  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7563  * @param {Object} config The config object
7564  */
7565 Roo.LoadMask = function(el, config){
7566     this.el = Roo.get(el);
7567     Roo.apply(this, config);
7568     if(this.store){
7569         this.store.on('beforeload', this.onBeforeLoad, this);
7570         this.store.on('load', this.onLoad, this);
7571         this.store.on('loadexception', this.onLoadException, this);
7572         this.removeMask = false;
7573     }else{
7574         var um = this.el.getUpdateManager();
7575         um.showLoadIndicator = false; // disable the default indicator
7576         um.on('beforeupdate', this.onBeforeLoad, this);
7577         um.on('update', this.onLoad, this);
7578         um.on('failure', this.onLoad, this);
7579         this.removeMask = true;
7580     }
7581 };
7582
7583 Roo.LoadMask.prototype = {
7584     /**
7585      * @cfg {Boolean} removeMask
7586      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7587      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7588      */
7589     /**
7590      * @cfg {String} msg
7591      * The text to display in a centered loading message box (defaults to 'Loading...')
7592      */
7593     msg : 'Loading...',
7594     /**
7595      * @cfg {String} msgCls
7596      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7597      */
7598     msgCls : 'x-mask-loading',
7599
7600     /**
7601      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7602      * @type Boolean
7603      */
7604     disabled: false,
7605
7606     /**
7607      * Disables the mask to prevent it from being displayed
7608      */
7609     disable : function(){
7610        this.disabled = true;
7611     },
7612
7613     /**
7614      * Enables the mask so that it can be displayed
7615      */
7616     enable : function(){
7617         this.disabled = false;
7618     },
7619     
7620     onLoadException : function()
7621     {
7622         Roo.log(arguments);
7623         
7624         if (typeof(arguments[3]) != 'undefined') {
7625             Roo.MessageBox.alert("Error loading",arguments[3]);
7626         } 
7627         /*
7628         try {
7629             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7630                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7631             }   
7632         } catch(e) {
7633             
7634         }
7635         */
7636     
7637         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7638     },
7639     // private
7640     onLoad : function()
7641     {
7642         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7643     },
7644
7645     // private
7646     onBeforeLoad : function(){
7647         if(!this.disabled){
7648             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7649         }
7650     },
7651
7652     // private
7653     destroy : function(){
7654         if(this.store){
7655             this.store.un('beforeload', this.onBeforeLoad, this);
7656             this.store.un('load', this.onLoad, this);
7657             this.store.un('loadexception', this.onLoadException, this);
7658         }else{
7659             var um = this.el.getUpdateManager();
7660             um.un('beforeupdate', this.onBeforeLoad, this);
7661             um.un('update', this.onLoad, this);
7662             um.un('failure', this.onLoad, this);
7663         }
7664     }
7665 };/*
7666  * - LGPL
7667  *
7668  * table
7669  * 
7670  */
7671
7672 /**
7673  * @class Roo.bootstrap.Table
7674  * @extends Roo.bootstrap.Component
7675  * Bootstrap Table class
7676  * @cfg {String} cls table class
7677  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7678  * @cfg {String} bgcolor Specifies the background color for a table
7679  * @cfg {Number} border Specifies whether the table cells should have borders or not
7680  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7681  * @cfg {Number} cellspacing Specifies the space between cells
7682  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7683  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7684  * @cfg {String} sortable Specifies that the table should be sortable
7685  * @cfg {String} summary Specifies a summary of the content of a table
7686  * @cfg {Number} width Specifies the width of a table
7687  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7688  * 
7689  * @cfg {boolean} striped Should the rows be alternative striped
7690  * @cfg {boolean} bordered Add borders to the table
7691  * @cfg {boolean} hover Add hover highlighting
7692  * @cfg {boolean} condensed Format condensed
7693  * @cfg {boolean} responsive Format condensed
7694  * @cfg {Boolean} loadMask (true|false) default false
7695  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7696  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7697  * @cfg {Boolean} rowSelection (true|false) default false
7698  * @cfg {Boolean} cellSelection (true|false) default false
7699  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7700  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7701  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7702  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7703  
7704  * 
7705  * @constructor
7706  * Create a new Table
7707  * @param {Object} config The config object
7708  */
7709
7710 Roo.bootstrap.Table = function(config){
7711     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7712     
7713   
7714     
7715     // BC...
7716     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7717     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7718     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7719     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7720     
7721     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7722     if (this.sm) {
7723         this.sm.grid = this;
7724         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7725         this.sm = this.selModel;
7726         this.sm.xmodule = this.xmodule || false;
7727     }
7728     
7729     if (this.cm && typeof(this.cm.config) == 'undefined') {
7730         this.colModel = new Roo.grid.ColumnModel(this.cm);
7731         this.cm = this.colModel;
7732         this.cm.xmodule = this.xmodule || false;
7733     }
7734     if (this.store) {
7735         this.store= Roo.factory(this.store, Roo.data);
7736         this.ds = this.store;
7737         this.ds.xmodule = this.xmodule || false;
7738          
7739     }
7740     if (this.footer && this.store) {
7741         this.footer.dataSource = this.ds;
7742         this.footer = Roo.factory(this.footer);
7743     }
7744     
7745     /** @private */
7746     this.addEvents({
7747         /**
7748          * @event cellclick
7749          * Fires when a cell is clicked
7750          * @param {Roo.bootstrap.Table} this
7751          * @param {Roo.Element} el
7752          * @param {Number} rowIndex
7753          * @param {Number} columnIndex
7754          * @param {Roo.EventObject} e
7755          */
7756         "cellclick" : true,
7757         /**
7758          * @event celldblclick
7759          * Fires when a cell is double clicked
7760          * @param {Roo.bootstrap.Table} this
7761          * @param {Roo.Element} el
7762          * @param {Number} rowIndex
7763          * @param {Number} columnIndex
7764          * @param {Roo.EventObject} e
7765          */
7766         "celldblclick" : true,
7767         /**
7768          * @event rowclick
7769          * Fires when a row is clicked
7770          * @param {Roo.bootstrap.Table} this
7771          * @param {Roo.Element} el
7772          * @param {Number} rowIndex
7773          * @param {Roo.EventObject} e
7774          */
7775         "rowclick" : true,
7776         /**
7777          * @event rowdblclick
7778          * Fires when a row is double clicked
7779          * @param {Roo.bootstrap.Table} this
7780          * @param {Roo.Element} el
7781          * @param {Number} rowIndex
7782          * @param {Roo.EventObject} e
7783          */
7784         "rowdblclick" : true,
7785         /**
7786          * @event mouseover
7787          * Fires when a mouseover occur
7788          * @param {Roo.bootstrap.Table} this
7789          * @param {Roo.Element} el
7790          * @param {Number} rowIndex
7791          * @param {Number} columnIndex
7792          * @param {Roo.EventObject} e
7793          */
7794         "mouseover" : true,
7795         /**
7796          * @event mouseout
7797          * Fires when a mouseout occur
7798          * @param {Roo.bootstrap.Table} this
7799          * @param {Roo.Element} el
7800          * @param {Number} rowIndex
7801          * @param {Number} columnIndex
7802          * @param {Roo.EventObject} e
7803          */
7804         "mouseout" : true,
7805         /**
7806          * @event rowclass
7807          * Fires when a row is rendered, so you can change add a style to it.
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7810          */
7811         'rowclass' : true,
7812           /**
7813          * @event rowsrendered
7814          * Fires when all the  rows have been rendered
7815          * @param {Roo.bootstrap.Table} this
7816          */
7817         'rowsrendered' : true,
7818         /**
7819          * @event contextmenu
7820          * The raw contextmenu event for the entire grid.
7821          * @param {Roo.EventObject} e
7822          */
7823         "contextmenu" : true,
7824         /**
7825          * @event rowcontextmenu
7826          * Fires when a row is right clicked
7827          * @param {Roo.bootstrap.Table} this
7828          * @param {Number} rowIndex
7829          * @param {Roo.EventObject} e
7830          */
7831         "rowcontextmenu" : true,
7832         /**
7833          * @event cellcontextmenu
7834          * Fires when a cell is right clicked
7835          * @param {Roo.bootstrap.Table} this
7836          * @param {Number} rowIndex
7837          * @param {Number} cellIndex
7838          * @param {Roo.EventObject} e
7839          */
7840          "cellcontextmenu" : true,
7841          /**
7842          * @event headercontextmenu
7843          * Fires when a header is right clicked
7844          * @param {Roo.bootstrap.Table} this
7845          * @param {Number} columnIndex
7846          * @param {Roo.EventObject} e
7847          */
7848         "headercontextmenu" : true
7849     });
7850 };
7851
7852 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7853     
7854     cls: false,
7855     align: false,
7856     bgcolor: false,
7857     border: false,
7858     cellpadding: false,
7859     cellspacing: false,
7860     frame: false,
7861     rules: false,
7862     sortable: false,
7863     summary: false,
7864     width: false,
7865     striped : false,
7866     scrollBody : false,
7867     bordered: false,
7868     hover:  false,
7869     condensed : false,
7870     responsive : false,
7871     sm : false,
7872     cm : false,
7873     store : false,
7874     loadMask : false,
7875     footerShow : true,
7876     headerShow : true,
7877   
7878     rowSelection : false,
7879     cellSelection : false,
7880     layout : false,
7881     
7882     // Roo.Element - the tbody
7883     mainBody: false,
7884     // Roo.Element - thead element
7885     mainHead: false,
7886     
7887     container: false, // used by gridpanel...
7888     
7889     lazyLoad : false,
7890     
7891     CSS : Roo.util.CSS,
7892     
7893     auto_hide_footer : false,
7894     
7895     getAutoCreate : function()
7896     {
7897         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7898         
7899         cfg = {
7900             tag: 'table',
7901             cls : 'table',
7902             cn : []
7903         };
7904         if (this.scrollBody) {
7905             cfg.cls += ' table-body-fixed';
7906         }    
7907         if (this.striped) {
7908             cfg.cls += ' table-striped';
7909         }
7910         
7911         if (this.hover) {
7912             cfg.cls += ' table-hover';
7913         }
7914         if (this.bordered) {
7915             cfg.cls += ' table-bordered';
7916         }
7917         if (this.condensed) {
7918             cfg.cls += ' table-condensed';
7919         }
7920         if (this.responsive) {
7921             cfg.cls += ' table-responsive';
7922         }
7923         
7924         if (this.cls) {
7925             cfg.cls+=  ' ' +this.cls;
7926         }
7927         
7928         // this lot should be simplifed...
7929         var _t = this;
7930         var cp = [
7931             'align',
7932             'bgcolor',
7933             'border',
7934             'cellpadding',
7935             'cellspacing',
7936             'frame',
7937             'rules',
7938             'sortable',
7939             'summary',
7940             'width'
7941         ].forEach(function(k) {
7942             if (_t[k]) {
7943                 cfg[k] = _t[k];
7944             }
7945         });
7946         
7947         
7948         if (this.layout) {
7949             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7950         }
7951         
7952         if(this.store || this.cm){
7953             if(this.headerShow){
7954                 cfg.cn.push(this.renderHeader());
7955             }
7956             
7957             cfg.cn.push(this.renderBody());
7958             
7959             if(this.footerShow){
7960                 cfg.cn.push(this.renderFooter());
7961             }
7962             // where does this come from?
7963             //cfg.cls+=  ' TableGrid';
7964         }
7965         
7966         return { cn : [ cfg ] };
7967     },
7968     
7969     initEvents : function()
7970     {   
7971         if(!this.store || !this.cm){
7972             return;
7973         }
7974         if (this.selModel) {
7975             this.selModel.initEvents();
7976         }
7977         
7978         
7979         //Roo.log('initEvents with ds!!!!');
7980         
7981         this.mainBody = this.el.select('tbody', true).first();
7982         this.mainHead = this.el.select('thead', true).first();
7983         this.mainFoot = this.el.select('tfoot', true).first();
7984         
7985         
7986         
7987         var _this = this;
7988         
7989         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7990             e.on('click', _this.sort, _this);
7991         });
7992         
7993         this.mainBody.on("click", this.onClick, this);
7994         this.mainBody.on("dblclick", this.onDblClick, this);
7995         
7996         // why is this done????? = it breaks dialogs??
7997         //this.parent().el.setStyle('position', 'relative');
7998         
7999         
8000         if (this.footer) {
8001             this.footer.parentId = this.id;
8002             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8003             
8004             if(this.lazyLoad){
8005                 this.el.select('tfoot tr td').first().addClass('hide');
8006             }
8007         } 
8008         
8009         if(this.loadMask) {
8010             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8011         }
8012         
8013         this.store.on('load', this.onLoad, this);
8014         this.store.on('beforeload', this.onBeforeLoad, this);
8015         this.store.on('update', this.onUpdate, this);
8016         this.store.on('add', this.onAdd, this);
8017         this.store.on("clear", this.clear, this);
8018         
8019         this.el.on("contextmenu", this.onContextMenu, this);
8020         
8021         this.mainBody.on('scroll', this.onBodyScroll, this);
8022         
8023         this.cm.on("headerchange", this.onHeaderChange, this);
8024         
8025         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8026         
8027     },
8028     
8029     onContextMenu : function(e, t)
8030     {
8031         this.processEvent("contextmenu", e);
8032     },
8033     
8034     processEvent : function(name, e)
8035     {
8036         if (name != 'touchstart' ) {
8037             this.fireEvent(name, e);    
8038         }
8039         
8040         var t = e.getTarget();
8041         
8042         var cell = Roo.get(t);
8043         
8044         if(!cell){
8045             return;
8046         }
8047         
8048         if(cell.findParent('tfoot', false, true)){
8049             return;
8050         }
8051         
8052         if(cell.findParent('thead', false, true)){
8053             
8054             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8055                 cell = Roo.get(t).findParent('th', false, true);
8056                 if (!cell) {
8057                     Roo.log("failed to find th in thead?");
8058                     Roo.log(e.getTarget());
8059                     return;
8060                 }
8061             }
8062             
8063             var cellIndex = cell.dom.cellIndex;
8064             
8065             var ename = name == 'touchstart' ? 'click' : name;
8066             this.fireEvent("header" + ename, this, cellIndex, e);
8067             
8068             return;
8069         }
8070         
8071         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8072             cell = Roo.get(t).findParent('td', false, true);
8073             if (!cell) {
8074                 Roo.log("failed to find th in tbody?");
8075                 Roo.log(e.getTarget());
8076                 return;
8077             }
8078         }
8079         
8080         var row = cell.findParent('tr', false, true);
8081         var cellIndex = cell.dom.cellIndex;
8082         var rowIndex = row.dom.rowIndex - 1;
8083         
8084         if(row !== false){
8085             
8086             this.fireEvent("row" + name, this, rowIndex, e);
8087             
8088             if(cell !== false){
8089             
8090                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8091             }
8092         }
8093         
8094     },
8095     
8096     onMouseover : function(e, el)
8097     {
8098         var cell = Roo.get(el);
8099         
8100         if(!cell){
8101             return;
8102         }
8103         
8104         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8105             cell = cell.findParent('td', false, true);
8106         }
8107         
8108         var row = cell.findParent('tr', false, true);
8109         var cellIndex = cell.dom.cellIndex;
8110         var rowIndex = row.dom.rowIndex - 1; // start from 0
8111         
8112         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8113         
8114     },
8115     
8116     onMouseout : function(e, el)
8117     {
8118         var cell = Roo.get(el);
8119         
8120         if(!cell){
8121             return;
8122         }
8123         
8124         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8125             cell = cell.findParent('td', false, true);
8126         }
8127         
8128         var row = cell.findParent('tr', false, true);
8129         var cellIndex = cell.dom.cellIndex;
8130         var rowIndex = row.dom.rowIndex - 1; // start from 0
8131         
8132         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8133         
8134     },
8135     
8136     onClick : function(e, el)
8137     {
8138         var cell = Roo.get(el);
8139         
8140         if(!cell || (!this.cellSelection && !this.rowSelection)){
8141             return;
8142         }
8143         
8144         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8145             cell = cell.findParent('td', false, true);
8146         }
8147         
8148         if(!cell || typeof(cell) == 'undefined'){
8149             return;
8150         }
8151         
8152         var row = cell.findParent('tr', false, true);
8153         
8154         if(!row || typeof(row) == 'undefined'){
8155             return;
8156         }
8157         
8158         var cellIndex = cell.dom.cellIndex;
8159         var rowIndex = this.getRowIndex(row);
8160         
8161         // why??? - should these not be based on SelectionModel?
8162         if(this.cellSelection){
8163             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8164         }
8165         
8166         if(this.rowSelection){
8167             this.fireEvent('rowclick', this, row, rowIndex, e);
8168         }
8169         
8170         
8171     },
8172         
8173     onDblClick : function(e,el)
8174     {
8175         var cell = Roo.get(el);
8176         
8177         if(!cell || (!this.cellSelection && !this.rowSelection)){
8178             return;
8179         }
8180         
8181         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8182             cell = cell.findParent('td', false, true);
8183         }
8184         
8185         if(!cell || typeof(cell) == 'undefined'){
8186             return;
8187         }
8188         
8189         var row = cell.findParent('tr', false, true);
8190         
8191         if(!row || typeof(row) == 'undefined'){
8192             return;
8193         }
8194         
8195         var cellIndex = cell.dom.cellIndex;
8196         var rowIndex = this.getRowIndex(row);
8197         
8198         if(this.cellSelection){
8199             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8200         }
8201         
8202         if(this.rowSelection){
8203             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8204         }
8205     },
8206     
8207     sort : function(e,el)
8208     {
8209         var col = Roo.get(el);
8210         
8211         if(!col.hasClass('sortable')){
8212             return;
8213         }
8214         
8215         var sort = col.attr('sort');
8216         var dir = 'ASC';
8217         
8218         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8219             dir = 'DESC';
8220         }
8221         
8222         this.store.sortInfo = {field : sort, direction : dir};
8223         
8224         if (this.footer) {
8225             Roo.log("calling footer first");
8226             this.footer.onClick('first');
8227         } else {
8228         
8229             this.store.load({ params : { start : 0 } });
8230         }
8231     },
8232     
8233     renderHeader : function()
8234     {
8235         var header = {
8236             tag: 'thead',
8237             cn : []
8238         };
8239         
8240         var cm = this.cm;
8241         this.totalWidth = 0;
8242         
8243         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8244             
8245             var config = cm.config[i];
8246             
8247             var c = {
8248                 tag: 'th',
8249                 cls : 'x-hcol-' + i,
8250                 style : '',
8251                 html: cm.getColumnHeader(i)
8252             };
8253             
8254             var hh = '';
8255             
8256             if(typeof(config.sortable) != 'undefined' && config.sortable){
8257                 c.cls = 'sortable';
8258                 c.html = '<i class="glyphicon"></i>' + c.html;
8259             }
8260             
8261             // could use BS4 hidden-..-down 
8262             
8263             if(typeof(config.lgHeader) != 'undefined'){
8264                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8265             }
8266             
8267             if(typeof(config.mdHeader) != 'undefined'){
8268                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8269             }
8270             
8271             if(typeof(config.smHeader) != 'undefined'){
8272                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8273             }
8274             
8275             if(typeof(config.xsHeader) != 'undefined'){
8276                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8277             }
8278             
8279             if(hh.length){
8280                 c.html = hh;
8281             }
8282             
8283             if(typeof(config.tooltip) != 'undefined'){
8284                 c.tooltip = config.tooltip;
8285             }
8286             
8287             if(typeof(config.colspan) != 'undefined'){
8288                 c.colspan = config.colspan;
8289             }
8290             
8291             if(typeof(config.hidden) != 'undefined' && config.hidden){
8292                 c.style += ' display:none;';
8293             }
8294             
8295             if(typeof(config.dataIndex) != 'undefined'){
8296                 c.sort = config.dataIndex;
8297             }
8298             
8299            
8300             
8301             if(typeof(config.align) != 'undefined' && config.align.length){
8302                 c.style += ' text-align:' + config.align + ';';
8303             }
8304             
8305             if(typeof(config.width) != 'undefined'){
8306                 c.style += ' width:' + config.width + 'px;';
8307                 this.totalWidth += config.width;
8308             } else {
8309                 this.totalWidth += 100; // assume minimum of 100 per column?
8310             }
8311             
8312             if(typeof(config.cls) != 'undefined'){
8313                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8314             }
8315             
8316             ['xs','sm','md','lg'].map(function(size){
8317                 
8318                 if(typeof(config[size]) == 'undefined'){
8319                     return;
8320                 }
8321                  
8322                 if (!config[size]) { // 0 = hidden
8323                     // BS 4 '0' is treated as hide that column and below.
8324                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8325                     return;
8326                 }
8327                 
8328                 c.cls += ' col-' + size + '-' + config[size] + (
8329                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8330                 );
8331                 
8332                 
8333             });
8334             
8335             header.cn.push(c)
8336         }
8337         
8338         return header;
8339     },
8340     
8341     renderBody : function()
8342     {
8343         var body = {
8344             tag: 'tbody',
8345             cn : [
8346                 {
8347                     tag: 'tr',
8348                     cn : [
8349                         {
8350                             tag : 'td',
8351                             colspan :  this.cm.getColumnCount()
8352                         }
8353                     ]
8354                 }
8355             ]
8356         };
8357         
8358         return body;
8359     },
8360     
8361     renderFooter : function()
8362     {
8363         var footer = {
8364             tag: 'tfoot',
8365             cn : [
8366                 {
8367                     tag: 'tr',
8368                     cn : [
8369                         {
8370                             tag : 'td',
8371                             colspan :  this.cm.getColumnCount()
8372                         }
8373                     ]
8374                 }
8375             ]
8376         };
8377         
8378         return footer;
8379     },
8380     
8381     
8382     
8383     onLoad : function()
8384     {
8385 //        Roo.log('ds onload');
8386         this.clear();
8387         
8388         var _this = this;
8389         var cm = this.cm;
8390         var ds = this.store;
8391         
8392         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8393             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8394             if (_this.store.sortInfo) {
8395                     
8396                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8397                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8398                 }
8399                 
8400                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8401                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8402                 }
8403             }
8404         });
8405         
8406         var tbody =  this.mainBody;
8407               
8408         if(ds.getCount() > 0){
8409             ds.data.each(function(d,rowIndex){
8410                 var row =  this.renderRow(cm, ds, rowIndex);
8411                 
8412                 tbody.createChild(row);
8413                 
8414                 var _this = this;
8415                 
8416                 if(row.cellObjects.length){
8417                     Roo.each(row.cellObjects, function(r){
8418                         _this.renderCellObject(r);
8419                     })
8420                 }
8421                 
8422             }, this);
8423         }
8424         
8425         var tfoot = this.el.select('tfoot', true).first();
8426         
8427         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8428             
8429             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8430             
8431             var total = this.ds.getTotalCount();
8432             
8433             if(this.footer.pageSize < total){
8434                 this.mainFoot.show();
8435             }
8436         }
8437         
8438         Roo.each(this.el.select('tbody td', true).elements, function(e){
8439             e.on('mouseover', _this.onMouseover, _this);
8440         });
8441         
8442         Roo.each(this.el.select('tbody td', true).elements, function(e){
8443             e.on('mouseout', _this.onMouseout, _this);
8444         });
8445         this.fireEvent('rowsrendered', this);
8446         
8447         this.autoSize();
8448     },
8449     
8450     
8451     onUpdate : function(ds,record)
8452     {
8453         this.refreshRow(record);
8454         this.autoSize();
8455     },
8456     
8457     onRemove : function(ds, record, index, isUpdate){
8458         if(isUpdate !== true){
8459             this.fireEvent("beforerowremoved", this, index, record);
8460         }
8461         var bt = this.mainBody.dom;
8462         
8463         var rows = this.el.select('tbody > tr', true).elements;
8464         
8465         if(typeof(rows[index]) != 'undefined'){
8466             bt.removeChild(rows[index].dom);
8467         }
8468         
8469 //        if(bt.rows[index]){
8470 //            bt.removeChild(bt.rows[index]);
8471 //        }
8472         
8473         if(isUpdate !== true){
8474             //this.stripeRows(index);
8475             //this.syncRowHeights(index, index);
8476             //this.layout();
8477             this.fireEvent("rowremoved", this, index, record);
8478         }
8479     },
8480     
8481     onAdd : function(ds, records, rowIndex)
8482     {
8483         //Roo.log('on Add called');
8484         // - note this does not handle multiple adding very well..
8485         var bt = this.mainBody.dom;
8486         for (var i =0 ; i < records.length;i++) {
8487             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8488             //Roo.log(records[i]);
8489             //Roo.log(this.store.getAt(rowIndex+i));
8490             this.insertRow(this.store, rowIndex + i, false);
8491             return;
8492         }
8493         
8494     },
8495     
8496     
8497     refreshRow : function(record){
8498         var ds = this.store, index;
8499         if(typeof record == 'number'){
8500             index = record;
8501             record = ds.getAt(index);
8502         }else{
8503             index = ds.indexOf(record);
8504             if (index < 0) {
8505                 return; // should not happen - but seems to 
8506             }
8507         }
8508         this.insertRow(ds, index, true);
8509         this.autoSize();
8510         this.onRemove(ds, record, index+1, true);
8511         this.autoSize();
8512         //this.syncRowHeights(index, index);
8513         //this.layout();
8514         this.fireEvent("rowupdated", this, index, record);
8515     },
8516     
8517     insertRow : function(dm, rowIndex, isUpdate){
8518         
8519         if(!isUpdate){
8520             this.fireEvent("beforerowsinserted", this, rowIndex);
8521         }
8522             //var s = this.getScrollState();
8523         var row = this.renderRow(this.cm, this.store, rowIndex);
8524         // insert before rowIndex..
8525         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8526         
8527         var _this = this;
8528                 
8529         if(row.cellObjects.length){
8530             Roo.each(row.cellObjects, function(r){
8531                 _this.renderCellObject(r);
8532             })
8533         }
8534             
8535         if(!isUpdate){
8536             this.fireEvent("rowsinserted", this, rowIndex);
8537             //this.syncRowHeights(firstRow, lastRow);
8538             //this.stripeRows(firstRow);
8539             //this.layout();
8540         }
8541         
8542     },
8543     
8544     
8545     getRowDom : function(rowIndex)
8546     {
8547         var rows = this.el.select('tbody > tr', true).elements;
8548         
8549         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8550         
8551     },
8552     // returns the object tree for a tr..
8553   
8554     
8555     renderRow : function(cm, ds, rowIndex) 
8556     {
8557         var d = ds.getAt(rowIndex);
8558         
8559         var row = {
8560             tag : 'tr',
8561             cls : 'x-row-' + rowIndex,
8562             cn : []
8563         };
8564             
8565         var cellObjects = [];
8566         
8567         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8568             var config = cm.config[i];
8569             
8570             var renderer = cm.getRenderer(i);
8571             var value = '';
8572             var id = false;
8573             
8574             if(typeof(renderer) !== 'undefined'){
8575                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8576             }
8577             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8578             // and are rendered into the cells after the row is rendered - using the id for the element.
8579             
8580             if(typeof(value) === 'object'){
8581                 id = Roo.id();
8582                 cellObjects.push({
8583                     container : id,
8584                     cfg : value 
8585                 })
8586             }
8587             
8588             var rowcfg = {
8589                 record: d,
8590                 rowIndex : rowIndex,
8591                 colIndex : i,
8592                 rowClass : ''
8593             };
8594
8595             this.fireEvent('rowclass', this, rowcfg);
8596             
8597             var td = {
8598                 tag: 'td',
8599                 cls : rowcfg.rowClass + ' x-col-' + i,
8600                 style: '',
8601                 html: (typeof(value) === 'object') ? '' : value
8602             };
8603             
8604             if (id) {
8605                 td.id = id;
8606             }
8607             
8608             if(typeof(config.colspan) != 'undefined'){
8609                 td.colspan = config.colspan;
8610             }
8611             
8612             if(typeof(config.hidden) != 'undefined' && config.hidden){
8613                 td.style += ' display:none;';
8614             }
8615             
8616             if(typeof(config.align) != 'undefined' && config.align.length){
8617                 td.style += ' text-align:' + config.align + ';';
8618             }
8619             if(typeof(config.valign) != 'undefined' && config.valign.length){
8620                 td.style += ' vertical-align:' + config.valign + ';';
8621             }
8622             
8623             if(typeof(config.width) != 'undefined'){
8624                 td.style += ' width:' +  config.width + 'px;';
8625             }
8626             
8627             if(typeof(config.cursor) != 'undefined'){
8628                 td.style += ' cursor:' +  config.cursor + ';';
8629             }
8630             
8631             if(typeof(config.cls) != 'undefined'){
8632                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8633             }
8634             
8635             ['xs','sm','md','lg'].map(function(size){
8636                 
8637                 if(typeof(config[size]) == 'undefined'){
8638                     return;
8639                 }
8640                 
8641                 
8642                   
8643                 if (!config[size]) { // 0 = hidden
8644                     // BS 4 '0' is treated as hide that column and below.
8645                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8646                     return;
8647                 }
8648                 
8649                 td.cls += ' col-' + size + '-' + config[size] + (
8650                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8651                 );
8652                  
8653
8654             });
8655             
8656             row.cn.push(td);
8657            
8658         }
8659         
8660         row.cellObjects = cellObjects;
8661         
8662         return row;
8663           
8664     },
8665     
8666     
8667     
8668     onBeforeLoad : function()
8669     {
8670         
8671     },
8672      /**
8673      * Remove all rows
8674      */
8675     clear : function()
8676     {
8677         this.el.select('tbody', true).first().dom.innerHTML = '';
8678     },
8679     /**
8680      * Show or hide a row.
8681      * @param {Number} rowIndex to show or hide
8682      * @param {Boolean} state hide
8683      */
8684     setRowVisibility : function(rowIndex, state)
8685     {
8686         var bt = this.mainBody.dom;
8687         
8688         var rows = this.el.select('tbody > tr', true).elements;
8689         
8690         if(typeof(rows[rowIndex]) == 'undefined'){
8691             return;
8692         }
8693         rows[rowIndex].dom.style.display = state ? '' : 'none';
8694     },
8695     
8696     
8697     getSelectionModel : function(){
8698         if(!this.selModel){
8699             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8700         }
8701         return this.selModel;
8702     },
8703     /*
8704      * Render the Roo.bootstrap object from renderder
8705      */
8706     renderCellObject : function(r)
8707     {
8708         var _this = this;
8709         
8710         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8711         
8712         var t = r.cfg.render(r.container);
8713         
8714         if(r.cfg.cn){
8715             Roo.each(r.cfg.cn, function(c){
8716                 var child = {
8717                     container: t.getChildContainer(),
8718                     cfg: c
8719                 };
8720                 _this.renderCellObject(child);
8721             })
8722         }
8723     },
8724     
8725     getRowIndex : function(row)
8726     {
8727         var rowIndex = -1;
8728         
8729         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8730             if(el != row){
8731                 return;
8732             }
8733             
8734             rowIndex = index;
8735         });
8736         
8737         return rowIndex;
8738     },
8739      /**
8740      * Returns the grid's underlying element = used by panel.Grid
8741      * @return {Element} The element
8742      */
8743     getGridEl : function(){
8744         return this.el;
8745     },
8746      /**
8747      * Forces a resize - used by panel.Grid
8748      * @return {Element} The element
8749      */
8750     autoSize : function()
8751     {
8752         //var ctr = Roo.get(this.container.dom.parentElement);
8753         var ctr = Roo.get(this.el.dom);
8754         
8755         var thd = this.getGridEl().select('thead',true).first();
8756         var tbd = this.getGridEl().select('tbody', true).first();
8757         var tfd = this.getGridEl().select('tfoot', true).first();
8758         
8759         var cw = ctr.getWidth();
8760         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8761         
8762         if (tbd) {
8763             
8764             tbd.setWidth(ctr.getWidth());
8765             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8766             // this needs fixing for various usage - currently only hydra job advers I think..
8767             //tdb.setHeight(
8768             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8769             //); 
8770             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8771             cw -= barsize;
8772         }
8773         cw = Math.max(cw, this.totalWidth);
8774         this.getGridEl().select('tbody tr',true).setWidth(cw);
8775         
8776         // resize 'expandable coloumn?
8777         
8778         return; // we doe not have a view in this design..
8779         
8780     },
8781     onBodyScroll: function()
8782     {
8783         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8784         if(this.mainHead){
8785             this.mainHead.setStyle({
8786                 'position' : 'relative',
8787                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8788             });
8789         }
8790         
8791         if(this.lazyLoad){
8792             
8793             var scrollHeight = this.mainBody.dom.scrollHeight;
8794             
8795             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8796             
8797             var height = this.mainBody.getHeight();
8798             
8799             if(scrollHeight - height == scrollTop) {
8800                 
8801                 var total = this.ds.getTotalCount();
8802                 
8803                 if(this.footer.cursor + this.footer.pageSize < total){
8804                     
8805                     this.footer.ds.load({
8806                         params : {
8807                             start : this.footer.cursor + this.footer.pageSize,
8808                             limit : this.footer.pageSize
8809                         },
8810                         add : true
8811                     });
8812                 }
8813             }
8814             
8815         }
8816     },
8817     
8818     onHeaderChange : function()
8819     {
8820         var header = this.renderHeader();
8821         var table = this.el.select('table', true).first();
8822         
8823         this.mainHead.remove();
8824         this.mainHead = table.createChild(header, this.mainBody, false);
8825     },
8826     
8827     onHiddenChange : function(colModel, colIndex, hidden)
8828     {
8829         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8830         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8831         
8832         this.CSS.updateRule(thSelector, "display", "");
8833         this.CSS.updateRule(tdSelector, "display", "");
8834         
8835         if(hidden){
8836             this.CSS.updateRule(thSelector, "display", "none");
8837             this.CSS.updateRule(tdSelector, "display", "none");
8838         }
8839         
8840         this.onHeaderChange();
8841         this.onLoad();
8842     },
8843     
8844     setColumnWidth: function(col_index, width)
8845     {
8846         // width = "md-2 xs-2..."
8847         if(!this.colModel.config[col_index]) {
8848             return;
8849         }
8850         
8851         var w = width.split(" ");
8852         
8853         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8854         
8855         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8856         
8857         
8858         for(var j = 0; j < w.length; j++) {
8859             
8860             if(!w[j]) {
8861                 continue;
8862             }
8863             
8864             var size_cls = w[j].split("-");
8865             
8866             if(!Number.isInteger(size_cls[1] * 1)) {
8867                 continue;
8868             }
8869             
8870             if(!this.colModel.config[col_index][size_cls[0]]) {
8871                 continue;
8872             }
8873             
8874             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8875                 continue;
8876             }
8877             
8878             h_row[0].classList.replace(
8879                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8880                 "col-"+size_cls[0]+"-"+size_cls[1]
8881             );
8882             
8883             for(var i = 0; i < rows.length; i++) {
8884                 
8885                 var size_cls = w[j].split("-");
8886                 
8887                 if(!Number.isInteger(size_cls[1] * 1)) {
8888                     continue;
8889                 }
8890                 
8891                 if(!this.colModel.config[col_index][size_cls[0]]) {
8892                     continue;
8893                 }
8894                 
8895                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8896                     continue;
8897                 }
8898                 
8899                 rows[i].classList.replace(
8900                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8901                     "col-"+size_cls[0]+"-"+size_cls[1]
8902                 );
8903             }
8904             
8905             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8906         }
8907     }
8908 });
8909
8910  
8911
8912  /*
8913  * - LGPL
8914  *
8915  * table cell
8916  * 
8917  */
8918
8919 /**
8920  * @class Roo.bootstrap.TableCell
8921  * @extends Roo.bootstrap.Component
8922  * Bootstrap TableCell class
8923  * @cfg {String} html cell contain text
8924  * @cfg {String} cls cell class
8925  * @cfg {String} tag cell tag (td|th) default td
8926  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8927  * @cfg {String} align Aligns the content in a cell
8928  * @cfg {String} axis Categorizes cells
8929  * @cfg {String} bgcolor Specifies the background color of a cell
8930  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8931  * @cfg {Number} colspan Specifies the number of columns a cell should span
8932  * @cfg {String} headers Specifies one or more header cells a cell is related to
8933  * @cfg {Number} height Sets the height of a cell
8934  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8935  * @cfg {Number} rowspan Sets the number of rows a cell should span
8936  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8937  * @cfg {String} valign Vertical aligns the content in a cell
8938  * @cfg {Number} width Specifies the width of a cell
8939  * 
8940  * @constructor
8941  * Create a new TableCell
8942  * @param {Object} config The config object
8943  */
8944
8945 Roo.bootstrap.TableCell = function(config){
8946     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8947 };
8948
8949 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8950     
8951     html: false,
8952     cls: false,
8953     tag: false,
8954     abbr: false,
8955     align: false,
8956     axis: false,
8957     bgcolor: false,
8958     charoff: false,
8959     colspan: false,
8960     headers: false,
8961     height: false,
8962     nowrap: false,
8963     rowspan: false,
8964     scope: false,
8965     valign: false,
8966     width: false,
8967     
8968     
8969     getAutoCreate : function(){
8970         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8971         
8972         cfg = {
8973             tag: 'td'
8974         };
8975         
8976         if(this.tag){
8977             cfg.tag = this.tag;
8978         }
8979         
8980         if (this.html) {
8981             cfg.html=this.html
8982         }
8983         if (this.cls) {
8984             cfg.cls=this.cls
8985         }
8986         if (this.abbr) {
8987             cfg.abbr=this.abbr
8988         }
8989         if (this.align) {
8990             cfg.align=this.align
8991         }
8992         if (this.axis) {
8993             cfg.axis=this.axis
8994         }
8995         if (this.bgcolor) {
8996             cfg.bgcolor=this.bgcolor
8997         }
8998         if (this.charoff) {
8999             cfg.charoff=this.charoff
9000         }
9001         if (this.colspan) {
9002             cfg.colspan=this.colspan
9003         }
9004         if (this.headers) {
9005             cfg.headers=this.headers
9006         }
9007         if (this.height) {
9008             cfg.height=this.height
9009         }
9010         if (this.nowrap) {
9011             cfg.nowrap=this.nowrap
9012         }
9013         if (this.rowspan) {
9014             cfg.rowspan=this.rowspan
9015         }
9016         if (this.scope) {
9017             cfg.scope=this.scope
9018         }
9019         if (this.valign) {
9020             cfg.valign=this.valign
9021         }
9022         if (this.width) {
9023             cfg.width=this.width
9024         }
9025         
9026         
9027         return cfg;
9028     }
9029    
9030 });
9031
9032  
9033
9034  /*
9035  * - LGPL
9036  *
9037  * table row
9038  * 
9039  */
9040
9041 /**
9042  * @class Roo.bootstrap.TableRow
9043  * @extends Roo.bootstrap.Component
9044  * Bootstrap TableRow class
9045  * @cfg {String} cls row class
9046  * @cfg {String} align Aligns the content in a table row
9047  * @cfg {String} bgcolor Specifies a background color for a table row
9048  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9049  * @cfg {String} valign Vertical aligns the content in a table row
9050  * 
9051  * @constructor
9052  * Create a new TableRow
9053  * @param {Object} config The config object
9054  */
9055
9056 Roo.bootstrap.TableRow = function(config){
9057     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9058 };
9059
9060 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9061     
9062     cls: false,
9063     align: false,
9064     bgcolor: false,
9065     charoff: false,
9066     valign: false,
9067     
9068     getAutoCreate : function(){
9069         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9070         
9071         cfg = {
9072             tag: 'tr'
9073         };
9074             
9075         if(this.cls){
9076             cfg.cls = this.cls;
9077         }
9078         if(this.align){
9079             cfg.align = this.align;
9080         }
9081         if(this.bgcolor){
9082             cfg.bgcolor = this.bgcolor;
9083         }
9084         if(this.charoff){
9085             cfg.charoff = this.charoff;
9086         }
9087         if(this.valign){
9088             cfg.valign = this.valign;
9089         }
9090         
9091         return cfg;
9092     }
9093    
9094 });
9095
9096  
9097
9098  /*
9099  * - LGPL
9100  *
9101  * table body
9102  * 
9103  */
9104
9105 /**
9106  * @class Roo.bootstrap.TableBody
9107  * @extends Roo.bootstrap.Component
9108  * Bootstrap TableBody class
9109  * @cfg {String} cls element class
9110  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9111  * @cfg {String} align Aligns the content inside the element
9112  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9113  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9114  * 
9115  * @constructor
9116  * Create a new TableBody
9117  * @param {Object} config The config object
9118  */
9119
9120 Roo.bootstrap.TableBody = function(config){
9121     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9122 };
9123
9124 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9125     
9126     cls: false,
9127     tag: false,
9128     align: false,
9129     charoff: false,
9130     valign: false,
9131     
9132     getAutoCreate : function(){
9133         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9134         
9135         cfg = {
9136             tag: 'tbody'
9137         };
9138             
9139         if (this.cls) {
9140             cfg.cls=this.cls
9141         }
9142         if(this.tag){
9143             cfg.tag = this.tag;
9144         }
9145         
9146         if(this.align){
9147             cfg.align = this.align;
9148         }
9149         if(this.charoff){
9150             cfg.charoff = this.charoff;
9151         }
9152         if(this.valign){
9153             cfg.valign = this.valign;
9154         }
9155         
9156         return cfg;
9157     }
9158     
9159     
9160 //    initEvents : function()
9161 //    {
9162 //        
9163 //        if(!this.store){
9164 //            return;
9165 //        }
9166 //        
9167 //        this.store = Roo.factory(this.store, Roo.data);
9168 //        this.store.on('load', this.onLoad, this);
9169 //        
9170 //        this.store.load();
9171 //        
9172 //    },
9173 //    
9174 //    onLoad: function () 
9175 //    {   
9176 //        this.fireEvent('load', this);
9177 //    }
9178 //    
9179 //   
9180 });
9181
9182  
9183
9184  /*
9185  * Based on:
9186  * Ext JS Library 1.1.1
9187  * Copyright(c) 2006-2007, Ext JS, LLC.
9188  *
9189  * Originally Released Under LGPL - original licence link has changed is not relivant.
9190  *
9191  * Fork - LGPL
9192  * <script type="text/javascript">
9193  */
9194
9195 // as we use this in bootstrap.
9196 Roo.namespace('Roo.form');
9197  /**
9198  * @class Roo.form.Action
9199  * Internal Class used to handle form actions
9200  * @constructor
9201  * @param {Roo.form.BasicForm} el The form element or its id
9202  * @param {Object} config Configuration options
9203  */
9204
9205  
9206  
9207 // define the action interface
9208 Roo.form.Action = function(form, options){
9209     this.form = form;
9210     this.options = options || {};
9211 };
9212 /**
9213  * Client Validation Failed
9214  * @const 
9215  */
9216 Roo.form.Action.CLIENT_INVALID = 'client';
9217 /**
9218  * Server Validation Failed
9219  * @const 
9220  */
9221 Roo.form.Action.SERVER_INVALID = 'server';
9222  /**
9223  * Connect to Server Failed
9224  * @const 
9225  */
9226 Roo.form.Action.CONNECT_FAILURE = 'connect';
9227 /**
9228  * Reading Data from Server Failed
9229  * @const 
9230  */
9231 Roo.form.Action.LOAD_FAILURE = 'load';
9232
9233 Roo.form.Action.prototype = {
9234     type : 'default',
9235     failureType : undefined,
9236     response : undefined,
9237     result : undefined,
9238
9239     // interface method
9240     run : function(options){
9241
9242     },
9243
9244     // interface method
9245     success : function(response){
9246
9247     },
9248
9249     // interface method
9250     handleResponse : function(response){
9251
9252     },
9253
9254     // default connection failure
9255     failure : function(response){
9256         
9257         this.response = response;
9258         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9259         this.form.afterAction(this, false);
9260     },
9261
9262     processResponse : function(response){
9263         this.response = response;
9264         if(!response.responseText){
9265             return true;
9266         }
9267         this.result = this.handleResponse(response);
9268         return this.result;
9269     },
9270
9271     // utility functions used internally
9272     getUrl : function(appendParams){
9273         var url = this.options.url || this.form.url || this.form.el.dom.action;
9274         if(appendParams){
9275             var p = this.getParams();
9276             if(p){
9277                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9278             }
9279         }
9280         return url;
9281     },
9282
9283     getMethod : function(){
9284         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9285     },
9286
9287     getParams : function(){
9288         var bp = this.form.baseParams;
9289         var p = this.options.params;
9290         if(p){
9291             if(typeof p == "object"){
9292                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9293             }else if(typeof p == 'string' && bp){
9294                 p += '&' + Roo.urlEncode(bp);
9295             }
9296         }else if(bp){
9297             p = Roo.urlEncode(bp);
9298         }
9299         return p;
9300     },
9301
9302     createCallback : function(){
9303         return {
9304             success: this.success,
9305             failure: this.failure,
9306             scope: this,
9307             timeout: (this.form.timeout*1000),
9308             upload: this.form.fileUpload ? this.success : undefined
9309         };
9310     }
9311 };
9312
9313 Roo.form.Action.Submit = function(form, options){
9314     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9315 };
9316
9317 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9318     type : 'submit',
9319
9320     haveProgress : false,
9321     uploadComplete : false,
9322     
9323     // uploadProgress indicator.
9324     uploadProgress : function()
9325     {
9326         if (!this.form.progressUrl) {
9327             return;
9328         }
9329         
9330         if (!this.haveProgress) {
9331             Roo.MessageBox.progress("Uploading", "Uploading");
9332         }
9333         if (this.uploadComplete) {
9334            Roo.MessageBox.hide();
9335            return;
9336         }
9337         
9338         this.haveProgress = true;
9339    
9340         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9341         
9342         var c = new Roo.data.Connection();
9343         c.request({
9344             url : this.form.progressUrl,
9345             params: {
9346                 id : uid
9347             },
9348             method: 'GET',
9349             success : function(req){
9350                //console.log(data);
9351                 var rdata = false;
9352                 var edata;
9353                 try  {
9354                    rdata = Roo.decode(req.responseText)
9355                 } catch (e) {
9356                     Roo.log("Invalid data from server..");
9357                     Roo.log(edata);
9358                     return;
9359                 }
9360                 if (!rdata || !rdata.success) {
9361                     Roo.log(rdata);
9362                     Roo.MessageBox.alert(Roo.encode(rdata));
9363                     return;
9364                 }
9365                 var data = rdata.data;
9366                 
9367                 if (this.uploadComplete) {
9368                    Roo.MessageBox.hide();
9369                    return;
9370                 }
9371                    
9372                 if (data){
9373                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9374                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9375                     );
9376                 }
9377                 this.uploadProgress.defer(2000,this);
9378             },
9379        
9380             failure: function(data) {
9381                 Roo.log('progress url failed ');
9382                 Roo.log(data);
9383             },
9384             scope : this
9385         });
9386            
9387     },
9388     
9389     
9390     run : function()
9391     {
9392         // run get Values on the form, so it syncs any secondary forms.
9393         this.form.getValues();
9394         
9395         var o = this.options;
9396         var method = this.getMethod();
9397         var isPost = method == 'POST';
9398         if(o.clientValidation === false || this.form.isValid()){
9399             
9400             if (this.form.progressUrl) {
9401                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9402                     (new Date() * 1) + '' + Math.random());
9403                     
9404             } 
9405             
9406             
9407             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9408                 form:this.form.el.dom,
9409                 url:this.getUrl(!isPost),
9410                 method: method,
9411                 params:isPost ? this.getParams() : null,
9412                 isUpload: this.form.fileUpload,
9413                 formData : this.form.formData
9414             }));
9415             
9416             this.uploadProgress();
9417
9418         }else if (o.clientValidation !== false){ // client validation failed
9419             this.failureType = Roo.form.Action.CLIENT_INVALID;
9420             this.form.afterAction(this, false);
9421         }
9422     },
9423
9424     success : function(response)
9425     {
9426         this.uploadComplete= true;
9427         if (this.haveProgress) {
9428             Roo.MessageBox.hide();
9429         }
9430         
9431         
9432         var result = this.processResponse(response);
9433         if(result === true || result.success){
9434             this.form.afterAction(this, true);
9435             return;
9436         }
9437         if(result.errors){
9438             this.form.markInvalid(result.errors);
9439             this.failureType = Roo.form.Action.SERVER_INVALID;
9440         }
9441         this.form.afterAction(this, false);
9442     },
9443     failure : function(response)
9444     {
9445         this.uploadComplete= true;
9446         if (this.haveProgress) {
9447             Roo.MessageBox.hide();
9448         }
9449         
9450         this.response = response;
9451         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9452         this.form.afterAction(this, false);
9453     },
9454     
9455     handleResponse : function(response){
9456         if(this.form.errorReader){
9457             var rs = this.form.errorReader.read(response);
9458             var errors = [];
9459             if(rs.records){
9460                 for(var i = 0, len = rs.records.length; i < len; i++) {
9461                     var r = rs.records[i];
9462                     errors[i] = r.data;
9463                 }
9464             }
9465             if(errors.length < 1){
9466                 errors = null;
9467             }
9468             return {
9469                 success : rs.success,
9470                 errors : errors
9471             };
9472         }
9473         var ret = false;
9474         try {
9475             ret = Roo.decode(response.responseText);
9476         } catch (e) {
9477             ret = {
9478                 success: false,
9479                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9480                 errors : []
9481             };
9482         }
9483         return ret;
9484         
9485     }
9486 });
9487
9488
9489 Roo.form.Action.Load = function(form, options){
9490     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9491     this.reader = this.form.reader;
9492 };
9493
9494 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9495     type : 'load',
9496
9497     run : function(){
9498         
9499         Roo.Ajax.request(Roo.apply(
9500                 this.createCallback(), {
9501                     method:this.getMethod(),
9502                     url:this.getUrl(false),
9503                     params:this.getParams()
9504         }));
9505     },
9506
9507     success : function(response){
9508         
9509         var result = this.processResponse(response);
9510         if(result === true || !result.success || !result.data){
9511             this.failureType = Roo.form.Action.LOAD_FAILURE;
9512             this.form.afterAction(this, false);
9513             return;
9514         }
9515         this.form.clearInvalid();
9516         this.form.setValues(result.data);
9517         this.form.afterAction(this, true);
9518     },
9519
9520     handleResponse : function(response){
9521         if(this.form.reader){
9522             var rs = this.form.reader.read(response);
9523             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9524             return {
9525                 success : rs.success,
9526                 data : data
9527             };
9528         }
9529         return Roo.decode(response.responseText);
9530     }
9531 });
9532
9533 Roo.form.Action.ACTION_TYPES = {
9534     'load' : Roo.form.Action.Load,
9535     'submit' : Roo.form.Action.Submit
9536 };/*
9537  * - LGPL
9538  *
9539  * form
9540  *
9541  */
9542
9543 /**
9544  * @class Roo.bootstrap.Form
9545  * @extends Roo.bootstrap.Component
9546  * Bootstrap Form class
9547  * @cfg {String} method  GET | POST (default POST)
9548  * @cfg {String} labelAlign top | left (default top)
9549  * @cfg {String} align left  | right - for navbars
9550  * @cfg {Boolean} loadMask load mask when submit (default true)
9551
9552  *
9553  * @constructor
9554  * Create a new Form
9555  * @param {Object} config The config object
9556  */
9557
9558
9559 Roo.bootstrap.Form = function(config){
9560     
9561     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9562     
9563     Roo.bootstrap.Form.popover.apply();
9564     
9565     this.addEvents({
9566         /**
9567          * @event clientvalidation
9568          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9569          * @param {Form} this
9570          * @param {Boolean} valid true if the form has passed client-side validation
9571          */
9572         clientvalidation: true,
9573         /**
9574          * @event beforeaction
9575          * Fires before any action is performed. Return false to cancel the action.
9576          * @param {Form} this
9577          * @param {Action} action The action to be performed
9578          */
9579         beforeaction: true,
9580         /**
9581          * @event actionfailed
9582          * Fires when an action fails.
9583          * @param {Form} this
9584          * @param {Action} action The action that failed
9585          */
9586         actionfailed : true,
9587         /**
9588          * @event actioncomplete
9589          * Fires when an action is completed.
9590          * @param {Form} this
9591          * @param {Action} action The action that completed
9592          */
9593         actioncomplete : true
9594     });
9595 };
9596
9597 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9598
9599      /**
9600      * @cfg {String} method
9601      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9602      */
9603     method : 'POST',
9604     /**
9605      * @cfg {String} url
9606      * The URL to use for form actions if one isn't supplied in the action options.
9607      */
9608     /**
9609      * @cfg {Boolean} fileUpload
9610      * Set to true if this form is a file upload.
9611      */
9612
9613     /**
9614      * @cfg {Object} baseParams
9615      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9616      */
9617
9618     /**
9619      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9620      */
9621     timeout: 30,
9622     /**
9623      * @cfg {Sting} align (left|right) for navbar forms
9624      */
9625     align : 'left',
9626
9627     // private
9628     activeAction : null,
9629
9630     /**
9631      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9632      * element by passing it or its id or mask the form itself by passing in true.
9633      * @type Mixed
9634      */
9635     waitMsgTarget : false,
9636
9637     loadMask : true,
9638     
9639     /**
9640      * @cfg {Boolean} errorMask (true|false) default false
9641      */
9642     errorMask : false,
9643     
9644     /**
9645      * @cfg {Number} maskOffset Default 100
9646      */
9647     maskOffset : 100,
9648     
9649     /**
9650      * @cfg {Boolean} maskBody
9651      */
9652     maskBody : false,
9653
9654     getAutoCreate : function(){
9655
9656         var cfg = {
9657             tag: 'form',
9658             method : this.method || 'POST',
9659             id : this.id || Roo.id(),
9660             cls : ''
9661         };
9662         if (this.parent().xtype.match(/^Nav/)) {
9663             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9664
9665         }
9666
9667         if (this.labelAlign == 'left' ) {
9668             cfg.cls += ' form-horizontal';
9669         }
9670
9671
9672         return cfg;
9673     },
9674     initEvents : function()
9675     {
9676         this.el.on('submit', this.onSubmit, this);
9677         // this was added as random key presses on the form where triggering form submit.
9678         this.el.on('keypress', function(e) {
9679             if (e.getCharCode() != 13) {
9680                 return true;
9681             }
9682             // we might need to allow it for textareas.. and some other items.
9683             // check e.getTarget().
9684
9685             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9686                 return true;
9687             }
9688
9689             Roo.log("keypress blocked");
9690
9691             e.preventDefault();
9692             return false;
9693         });
9694         
9695     },
9696     // private
9697     onSubmit : function(e){
9698         e.stopEvent();
9699     },
9700
9701      /**
9702      * Returns true if client-side validation on the form is successful.
9703      * @return Boolean
9704      */
9705     isValid : function(){
9706         var items = this.getItems();
9707         var valid = true;
9708         var target = false;
9709         
9710         items.each(function(f){
9711             
9712             if(f.validate()){
9713                 return;
9714             }
9715             
9716             Roo.log('invalid field: ' + f.name);
9717             
9718             valid = false;
9719
9720             if(!target && f.el.isVisible(true)){
9721                 target = f;
9722             }
9723            
9724         });
9725         
9726         if(this.errorMask && !valid){
9727             Roo.bootstrap.Form.popover.mask(this, target);
9728         }
9729         
9730         return valid;
9731     },
9732     
9733     /**
9734      * Returns true if any fields in this form have changed since their original load.
9735      * @return Boolean
9736      */
9737     isDirty : function(){
9738         var dirty = false;
9739         var items = this.getItems();
9740         items.each(function(f){
9741            if(f.isDirty()){
9742                dirty = true;
9743                return false;
9744            }
9745            return true;
9746         });
9747         return dirty;
9748     },
9749      /**
9750      * Performs a predefined action (submit or load) or custom actions you define on this form.
9751      * @param {String} actionName The name of the action type
9752      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9753      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9754      * accept other config options):
9755      * <pre>
9756 Property          Type             Description
9757 ----------------  ---------------  ----------------------------------------------------------------------------------
9758 url               String           The url for the action (defaults to the form's url)
9759 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9760 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9761 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9762                                    validate the form on the client (defaults to false)
9763      * </pre>
9764      * @return {BasicForm} this
9765      */
9766     doAction : function(action, options){
9767         if(typeof action == 'string'){
9768             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9769         }
9770         if(this.fireEvent('beforeaction', this, action) !== false){
9771             this.beforeAction(action);
9772             action.run.defer(100, action);
9773         }
9774         return this;
9775     },
9776
9777     // private
9778     beforeAction : function(action){
9779         var o = action.options;
9780         
9781         if(this.loadMask){
9782             
9783             if(this.maskBody){
9784                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9785             } else {
9786                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9787             }
9788         }
9789         // not really supported yet.. ??
9790
9791         //if(this.waitMsgTarget === true){
9792         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9793         //}else if(this.waitMsgTarget){
9794         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9795         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9796         //}else {
9797         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9798        // }
9799
9800     },
9801
9802     // private
9803     afterAction : function(action, success){
9804         this.activeAction = null;
9805         var o = action.options;
9806
9807         if(this.loadMask){
9808             
9809             if(this.maskBody){
9810                 Roo.get(document.body).unmask();
9811             } else {
9812                 this.el.unmask();
9813             }
9814         }
9815         
9816         //if(this.waitMsgTarget === true){
9817 //            this.el.unmask();
9818         //}else if(this.waitMsgTarget){
9819         //    this.waitMsgTarget.unmask();
9820         //}else{
9821         //    Roo.MessageBox.updateProgress(1);
9822         //    Roo.MessageBox.hide();
9823        // }
9824         //
9825         if(success){
9826             if(o.reset){
9827                 this.reset();
9828             }
9829             Roo.callback(o.success, o.scope, [this, action]);
9830             this.fireEvent('actioncomplete', this, action);
9831
9832         }else{
9833
9834             // failure condition..
9835             // we have a scenario where updates need confirming.
9836             // eg. if a locking scenario exists..
9837             // we look for { errors : { needs_confirm : true }} in the response.
9838             if (
9839                 (typeof(action.result) != 'undefined')  &&
9840                 (typeof(action.result.errors) != 'undefined')  &&
9841                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9842            ){
9843                 var _t = this;
9844                 Roo.log("not supported yet");
9845                  /*
9846
9847                 Roo.MessageBox.confirm(
9848                     "Change requires confirmation",
9849                     action.result.errorMsg,
9850                     function(r) {
9851                         if (r != 'yes') {
9852                             return;
9853                         }
9854                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9855                     }
9856
9857                 );
9858                 */
9859
9860
9861                 return;
9862             }
9863
9864             Roo.callback(o.failure, o.scope, [this, action]);
9865             // show an error message if no failed handler is set..
9866             if (!this.hasListener('actionfailed')) {
9867                 Roo.log("need to add dialog support");
9868                 /*
9869                 Roo.MessageBox.alert("Error",
9870                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9871                         action.result.errorMsg :
9872                         "Saving Failed, please check your entries or try again"
9873                 );
9874                 */
9875             }
9876
9877             this.fireEvent('actionfailed', this, action);
9878         }
9879
9880     },
9881     /**
9882      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9883      * @param {String} id The value to search for
9884      * @return Field
9885      */
9886     findField : function(id){
9887         var items = this.getItems();
9888         var field = items.get(id);
9889         if(!field){
9890              items.each(function(f){
9891                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9892                     field = f;
9893                     return false;
9894                 }
9895                 return true;
9896             });
9897         }
9898         return field || null;
9899     },
9900      /**
9901      * Mark fields in this form invalid in bulk.
9902      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9903      * @return {BasicForm} this
9904      */
9905     markInvalid : function(errors){
9906         if(errors instanceof Array){
9907             for(var i = 0, len = errors.length; i < len; i++){
9908                 var fieldError = errors[i];
9909                 var f = this.findField(fieldError.id);
9910                 if(f){
9911                     f.markInvalid(fieldError.msg);
9912                 }
9913             }
9914         }else{
9915             var field, id;
9916             for(id in errors){
9917                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9918                     field.markInvalid(errors[id]);
9919                 }
9920             }
9921         }
9922         //Roo.each(this.childForms || [], function (f) {
9923         //    f.markInvalid(errors);
9924         //});
9925
9926         return this;
9927     },
9928
9929     /**
9930      * Set values for fields in this form in bulk.
9931      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9932      * @return {BasicForm} this
9933      */
9934     setValues : function(values){
9935         if(values instanceof Array){ // array of objects
9936             for(var i = 0, len = values.length; i < len; i++){
9937                 var v = values[i];
9938                 var f = this.findField(v.id);
9939                 if(f){
9940                     f.setValue(v.value);
9941                     if(this.trackResetOnLoad){
9942                         f.originalValue = f.getValue();
9943                     }
9944                 }
9945             }
9946         }else{ // object hash
9947             var field, id;
9948             for(id in values){
9949                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9950
9951                     if (field.setFromData &&
9952                         field.valueField &&
9953                         field.displayField &&
9954                         // combos' with local stores can
9955                         // be queried via setValue()
9956                         // to set their value..
9957                         (field.store && !field.store.isLocal)
9958                         ) {
9959                         // it's a combo
9960                         var sd = { };
9961                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9962                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9963                         field.setFromData(sd);
9964
9965                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9966                         
9967                         field.setFromData(values);
9968                         
9969                     } else {
9970                         field.setValue(values[id]);
9971                     }
9972
9973
9974                     if(this.trackResetOnLoad){
9975                         field.originalValue = field.getValue();
9976                     }
9977                 }
9978             }
9979         }
9980
9981         //Roo.each(this.childForms || [], function (f) {
9982         //    f.setValues(values);
9983         //});
9984
9985         return this;
9986     },
9987
9988     /**
9989      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9990      * they are returned as an array.
9991      * @param {Boolean} asString
9992      * @return {Object}
9993      */
9994     getValues : function(asString){
9995         //if (this.childForms) {
9996             // copy values from the child forms
9997         //    Roo.each(this.childForms, function (f) {
9998         //        this.setValues(f.getValues());
9999         //    }, this);
10000         //}
10001
10002
10003
10004         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10005         if(asString === true){
10006             return fs;
10007         }
10008         return Roo.urlDecode(fs);
10009     },
10010
10011     /**
10012      * Returns the fields in this form as an object with key/value pairs.
10013      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10014      * @return {Object}
10015      */
10016     getFieldValues : function(with_hidden)
10017     {
10018         var items = this.getItems();
10019         var ret = {};
10020         items.each(function(f){
10021             
10022             if (!f.getName()) {
10023                 return;
10024             }
10025             
10026             var v = f.getValue();
10027             
10028             if (f.inputType =='radio') {
10029                 if (typeof(ret[f.getName()]) == 'undefined') {
10030                     ret[f.getName()] = ''; // empty..
10031                 }
10032
10033                 if (!f.el.dom.checked) {
10034                     return;
10035
10036                 }
10037                 v = f.el.dom.value;
10038
10039             }
10040             
10041             if(f.xtype == 'MoneyField'){
10042                 ret[f.currencyName] = f.getCurrency();
10043             }
10044
10045             // not sure if this supported any more..
10046             if ((typeof(v) == 'object') && f.getRawValue) {
10047                 v = f.getRawValue() ; // dates..
10048             }
10049             // combo boxes where name != hiddenName...
10050             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10051                 ret[f.name] = f.getRawValue();
10052             }
10053             ret[f.getName()] = v;
10054         });
10055
10056         return ret;
10057     },
10058
10059     /**
10060      * Clears all invalid messages in this form.
10061      * @return {BasicForm} this
10062      */
10063     clearInvalid : function(){
10064         var items = this.getItems();
10065
10066         items.each(function(f){
10067            f.clearInvalid();
10068         });
10069
10070         return this;
10071     },
10072
10073     /**
10074      * Resets this form.
10075      * @return {BasicForm} this
10076      */
10077     reset : function(){
10078         var items = this.getItems();
10079         items.each(function(f){
10080             f.reset();
10081         });
10082
10083         Roo.each(this.childForms || [], function (f) {
10084             f.reset();
10085         });
10086
10087
10088         return this;
10089     },
10090     
10091     getItems : function()
10092     {
10093         var r=new Roo.util.MixedCollection(false, function(o){
10094             return o.id || (o.id = Roo.id());
10095         });
10096         var iter = function(el) {
10097             if (el.inputEl) {
10098                 r.add(el);
10099             }
10100             if (!el.items) {
10101                 return;
10102             }
10103             Roo.each(el.items,function(e) {
10104                 iter(e);
10105             });
10106         };
10107
10108         iter(this);
10109         return r;
10110     },
10111     
10112     hideFields : function(items)
10113     {
10114         Roo.each(items, function(i){
10115             
10116             var f = this.findField(i);
10117             
10118             if(!f){
10119                 return;
10120             }
10121             
10122             f.hide();
10123             
10124         }, this);
10125     },
10126     
10127     showFields : function(items)
10128     {
10129         Roo.each(items, function(i){
10130             
10131             var f = this.findField(i);
10132             
10133             if(!f){
10134                 return;
10135             }
10136             
10137             f.show();
10138             
10139         }, this);
10140     }
10141
10142 });
10143
10144 Roo.apply(Roo.bootstrap.Form, {
10145     
10146     popover : {
10147         
10148         padding : 5,
10149         
10150         isApplied : false,
10151         
10152         isMasked : false,
10153         
10154         form : false,
10155         
10156         target : false,
10157         
10158         toolTip : false,
10159         
10160         intervalID : false,
10161         
10162         maskEl : false,
10163         
10164         apply : function()
10165         {
10166             if(this.isApplied){
10167                 return;
10168             }
10169             
10170             this.maskEl = {
10171                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10172                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10173                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10174                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10175             };
10176             
10177             this.maskEl.top.enableDisplayMode("block");
10178             this.maskEl.left.enableDisplayMode("block");
10179             this.maskEl.bottom.enableDisplayMode("block");
10180             this.maskEl.right.enableDisplayMode("block");
10181             
10182             this.toolTip = new Roo.bootstrap.Tooltip({
10183                 cls : 'roo-form-error-popover',
10184                 alignment : {
10185                     'left' : ['r-l', [-2,0], 'right'],
10186                     'right' : ['l-r', [2,0], 'left'],
10187                     'bottom' : ['tl-bl', [0,2], 'top'],
10188                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10189                 }
10190             });
10191             
10192             this.toolTip.render(Roo.get(document.body));
10193
10194             this.toolTip.el.enableDisplayMode("block");
10195             
10196             Roo.get(document.body).on('click', function(){
10197                 this.unmask();
10198             }, this);
10199             
10200             Roo.get(document.body).on('touchstart', function(){
10201                 this.unmask();
10202             }, this);
10203             
10204             this.isApplied = true
10205         },
10206         
10207         mask : function(form, target)
10208         {
10209             this.form = form;
10210             
10211             this.target = target;
10212             
10213             if(!this.form.errorMask || !target.el){
10214                 return;
10215             }
10216             
10217             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10218             
10219             Roo.log(scrollable);
10220             
10221             var ot = this.target.el.calcOffsetsTo(scrollable);
10222             
10223             var scrollTo = ot[1] - this.form.maskOffset;
10224             
10225             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10226             
10227             scrollable.scrollTo('top', scrollTo);
10228             
10229             var box = this.target.el.getBox();
10230             Roo.log(box);
10231             var zIndex = Roo.bootstrap.Modal.zIndex++;
10232
10233             
10234             this.maskEl.top.setStyle('position', 'absolute');
10235             this.maskEl.top.setStyle('z-index', zIndex);
10236             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10237             this.maskEl.top.setLeft(0);
10238             this.maskEl.top.setTop(0);
10239             this.maskEl.top.show();
10240             
10241             this.maskEl.left.setStyle('position', 'absolute');
10242             this.maskEl.left.setStyle('z-index', zIndex);
10243             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10244             this.maskEl.left.setLeft(0);
10245             this.maskEl.left.setTop(box.y - this.padding);
10246             this.maskEl.left.show();
10247
10248             this.maskEl.bottom.setStyle('position', 'absolute');
10249             this.maskEl.bottom.setStyle('z-index', zIndex);
10250             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10251             this.maskEl.bottom.setLeft(0);
10252             this.maskEl.bottom.setTop(box.bottom + this.padding);
10253             this.maskEl.bottom.show();
10254
10255             this.maskEl.right.setStyle('position', 'absolute');
10256             this.maskEl.right.setStyle('z-index', zIndex);
10257             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10258             this.maskEl.right.setLeft(box.right + this.padding);
10259             this.maskEl.right.setTop(box.y - this.padding);
10260             this.maskEl.right.show();
10261
10262             this.toolTip.bindEl = this.target.el;
10263
10264             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10265
10266             var tip = this.target.blankText;
10267
10268             if(this.target.getValue() !== '' ) {
10269                 
10270                 if (this.target.invalidText.length) {
10271                     tip = this.target.invalidText;
10272                 } else if (this.target.regexText.length){
10273                     tip = this.target.regexText;
10274                 }
10275             }
10276
10277             this.toolTip.show(tip);
10278
10279             this.intervalID = window.setInterval(function() {
10280                 Roo.bootstrap.Form.popover.unmask();
10281             }, 10000);
10282
10283             window.onwheel = function(){ return false;};
10284             
10285             (function(){ this.isMasked = true; }).defer(500, this);
10286             
10287         },
10288         
10289         unmask : function()
10290         {
10291             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10292                 return;
10293             }
10294             
10295             this.maskEl.top.setStyle('position', 'absolute');
10296             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10297             this.maskEl.top.hide();
10298
10299             this.maskEl.left.setStyle('position', 'absolute');
10300             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10301             this.maskEl.left.hide();
10302
10303             this.maskEl.bottom.setStyle('position', 'absolute');
10304             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10305             this.maskEl.bottom.hide();
10306
10307             this.maskEl.right.setStyle('position', 'absolute');
10308             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10309             this.maskEl.right.hide();
10310             
10311             this.toolTip.hide();
10312             
10313             this.toolTip.el.hide();
10314             
10315             window.onwheel = function(){ return true;};
10316             
10317             if(this.intervalID){
10318                 window.clearInterval(this.intervalID);
10319                 this.intervalID = false;
10320             }
10321             
10322             this.isMasked = false;
10323             
10324         }
10325         
10326     }
10327     
10328 });
10329
10330 /*
10331  * Based on:
10332  * Ext JS Library 1.1.1
10333  * Copyright(c) 2006-2007, Ext JS, LLC.
10334  *
10335  * Originally Released Under LGPL - original licence link has changed is not relivant.
10336  *
10337  * Fork - LGPL
10338  * <script type="text/javascript">
10339  */
10340 /**
10341  * @class Roo.form.VTypes
10342  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10343  * @singleton
10344  */
10345 Roo.form.VTypes = function(){
10346     // closure these in so they are only created once.
10347     var alpha = /^[a-zA-Z_]+$/;
10348     var alphanum = /^[a-zA-Z0-9_]+$/;
10349     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10350     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10351
10352     // All these messages and functions are configurable
10353     return {
10354         /**
10355          * The function used to validate email addresses
10356          * @param {String} value The email address
10357          */
10358         'email' : function(v){
10359             return email.test(v);
10360         },
10361         /**
10362          * The error text to display when the email validation function returns false
10363          * @type String
10364          */
10365         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10366         /**
10367          * The keystroke filter mask to be applied on email input
10368          * @type RegExp
10369          */
10370         'emailMask' : /[a-z0-9_\.\-@]/i,
10371
10372         /**
10373          * The function used to validate URLs
10374          * @param {String} value The URL
10375          */
10376         'url' : function(v){
10377             return url.test(v);
10378         },
10379         /**
10380          * The error text to display when the url validation function returns false
10381          * @type String
10382          */
10383         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10384         
10385         /**
10386          * The function used to validate alpha values
10387          * @param {String} value The value
10388          */
10389         'alpha' : function(v){
10390             return alpha.test(v);
10391         },
10392         /**
10393          * The error text to display when the alpha validation function returns false
10394          * @type String
10395          */
10396         'alphaText' : 'This field should only contain letters and _',
10397         /**
10398          * The keystroke filter mask to be applied on alpha input
10399          * @type RegExp
10400          */
10401         'alphaMask' : /[a-z_]/i,
10402
10403         /**
10404          * The function used to validate alphanumeric values
10405          * @param {String} value The value
10406          */
10407         'alphanum' : function(v){
10408             return alphanum.test(v);
10409         },
10410         /**
10411          * The error text to display when the alphanumeric validation function returns false
10412          * @type String
10413          */
10414         'alphanumText' : 'This field should only contain letters, numbers and _',
10415         /**
10416          * The keystroke filter mask to be applied on alphanumeric input
10417          * @type RegExp
10418          */
10419         'alphanumMask' : /[a-z0-9_]/i
10420     };
10421 }();/*
10422  * - LGPL
10423  *
10424  * Input
10425  * 
10426  */
10427
10428 /**
10429  * @class Roo.bootstrap.Input
10430  * @extends Roo.bootstrap.Component
10431  * Bootstrap Input class
10432  * @cfg {Boolean} disabled is it disabled
10433  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10434  * @cfg {String} name name of the input
10435  * @cfg {string} fieldLabel - the label associated
10436  * @cfg {string} placeholder - placeholder to put in text.
10437  * @cfg {string}  before - input group add on before
10438  * @cfg {string} after - input group add on after
10439  * @cfg {string} size - (lg|sm) or leave empty..
10440  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10441  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10442  * @cfg {Number} md colspan out of 12 for computer-sized screens
10443  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10444  * @cfg {string} value default value of the input
10445  * @cfg {Number} labelWidth set the width of label 
10446  * @cfg {Number} labellg set the width of label (1-12)
10447  * @cfg {Number} labelmd set the width of label (1-12)
10448  * @cfg {Number} labelsm set the width of label (1-12)
10449  * @cfg {Number} labelxs set the width of label (1-12)
10450  * @cfg {String} labelAlign (top|left)
10451  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10452  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10453  * @cfg {String} indicatorpos (left|right) default left
10454  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10455  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10456  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10457
10458  * @cfg {String} align (left|center|right) Default left
10459  * @cfg {Boolean} forceFeedback (true|false) Default false
10460  * 
10461  * @constructor
10462  * Create a new Input
10463  * @param {Object} config The config object
10464  */
10465
10466 Roo.bootstrap.Input = function(config){
10467     
10468     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10469     
10470     this.addEvents({
10471         /**
10472          * @event focus
10473          * Fires when this field receives input focus.
10474          * @param {Roo.form.Field} this
10475          */
10476         focus : true,
10477         /**
10478          * @event blur
10479          * Fires when this field loses input focus.
10480          * @param {Roo.form.Field} this
10481          */
10482         blur : true,
10483         /**
10484          * @event specialkey
10485          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10486          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10487          * @param {Roo.form.Field} this
10488          * @param {Roo.EventObject} e The event object
10489          */
10490         specialkey : true,
10491         /**
10492          * @event change
10493          * Fires just before the field blurs if the field value has changed.
10494          * @param {Roo.form.Field} this
10495          * @param {Mixed} newValue The new value
10496          * @param {Mixed} oldValue The original value
10497          */
10498         change : true,
10499         /**
10500          * @event invalid
10501          * Fires after the field has been marked as invalid.
10502          * @param {Roo.form.Field} this
10503          * @param {String} msg The validation message
10504          */
10505         invalid : true,
10506         /**
10507          * @event valid
10508          * Fires after the field has been validated with no errors.
10509          * @param {Roo.form.Field} this
10510          */
10511         valid : true,
10512          /**
10513          * @event keyup
10514          * Fires after the key up
10515          * @param {Roo.form.Field} this
10516          * @param {Roo.EventObject}  e The event Object
10517          */
10518         keyup : true,
10519         /**
10520          * @event paste
10521          * Fires after the user pastes into input
10522          * @param {Roo.form.Field} this
10523          * @param {Roo.EventObject}  e The event Object
10524          */
10525         paste : true
10526     });
10527 };
10528
10529 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10530      /**
10531      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10532       automatic validation (defaults to "keyup").
10533      */
10534     validationEvent : "keyup",
10535      /**
10536      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10537      */
10538     validateOnBlur : true,
10539     /**
10540      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10541      */
10542     validationDelay : 250,
10543      /**
10544      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10545      */
10546     focusClass : "x-form-focus",  // not needed???
10547     
10548        
10549     /**
10550      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10551      */
10552     invalidClass : "has-warning",
10553     
10554     /**
10555      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10556      */
10557     validClass : "has-success",
10558     
10559     /**
10560      * @cfg {Boolean} hasFeedback (true|false) default true
10561      */
10562     hasFeedback : true,
10563     
10564     /**
10565      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10566      */
10567     invalidFeedbackClass : "glyphicon-warning-sign",
10568     
10569     /**
10570      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10571      */
10572     validFeedbackClass : "glyphicon-ok",
10573     
10574     /**
10575      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10576      */
10577     selectOnFocus : false,
10578     
10579      /**
10580      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10581      */
10582     maskRe : null,
10583        /**
10584      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10585      */
10586     vtype : null,
10587     
10588       /**
10589      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10590      */
10591     disableKeyFilter : false,
10592     
10593        /**
10594      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10595      */
10596     disabled : false,
10597      /**
10598      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10599      */
10600     allowBlank : true,
10601     /**
10602      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10603      */
10604     blankText : "Please complete this mandatory field",
10605     
10606      /**
10607      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10608      */
10609     minLength : 0,
10610     /**
10611      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10612      */
10613     maxLength : Number.MAX_VALUE,
10614     /**
10615      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10616      */
10617     minLengthText : "The minimum length for this field is {0}",
10618     /**
10619      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10620      */
10621     maxLengthText : "The maximum length for this field is {0}",
10622   
10623     
10624     /**
10625      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10626      * If available, this function will be called only after the basic validators all return true, and will be passed the
10627      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10628      */
10629     validator : null,
10630     /**
10631      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10632      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10633      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10634      */
10635     regex : null,
10636     /**
10637      * @cfg {String} regexText -- Depricated - use Invalid Text
10638      */
10639     regexText : "",
10640     
10641     /**
10642      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10643      */
10644     invalidText : "",
10645     
10646     
10647     
10648     autocomplete: false,
10649     
10650     
10651     fieldLabel : '',
10652     inputType : 'text',
10653     
10654     name : false,
10655     placeholder: false,
10656     before : false,
10657     after : false,
10658     size : false,
10659     hasFocus : false,
10660     preventMark: false,
10661     isFormField : true,
10662     value : '',
10663     labelWidth : 2,
10664     labelAlign : false,
10665     readOnly : false,
10666     align : false,
10667     formatedValue : false,
10668     forceFeedback : false,
10669     
10670     indicatorpos : 'left',
10671     
10672     labellg : 0,
10673     labelmd : 0,
10674     labelsm : 0,
10675     labelxs : 0,
10676     
10677     capture : '',
10678     accept : '',
10679     
10680     parentLabelAlign : function()
10681     {
10682         var parent = this;
10683         while (parent.parent()) {
10684             parent = parent.parent();
10685             if (typeof(parent.labelAlign) !='undefined') {
10686                 return parent.labelAlign;
10687             }
10688         }
10689         return 'left';
10690         
10691     },
10692     
10693     getAutoCreate : function()
10694     {
10695         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10696         
10697         var id = Roo.id();
10698         
10699         var cfg = {};
10700         
10701         if(this.inputType != 'hidden'){
10702             cfg.cls = 'form-group' //input-group
10703         }
10704         
10705         var input =  {
10706             tag: 'input',
10707             id : id,
10708             type : this.inputType,
10709             value : this.value,
10710             cls : 'form-control',
10711             placeholder : this.placeholder || '',
10712             autocomplete : this.autocomplete || 'new-password'
10713         };
10714         if (this.inputType == 'file') {
10715             input.style = 'overflow:hidden'; // why not in CSS?
10716         }
10717         
10718         if(this.capture.length){
10719             input.capture = this.capture;
10720         }
10721         
10722         if(this.accept.length){
10723             input.accept = this.accept + "/*";
10724         }
10725         
10726         if(this.align){
10727             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10728         }
10729         
10730         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10731             input.maxLength = this.maxLength;
10732         }
10733         
10734         if (this.disabled) {
10735             input.disabled=true;
10736         }
10737         
10738         if (this.readOnly) {
10739             input.readonly=true;
10740         }
10741         
10742         if (this.name) {
10743             input.name = this.name;
10744         }
10745         
10746         if (this.size) {
10747             input.cls += ' input-' + this.size;
10748         }
10749         
10750         var settings=this;
10751         ['xs','sm','md','lg'].map(function(size){
10752             if (settings[size]) {
10753                 cfg.cls += ' col-' + size + '-' + settings[size];
10754             }
10755         });
10756         
10757         var inputblock = input;
10758         
10759         var feedback = {
10760             tag: 'span',
10761             cls: 'glyphicon form-control-feedback'
10762         };
10763             
10764         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10765             
10766             inputblock = {
10767                 cls : 'has-feedback',
10768                 cn :  [
10769                     input,
10770                     feedback
10771                 ] 
10772             };  
10773         }
10774         
10775         if (this.before || this.after) {
10776             
10777             inputblock = {
10778                 cls : 'input-group',
10779                 cn :  [] 
10780             };
10781             
10782             if (this.before && typeof(this.before) == 'string') {
10783                 
10784                 inputblock.cn.push({
10785                     tag :'span',
10786                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10787                     html : this.before
10788                 });
10789             }
10790             if (this.before && typeof(this.before) == 'object') {
10791                 this.before = Roo.factory(this.before);
10792                 
10793                 inputblock.cn.push({
10794                     tag :'span',
10795                     cls : 'roo-input-before input-group-prepend   input-group-' +
10796                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10797                 });
10798             }
10799             
10800             inputblock.cn.push(input);
10801             
10802             if (this.after && typeof(this.after) == 'string') {
10803                 inputblock.cn.push({
10804                     tag :'span',
10805                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10806                     html : this.after
10807                 });
10808             }
10809             if (this.after && typeof(this.after) == 'object') {
10810                 this.after = Roo.factory(this.after);
10811                 
10812                 inputblock.cn.push({
10813                     tag :'span',
10814                     cls : 'roo-input-after input-group-append  input-group-' +
10815                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10816                 });
10817             }
10818             
10819             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10820                 inputblock.cls += ' has-feedback';
10821                 inputblock.cn.push(feedback);
10822             }
10823         };
10824         var indicator = {
10825             tag : 'i',
10826             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10827             tooltip : 'This field is required'
10828         };
10829         if (this.allowBlank ) {
10830             indicator.style = this.allowBlank ? ' display:none' : '';
10831         }
10832         if (align ==='left' && this.fieldLabel.length) {
10833             
10834             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10835             
10836             cfg.cn = [
10837                 indicator,
10838                 {
10839                     tag: 'label',
10840                     'for' :  id,
10841                     cls : 'control-label col-form-label',
10842                     html : this.fieldLabel
10843
10844                 },
10845                 {
10846                     cls : "", 
10847                     cn: [
10848                         inputblock
10849                     ]
10850                 }
10851             ];
10852             
10853             var labelCfg = cfg.cn[1];
10854             var contentCfg = cfg.cn[2];
10855             
10856             if(this.indicatorpos == 'right'){
10857                 cfg.cn = [
10858                     {
10859                         tag: 'label',
10860                         'for' :  id,
10861                         cls : 'control-label col-form-label',
10862                         cn : [
10863                             {
10864                                 tag : 'span',
10865                                 html : this.fieldLabel
10866                             },
10867                             indicator
10868                         ]
10869                     },
10870                     {
10871                         cls : "",
10872                         cn: [
10873                             inputblock
10874                         ]
10875                     }
10876
10877                 ];
10878                 
10879                 labelCfg = cfg.cn[0];
10880                 contentCfg = cfg.cn[1];
10881             
10882             }
10883             
10884             if(this.labelWidth > 12){
10885                 labelCfg.style = "width: " + this.labelWidth + 'px';
10886             }
10887             
10888             if(this.labelWidth < 13 && this.labelmd == 0){
10889                 this.labelmd = this.labelWidth;
10890             }
10891             
10892             if(this.labellg > 0){
10893                 labelCfg.cls += ' col-lg-' + this.labellg;
10894                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10895             }
10896             
10897             if(this.labelmd > 0){
10898                 labelCfg.cls += ' col-md-' + this.labelmd;
10899                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10900             }
10901             
10902             if(this.labelsm > 0){
10903                 labelCfg.cls += ' col-sm-' + this.labelsm;
10904                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10905             }
10906             
10907             if(this.labelxs > 0){
10908                 labelCfg.cls += ' col-xs-' + this.labelxs;
10909                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10910             }
10911             
10912             
10913         } else if ( this.fieldLabel.length) {
10914                 
10915             
10916             
10917             cfg.cn = [
10918                 {
10919                     tag : 'i',
10920                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10921                     tooltip : 'This field is required',
10922                     style : this.allowBlank ? ' display:none' : '' 
10923                 },
10924                 {
10925                     tag: 'label',
10926                    //cls : 'input-group-addon',
10927                     html : this.fieldLabel
10928
10929                 },
10930
10931                inputblock
10932
10933            ];
10934            
10935            if(this.indicatorpos == 'right'){
10936        
10937                 cfg.cn = [
10938                     {
10939                         tag: 'label',
10940                        //cls : 'input-group-addon',
10941                         html : this.fieldLabel
10942
10943                     },
10944                     {
10945                         tag : 'i',
10946                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10947                         tooltip : 'This field is required',
10948                         style : this.allowBlank ? ' display:none' : '' 
10949                     },
10950
10951                    inputblock
10952
10953                ];
10954
10955             }
10956
10957         } else {
10958             
10959             cfg.cn = [
10960
10961                     inputblock
10962
10963             ];
10964                 
10965                 
10966         };
10967         
10968         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10969            cfg.cls += ' navbar-form';
10970         }
10971         
10972         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10973             // on BS4 we do this only if not form 
10974             cfg.cls += ' navbar-form';
10975             cfg.tag = 'li';
10976         }
10977         
10978         return cfg;
10979         
10980     },
10981     /**
10982      * return the real input element.
10983      */
10984     inputEl: function ()
10985     {
10986         return this.el.select('input.form-control',true).first();
10987     },
10988     
10989     tooltipEl : function()
10990     {
10991         return this.inputEl();
10992     },
10993     
10994     indicatorEl : function()
10995     {
10996         if (Roo.bootstrap.version == 4) {
10997             return false; // not enabled in v4 yet.
10998         }
10999         
11000         var indicator = this.el.select('i.roo-required-indicator',true).first();
11001         
11002         if(!indicator){
11003             return false;
11004         }
11005         
11006         return indicator;
11007         
11008     },
11009     
11010     setDisabled : function(v)
11011     {
11012         var i  = this.inputEl().dom;
11013         if (!v) {
11014             i.removeAttribute('disabled');
11015             return;
11016             
11017         }
11018         i.setAttribute('disabled','true');
11019     },
11020     initEvents : function()
11021     {
11022           
11023         this.inputEl().on("keydown" , this.fireKey,  this);
11024         this.inputEl().on("focus", this.onFocus,  this);
11025         this.inputEl().on("blur", this.onBlur,  this);
11026         
11027         this.inputEl().relayEvent('keyup', this);
11028         this.inputEl().relayEvent('paste', this);
11029         
11030         this.indicator = this.indicatorEl();
11031         
11032         if(this.indicator){
11033             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11034         }
11035  
11036         // reference to original value for reset
11037         this.originalValue = this.getValue();
11038         //Roo.form.TextField.superclass.initEvents.call(this);
11039         if(this.validationEvent == 'keyup'){
11040             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11041             this.inputEl().on('keyup', this.filterValidation, this);
11042         }
11043         else if(this.validationEvent !== false){
11044             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11045         }
11046         
11047         if(this.selectOnFocus){
11048             this.on("focus", this.preFocus, this);
11049             
11050         }
11051         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11052             this.inputEl().on("keypress", this.filterKeys, this);
11053         } else {
11054             this.inputEl().relayEvent('keypress', this);
11055         }
11056        /* if(this.grow){
11057             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11058             this.el.on("click", this.autoSize,  this);
11059         }
11060         */
11061         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11062             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11063         }
11064         
11065         if (typeof(this.before) == 'object') {
11066             this.before.render(this.el.select('.roo-input-before',true).first());
11067         }
11068         if (typeof(this.after) == 'object') {
11069             this.after.render(this.el.select('.roo-input-after',true).first());
11070         }
11071         
11072         this.inputEl().on('change', this.onChange, this);
11073         
11074     },
11075     filterValidation : function(e){
11076         if(!e.isNavKeyPress()){
11077             this.validationTask.delay(this.validationDelay);
11078         }
11079     },
11080      /**
11081      * Validates the field value
11082      * @return {Boolean} True if the value is valid, else false
11083      */
11084     validate : function(){
11085         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11086         if(this.disabled || this.validateValue(this.getRawValue())){
11087             this.markValid();
11088             return true;
11089         }
11090         
11091         this.markInvalid();
11092         return false;
11093     },
11094     
11095     
11096     /**
11097      * Validates a value according to the field's validation rules and marks the field as invalid
11098      * if the validation fails
11099      * @param {Mixed} value The value to validate
11100      * @return {Boolean} True if the value is valid, else false
11101      */
11102     validateValue : function(value)
11103     {
11104         if(this.getVisibilityEl().hasClass('hidden')){
11105             return true;
11106         }
11107         
11108         if(value.length < 1)  { // if it's blank
11109             if(this.allowBlank){
11110                 return true;
11111             }
11112             return false;
11113         }
11114         
11115         if(value.length < this.minLength){
11116             return false;
11117         }
11118         if(value.length > this.maxLength){
11119             return false;
11120         }
11121         if(this.vtype){
11122             var vt = Roo.form.VTypes;
11123             if(!vt[this.vtype](value, this)){
11124                 return false;
11125             }
11126         }
11127         if(typeof this.validator == "function"){
11128             var msg = this.validator(value);
11129             if(msg !== true){
11130                 return false;
11131             }
11132             if (typeof(msg) == 'string') {
11133                 this.invalidText = msg;
11134             }
11135         }
11136         
11137         if(this.regex && !this.regex.test(value)){
11138             return false;
11139         }
11140         
11141         return true;
11142     },
11143     
11144      // private
11145     fireKey : function(e){
11146         //Roo.log('field ' + e.getKey());
11147         if(e.isNavKeyPress()){
11148             this.fireEvent("specialkey", this, e);
11149         }
11150     },
11151     focus : function (selectText){
11152         if(this.rendered){
11153             this.inputEl().focus();
11154             if(selectText === true){
11155                 this.inputEl().dom.select();
11156             }
11157         }
11158         return this;
11159     } ,
11160     
11161     onFocus : function(){
11162         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11163            // this.el.addClass(this.focusClass);
11164         }
11165         if(!this.hasFocus){
11166             this.hasFocus = true;
11167             this.startValue = this.getValue();
11168             this.fireEvent("focus", this);
11169         }
11170     },
11171     
11172     beforeBlur : Roo.emptyFn,
11173
11174     
11175     // private
11176     onBlur : function(){
11177         this.beforeBlur();
11178         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11179             //this.el.removeClass(this.focusClass);
11180         }
11181         this.hasFocus = false;
11182         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11183             this.validate();
11184         }
11185         var v = this.getValue();
11186         if(String(v) !== String(this.startValue)){
11187             this.fireEvent('change', this, v, this.startValue);
11188         }
11189         this.fireEvent("blur", this);
11190     },
11191     
11192     onChange : function(e)
11193     {
11194         var v = this.getValue();
11195         if(String(v) !== String(this.startValue)){
11196             this.fireEvent('change', this, v, this.startValue);
11197         }
11198         
11199     },
11200     
11201     /**
11202      * Resets the current field value to the originally loaded value and clears any validation messages
11203      */
11204     reset : function(){
11205         this.setValue(this.originalValue);
11206         this.validate();
11207     },
11208      /**
11209      * Returns the name of the field
11210      * @return {Mixed} name The name field
11211      */
11212     getName: function(){
11213         return this.name;
11214     },
11215      /**
11216      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11217      * @return {Mixed} value The field value
11218      */
11219     getValue : function(){
11220         
11221         var v = this.inputEl().getValue();
11222         
11223         return v;
11224     },
11225     /**
11226      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11227      * @return {Mixed} value The field value
11228      */
11229     getRawValue : function(){
11230         var v = this.inputEl().getValue();
11231         
11232         return v;
11233     },
11234     
11235     /**
11236      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11237      * @param {Mixed} value The value to set
11238      */
11239     setRawValue : function(v){
11240         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11241     },
11242     
11243     selectText : function(start, end){
11244         var v = this.getRawValue();
11245         if(v.length > 0){
11246             start = start === undefined ? 0 : start;
11247             end = end === undefined ? v.length : end;
11248             var d = this.inputEl().dom;
11249             if(d.setSelectionRange){
11250                 d.setSelectionRange(start, end);
11251             }else if(d.createTextRange){
11252                 var range = d.createTextRange();
11253                 range.moveStart("character", start);
11254                 range.moveEnd("character", v.length-end);
11255                 range.select();
11256             }
11257         }
11258     },
11259     
11260     /**
11261      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11262      * @param {Mixed} value The value to set
11263      */
11264     setValue : function(v){
11265         this.value = v;
11266         if(this.rendered){
11267             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11268             this.validate();
11269         }
11270     },
11271     
11272     /*
11273     processValue : function(value){
11274         if(this.stripCharsRe){
11275             var newValue = value.replace(this.stripCharsRe, '');
11276             if(newValue !== value){
11277                 this.setRawValue(newValue);
11278                 return newValue;
11279             }
11280         }
11281         return value;
11282     },
11283   */
11284     preFocus : function(){
11285         
11286         if(this.selectOnFocus){
11287             this.inputEl().dom.select();
11288         }
11289     },
11290     filterKeys : function(e){
11291         var k = e.getKey();
11292         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11293             return;
11294         }
11295         var c = e.getCharCode(), cc = String.fromCharCode(c);
11296         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11297             return;
11298         }
11299         if(!this.maskRe.test(cc)){
11300             e.stopEvent();
11301         }
11302     },
11303      /**
11304      * Clear any invalid styles/messages for this field
11305      */
11306     clearInvalid : function(){
11307         
11308         if(!this.el || this.preventMark){ // not rendered
11309             return;
11310         }
11311         
11312         
11313         this.el.removeClass([this.invalidClass, 'is-invalid']);
11314         
11315         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11316             
11317             var feedback = this.el.select('.form-control-feedback', true).first();
11318             
11319             if(feedback){
11320                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11321             }
11322             
11323         }
11324         
11325         if(this.indicator){
11326             this.indicator.removeClass('visible');
11327             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11328         }
11329         
11330         this.fireEvent('valid', this);
11331     },
11332     
11333      /**
11334      * Mark this field as valid
11335      */
11336     markValid : function()
11337     {
11338         if(!this.el  || this.preventMark){ // not rendered...
11339             return;
11340         }
11341         
11342         this.el.removeClass([this.invalidClass, this.validClass]);
11343         this.inputEl().removeClass(['is-valid', 'is-invalid']);
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         }
11350         
11351         if(this.indicator){
11352             this.indicator.removeClass('visible');
11353             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11354         }
11355         
11356         if(this.disabled){
11357             return;
11358         }
11359         
11360            
11361         if(this.allowBlank && !this.getRawValue().length){
11362             return;
11363         }
11364         if (Roo.bootstrap.version == 3) {
11365             this.el.addClass(this.validClass);
11366         } else {
11367             this.inputEl().addClass('is-valid');
11368         }
11369
11370         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11371             
11372             var feedback = this.el.select('.form-control-feedback', true).first();
11373             
11374             if(feedback){
11375                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11376                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11377             }
11378             
11379         }
11380         
11381         this.fireEvent('valid', this);
11382     },
11383     
11384      /**
11385      * Mark this field as invalid
11386      * @param {String} msg The validation message
11387      */
11388     markInvalid : function(msg)
11389     {
11390         if(!this.el  || this.preventMark){ // not rendered
11391             return;
11392         }
11393         
11394         this.el.removeClass([this.invalidClass, this.validClass]);
11395         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11396         
11397         var feedback = this.el.select('.form-control-feedback', true).first();
11398             
11399         if(feedback){
11400             this.el.select('.form-control-feedback', true).first().removeClass(
11401                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11402         }
11403
11404         if(this.disabled){
11405             return;
11406         }
11407         
11408         if(this.allowBlank && !this.getRawValue().length){
11409             return;
11410         }
11411         
11412         if(this.indicator){
11413             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11414             this.indicator.addClass('visible');
11415         }
11416         if (Roo.bootstrap.version == 3) {
11417             this.el.addClass(this.invalidClass);
11418         } else {
11419             this.inputEl().addClass('is-invalid');
11420         }
11421         
11422         
11423         
11424         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11425             
11426             var feedback = this.el.select('.form-control-feedback', true).first();
11427             
11428             if(feedback){
11429                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11430                 
11431                 if(this.getValue().length || this.forceFeedback){
11432                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11433                 }
11434                 
11435             }
11436             
11437         }
11438         
11439         this.fireEvent('invalid', this, msg);
11440     },
11441     // private
11442     SafariOnKeyDown : function(event)
11443     {
11444         // this is a workaround for a password hang bug on chrome/ webkit.
11445         if (this.inputEl().dom.type != 'password') {
11446             return;
11447         }
11448         
11449         var isSelectAll = false;
11450         
11451         if(this.inputEl().dom.selectionEnd > 0){
11452             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11453         }
11454         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11455             event.preventDefault();
11456             this.setValue('');
11457             return;
11458         }
11459         
11460         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11461             
11462             event.preventDefault();
11463             // this is very hacky as keydown always get's upper case.
11464             //
11465             var cc = String.fromCharCode(event.getCharCode());
11466             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11467             
11468         }
11469     },
11470     adjustWidth : function(tag, w){
11471         tag = tag.toLowerCase();
11472         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11473             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11474                 if(tag == 'input'){
11475                     return w + 2;
11476                 }
11477                 if(tag == 'textarea'){
11478                     return w-2;
11479                 }
11480             }else if(Roo.isOpera){
11481                 if(tag == 'input'){
11482                     return w + 2;
11483                 }
11484                 if(tag == 'textarea'){
11485                     return w-2;
11486                 }
11487             }
11488         }
11489         return w;
11490     },
11491     
11492     setFieldLabel : function(v)
11493     {
11494         if(!this.rendered){
11495             return;
11496         }
11497         
11498         if(this.indicatorEl()){
11499             var ar = this.el.select('label > span',true);
11500             
11501             if (ar.elements.length) {
11502                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11503                 this.fieldLabel = v;
11504                 return;
11505             }
11506             
11507             var br = this.el.select('label',true);
11508             
11509             if(br.elements.length) {
11510                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11511                 this.fieldLabel = v;
11512                 return;
11513             }
11514             
11515             Roo.log('Cannot Found any of label > span || label in input');
11516             return;
11517         }
11518         
11519         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11520         this.fieldLabel = v;
11521         
11522         
11523     }
11524 });
11525
11526  
11527 /*
11528  * - LGPL
11529  *
11530  * Input
11531  * 
11532  */
11533
11534 /**
11535  * @class Roo.bootstrap.TextArea
11536  * @extends Roo.bootstrap.Input
11537  * Bootstrap TextArea class
11538  * @cfg {Number} cols Specifies the visible width of a text area
11539  * @cfg {Number} rows Specifies the visible number of lines in a text area
11540  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11541  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11542  * @cfg {string} html text
11543  * 
11544  * @constructor
11545  * Create a new TextArea
11546  * @param {Object} config The config object
11547  */
11548
11549 Roo.bootstrap.TextArea = function(config){
11550     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11551    
11552 };
11553
11554 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11555      
11556     cols : false,
11557     rows : 5,
11558     readOnly : false,
11559     warp : 'soft',
11560     resize : false,
11561     value: false,
11562     html: false,
11563     
11564     getAutoCreate : function(){
11565         
11566         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11567         
11568         var id = Roo.id();
11569         
11570         var cfg = {};
11571         
11572         if(this.inputType != 'hidden'){
11573             cfg.cls = 'form-group' //input-group
11574         }
11575         
11576         var input =  {
11577             tag: 'textarea',
11578             id : id,
11579             warp : this.warp,
11580             rows : this.rows,
11581             value : this.value || '',
11582             html: this.html || '',
11583             cls : 'form-control',
11584             placeholder : this.placeholder || '' 
11585             
11586         };
11587         
11588         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11589             input.maxLength = this.maxLength;
11590         }
11591         
11592         if(this.resize){
11593             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11594         }
11595         
11596         if(this.cols){
11597             input.cols = this.cols;
11598         }
11599         
11600         if (this.readOnly) {
11601             input.readonly = true;
11602         }
11603         
11604         if (this.name) {
11605             input.name = this.name;
11606         }
11607         
11608         if (this.size) {
11609             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11610         }
11611         
11612         var settings=this;
11613         ['xs','sm','md','lg'].map(function(size){
11614             if (settings[size]) {
11615                 cfg.cls += ' col-' + size + '-' + settings[size];
11616             }
11617         });
11618         
11619         var inputblock = input;
11620         
11621         if(this.hasFeedback && !this.allowBlank){
11622             
11623             var feedback = {
11624                 tag: 'span',
11625                 cls: 'glyphicon form-control-feedback'
11626             };
11627
11628             inputblock = {
11629                 cls : 'has-feedback',
11630                 cn :  [
11631                     input,
11632                     feedback
11633                 ] 
11634             };  
11635         }
11636         
11637         
11638         if (this.before || this.after) {
11639             
11640             inputblock = {
11641                 cls : 'input-group',
11642                 cn :  [] 
11643             };
11644             if (this.before) {
11645                 inputblock.cn.push({
11646                     tag :'span',
11647                     cls : 'input-group-addon',
11648                     html : this.before
11649                 });
11650             }
11651             
11652             inputblock.cn.push(input);
11653             
11654             if(this.hasFeedback && !this.allowBlank){
11655                 inputblock.cls += ' has-feedback';
11656                 inputblock.cn.push(feedback);
11657             }
11658             
11659             if (this.after) {
11660                 inputblock.cn.push({
11661                     tag :'span',
11662                     cls : 'input-group-addon',
11663                     html : this.after
11664                 });
11665             }
11666             
11667         }
11668         
11669         if (align ==='left' && this.fieldLabel.length) {
11670             cfg.cn = [
11671                 {
11672                     tag: 'label',
11673                     'for' :  id,
11674                     cls : 'control-label',
11675                     html : this.fieldLabel
11676                 },
11677                 {
11678                     cls : "",
11679                     cn: [
11680                         inputblock
11681                     ]
11682                 }
11683
11684             ];
11685             
11686             if(this.labelWidth > 12){
11687                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11688             }
11689
11690             if(this.labelWidth < 13 && this.labelmd == 0){
11691                 this.labelmd = this.labelWidth;
11692             }
11693
11694             if(this.labellg > 0){
11695                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11696                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11697             }
11698
11699             if(this.labelmd > 0){
11700                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11701                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11702             }
11703
11704             if(this.labelsm > 0){
11705                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11706                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11707             }
11708
11709             if(this.labelxs > 0){
11710                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11711                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11712             }
11713             
11714         } else if ( this.fieldLabel.length) {
11715             cfg.cn = [
11716
11717                {
11718                    tag: 'label',
11719                    //cls : 'input-group-addon',
11720                    html : this.fieldLabel
11721
11722                },
11723
11724                inputblock
11725
11726            ];
11727
11728         } else {
11729
11730             cfg.cn = [
11731
11732                 inputblock
11733
11734             ];
11735                 
11736         }
11737         
11738         if (this.disabled) {
11739             input.disabled=true;
11740         }
11741         
11742         return cfg;
11743         
11744     },
11745     /**
11746      * return the real textarea element.
11747      */
11748     inputEl: function ()
11749     {
11750         return this.el.select('textarea.form-control',true).first();
11751     },
11752     
11753     /**
11754      * Clear any invalid styles/messages for this field
11755      */
11756     clearInvalid : function()
11757     {
11758         
11759         if(!this.el || this.preventMark){ // not rendered
11760             return;
11761         }
11762         
11763         var label = this.el.select('label', true).first();
11764         var icon = this.el.select('i.fa-star', true).first();
11765         
11766         if(label && icon){
11767             icon.remove();
11768         }
11769         this.el.removeClass( this.validClass);
11770         this.inputEl().removeClass('is-invalid');
11771          
11772         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11773             
11774             var feedback = this.el.select('.form-control-feedback', true).first();
11775             
11776             if(feedback){
11777                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11778             }
11779             
11780         }
11781         
11782         this.fireEvent('valid', this);
11783     },
11784     
11785      /**
11786      * Mark this field as valid
11787      */
11788     markValid : function()
11789     {
11790         if(!this.el  || this.preventMark){ // not rendered
11791             return;
11792         }
11793         
11794         this.el.removeClass([this.invalidClass, this.validClass]);
11795         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11796         
11797         var feedback = this.el.select('.form-control-feedback', true).first();
11798             
11799         if(feedback){
11800             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11801         }
11802
11803         if(this.disabled || this.allowBlank){
11804             return;
11805         }
11806         
11807         var label = this.el.select('label', true).first();
11808         var icon = this.el.select('i.fa-star', true).first();
11809         
11810         if(label && icon){
11811             icon.remove();
11812         }
11813         if (Roo.bootstrap.version == 3) {
11814             this.el.addClass(this.validClass);
11815         } else {
11816             this.inputEl().addClass('is-valid');
11817         }
11818         
11819         
11820         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11821             
11822             var feedback = this.el.select('.form-control-feedback', true).first();
11823             
11824             if(feedback){
11825                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11826                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11827             }
11828             
11829         }
11830         
11831         this.fireEvent('valid', this);
11832     },
11833     
11834      /**
11835      * Mark this field as invalid
11836      * @param {String} msg The validation message
11837      */
11838     markInvalid : function(msg)
11839     {
11840         if(!this.el  || this.preventMark){ // not rendered
11841             return;
11842         }
11843         
11844         this.el.removeClass([this.invalidClass, this.validClass]);
11845         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11846         
11847         var feedback = this.el.select('.form-control-feedback', true).first();
11848             
11849         if(feedback){
11850             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11851         }
11852
11853         if(this.disabled || this.allowBlank){
11854             return;
11855         }
11856         
11857         var label = this.el.select('label', true).first();
11858         var icon = this.el.select('i.fa-star', true).first();
11859         
11860         if(!this.getValue().length && label && !icon){
11861             this.el.createChild({
11862                 tag : 'i',
11863                 cls : 'text-danger fa fa-lg fa-star',
11864                 tooltip : 'This field is required',
11865                 style : 'margin-right:5px;'
11866             }, label, true);
11867         }
11868         
11869         if (Roo.bootstrap.version == 3) {
11870             this.el.addClass(this.invalidClass);
11871         } else {
11872             this.inputEl().addClass('is-invalid');
11873         }
11874         
11875         // fixme ... this may be depricated need to test..
11876         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11877             
11878             var feedback = this.el.select('.form-control-feedback', true).first();
11879             
11880             if(feedback){
11881                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11882                 
11883                 if(this.getValue().length || this.forceFeedback){
11884                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11885                 }
11886                 
11887             }
11888             
11889         }
11890         
11891         this.fireEvent('invalid', this, msg);
11892     }
11893 });
11894
11895  
11896 /*
11897  * - LGPL
11898  *
11899  * trigger field - base class for combo..
11900  * 
11901  */
11902  
11903 /**
11904  * @class Roo.bootstrap.TriggerField
11905  * @extends Roo.bootstrap.Input
11906  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11907  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11908  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11909  * for which you can provide a custom implementation.  For example:
11910  * <pre><code>
11911 var trigger = new Roo.bootstrap.TriggerField();
11912 trigger.onTriggerClick = myTriggerFn;
11913 trigger.applyTo('my-field');
11914 </code></pre>
11915  *
11916  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11917  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11918  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11919  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11920  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11921
11922  * @constructor
11923  * Create a new TriggerField.
11924  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11925  * to the base TextField)
11926  */
11927 Roo.bootstrap.TriggerField = function(config){
11928     this.mimicing = false;
11929     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11930 };
11931
11932 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11933     /**
11934      * @cfg {String} triggerClass A CSS class to apply to the trigger
11935      */
11936      /**
11937      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11938      */
11939     hideTrigger:false,
11940
11941     /**
11942      * @cfg {Boolean} removable (true|false) special filter default false
11943      */
11944     removable : false,
11945     
11946     /** @cfg {Boolean} grow @hide */
11947     /** @cfg {Number} growMin @hide */
11948     /** @cfg {Number} growMax @hide */
11949
11950     /**
11951      * @hide 
11952      * @method
11953      */
11954     autoSize: Roo.emptyFn,
11955     // private
11956     monitorTab : true,
11957     // private
11958     deferHeight : true,
11959
11960     
11961     actionMode : 'wrap',
11962     
11963     caret : false,
11964     
11965     
11966     getAutoCreate : function(){
11967        
11968         var align = this.labelAlign || this.parentLabelAlign();
11969         
11970         var id = Roo.id();
11971         
11972         var cfg = {
11973             cls: 'form-group' //input-group
11974         };
11975         
11976         
11977         var input =  {
11978             tag: 'input',
11979             id : id,
11980             type : this.inputType,
11981             cls : 'form-control',
11982             autocomplete: 'new-password',
11983             placeholder : this.placeholder || '' 
11984             
11985         };
11986         if (this.name) {
11987             input.name = this.name;
11988         }
11989         if (this.size) {
11990             input.cls += ' input-' + this.size;
11991         }
11992         
11993         if (this.disabled) {
11994             input.disabled=true;
11995         }
11996         
11997         var inputblock = input;
11998         
11999         if(this.hasFeedback && !this.allowBlank){
12000             
12001             var feedback = {
12002                 tag: 'span',
12003                 cls: 'glyphicon form-control-feedback'
12004             };
12005             
12006             if(this.removable && !this.editable  ){
12007                 inputblock = {
12008                     cls : 'has-feedback',
12009                     cn :  [
12010                         inputblock,
12011                         {
12012                             tag: 'button',
12013                             html : 'x',
12014                             cls : 'roo-combo-removable-btn close'
12015                         },
12016                         feedback
12017                     ] 
12018                 };
12019             } else {
12020                 inputblock = {
12021                     cls : 'has-feedback',
12022                     cn :  [
12023                         inputblock,
12024                         feedback
12025                     ] 
12026                 };
12027             }
12028
12029         } else {
12030             if(this.removable && !this.editable ){
12031                 inputblock = {
12032                     cls : 'roo-removable',
12033                     cn :  [
12034                         inputblock,
12035                         {
12036                             tag: 'button',
12037                             html : 'x',
12038                             cls : 'roo-combo-removable-btn close'
12039                         }
12040                     ] 
12041                 };
12042             }
12043         }
12044         
12045         if (this.before || this.after) {
12046             
12047             inputblock = {
12048                 cls : 'input-group',
12049                 cn :  [] 
12050             };
12051             if (this.before) {
12052                 inputblock.cn.push({
12053                     tag :'span',
12054                     cls : 'input-group-addon input-group-prepend input-group-text',
12055                     html : this.before
12056                 });
12057             }
12058             
12059             inputblock.cn.push(input);
12060             
12061             if(this.hasFeedback && !this.allowBlank){
12062                 inputblock.cls += ' has-feedback';
12063                 inputblock.cn.push(feedback);
12064             }
12065             
12066             if (this.after) {
12067                 inputblock.cn.push({
12068                     tag :'span',
12069                     cls : 'input-group-addon input-group-append input-group-text',
12070                     html : this.after
12071                 });
12072             }
12073             
12074         };
12075         
12076       
12077         
12078         var ibwrap = inputblock;
12079         
12080         if(this.multiple){
12081             ibwrap = {
12082                 tag: 'ul',
12083                 cls: 'roo-select2-choices',
12084                 cn:[
12085                     {
12086                         tag: 'li',
12087                         cls: 'roo-select2-search-field',
12088                         cn: [
12089
12090                             inputblock
12091                         ]
12092                     }
12093                 ]
12094             };
12095                 
12096         }
12097         
12098         var combobox = {
12099             cls: 'roo-select2-container input-group',
12100             cn: [
12101                  {
12102                     tag: 'input',
12103                     type : 'hidden',
12104                     cls: 'form-hidden-field'
12105                 },
12106                 ibwrap
12107             ]
12108         };
12109         
12110         if(!this.multiple && this.showToggleBtn){
12111             
12112             var caret = {
12113                         tag: 'span',
12114                         cls: 'caret'
12115              };
12116             if (this.caret != false) {
12117                 caret = {
12118                      tag: 'i',
12119                      cls: 'fa fa-' + this.caret
12120                 };
12121                 
12122             }
12123             
12124             combobox.cn.push({
12125                 tag :'span',
12126                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12127                 cn : [
12128                     Roo.bootstrap.version == 3 ? caret : '',
12129                     {
12130                         tag: 'span',
12131                         cls: 'combobox-clear',
12132                         cn  : [
12133                             {
12134                                 tag : 'i',
12135                                 cls: 'icon-remove'
12136                             }
12137                         ]
12138                     }
12139                 ]
12140
12141             })
12142         }
12143         
12144         if(this.multiple){
12145             combobox.cls += ' roo-select2-container-multi';
12146         }
12147          var indicator = {
12148             tag : 'i',
12149             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12150             tooltip : 'This field is required'
12151         };
12152         if (Roo.bootstrap.version == 4) {
12153             indicator = {
12154                 tag : 'i',
12155                 style : 'display:none'
12156             };
12157         }
12158         
12159         
12160         if (align ==='left' && this.fieldLabel.length) {
12161             
12162             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12163
12164             cfg.cn = [
12165                 indicator,
12166                 {
12167                     tag: 'label',
12168                     'for' :  id,
12169                     cls : 'control-label',
12170                     html : this.fieldLabel
12171
12172                 },
12173                 {
12174                     cls : "", 
12175                     cn: [
12176                         combobox
12177                     ]
12178                 }
12179
12180             ];
12181             
12182             var labelCfg = cfg.cn[1];
12183             var contentCfg = cfg.cn[2];
12184             
12185             if(this.indicatorpos == 'right'){
12186                 cfg.cn = [
12187                     {
12188                         tag: 'label',
12189                         'for' :  id,
12190                         cls : 'control-label',
12191                         cn : [
12192                             {
12193                                 tag : 'span',
12194                                 html : this.fieldLabel
12195                             },
12196                             indicator
12197                         ]
12198                     },
12199                     {
12200                         cls : "", 
12201                         cn: [
12202                             combobox
12203                         ]
12204                     }
12205
12206                 ];
12207                 
12208                 labelCfg = cfg.cn[0];
12209                 contentCfg = cfg.cn[1];
12210             }
12211             
12212             if(this.labelWidth > 12){
12213                 labelCfg.style = "width: " + this.labelWidth + 'px';
12214             }
12215             
12216             if(this.labelWidth < 13 && this.labelmd == 0){
12217                 this.labelmd = this.labelWidth;
12218             }
12219             
12220             if(this.labellg > 0){
12221                 labelCfg.cls += ' col-lg-' + this.labellg;
12222                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12223             }
12224             
12225             if(this.labelmd > 0){
12226                 labelCfg.cls += ' col-md-' + this.labelmd;
12227                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12228             }
12229             
12230             if(this.labelsm > 0){
12231                 labelCfg.cls += ' col-sm-' + this.labelsm;
12232                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12233             }
12234             
12235             if(this.labelxs > 0){
12236                 labelCfg.cls += ' col-xs-' + this.labelxs;
12237                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12238             }
12239             
12240         } else if ( this.fieldLabel.length) {
12241 //                Roo.log(" label");
12242             cfg.cn = [
12243                 indicator,
12244                {
12245                    tag: 'label',
12246                    //cls : 'input-group-addon',
12247                    html : this.fieldLabel
12248
12249                },
12250
12251                combobox
12252
12253             ];
12254             
12255             if(this.indicatorpos == 'right'){
12256                 
12257                 cfg.cn = [
12258                     {
12259                        tag: 'label',
12260                        cn : [
12261                            {
12262                                tag : 'span',
12263                                html : this.fieldLabel
12264                            },
12265                            indicator
12266                        ]
12267
12268                     },
12269                     combobox
12270
12271                 ];
12272
12273             }
12274
12275         } else {
12276             
12277 //                Roo.log(" no label && no align");
12278                 cfg = combobox
12279                      
12280                 
12281         }
12282         
12283         var settings=this;
12284         ['xs','sm','md','lg'].map(function(size){
12285             if (settings[size]) {
12286                 cfg.cls += ' col-' + size + '-' + settings[size];
12287             }
12288         });
12289         
12290         return cfg;
12291         
12292     },
12293     
12294     
12295     
12296     // private
12297     onResize : function(w, h){
12298 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12299 //        if(typeof w == 'number'){
12300 //            var x = w - this.trigger.getWidth();
12301 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12302 //            this.trigger.setStyle('left', x+'px');
12303 //        }
12304     },
12305
12306     // private
12307     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12308
12309     // private
12310     getResizeEl : function(){
12311         return this.inputEl();
12312     },
12313
12314     // private
12315     getPositionEl : function(){
12316         return this.inputEl();
12317     },
12318
12319     // private
12320     alignErrorIcon : function(){
12321         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12322     },
12323
12324     // private
12325     initEvents : function(){
12326         
12327         this.createList();
12328         
12329         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12330         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12331         if(!this.multiple && this.showToggleBtn){
12332             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12333             if(this.hideTrigger){
12334                 this.trigger.setDisplayed(false);
12335             }
12336             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12337         }
12338         
12339         if(this.multiple){
12340             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12341         }
12342         
12343         if(this.removable && !this.editable && !this.tickable){
12344             var close = this.closeTriggerEl();
12345             
12346             if(close){
12347                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12348                 close.on('click', this.removeBtnClick, this, close);
12349             }
12350         }
12351         
12352         //this.trigger.addClassOnOver('x-form-trigger-over');
12353         //this.trigger.addClassOnClick('x-form-trigger-click');
12354         
12355         //if(!this.width){
12356         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12357         //}
12358     },
12359     
12360     closeTriggerEl : function()
12361     {
12362         var close = this.el.select('.roo-combo-removable-btn', true).first();
12363         return close ? close : false;
12364     },
12365     
12366     removeBtnClick : function(e, h, el)
12367     {
12368         e.preventDefault();
12369         
12370         if(this.fireEvent("remove", this) !== false){
12371             this.reset();
12372             this.fireEvent("afterremove", this)
12373         }
12374     },
12375     
12376     createList : function()
12377     {
12378         this.list = Roo.get(document.body).createChild({
12379             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12380             cls: 'typeahead typeahead-long dropdown-menu shadow',
12381             style: 'display:none'
12382         });
12383         
12384         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12385         
12386     },
12387
12388     // private
12389     initTrigger : function(){
12390        
12391     },
12392
12393     // private
12394     onDestroy : function(){
12395         if(this.trigger){
12396             this.trigger.removeAllListeners();
12397           //  this.trigger.remove();
12398         }
12399         //if(this.wrap){
12400         //    this.wrap.remove();
12401         //}
12402         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12403     },
12404
12405     // private
12406     onFocus : function(){
12407         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12408         /*
12409         if(!this.mimicing){
12410             this.wrap.addClass('x-trigger-wrap-focus');
12411             this.mimicing = true;
12412             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12413             if(this.monitorTab){
12414                 this.el.on("keydown", this.checkTab, this);
12415             }
12416         }
12417         */
12418     },
12419
12420     // private
12421     checkTab : function(e){
12422         if(e.getKey() == e.TAB){
12423             this.triggerBlur();
12424         }
12425     },
12426
12427     // private
12428     onBlur : function(){
12429         // do nothing
12430     },
12431
12432     // private
12433     mimicBlur : function(e, t){
12434         /*
12435         if(!this.wrap.contains(t) && this.validateBlur()){
12436             this.triggerBlur();
12437         }
12438         */
12439     },
12440
12441     // private
12442     triggerBlur : function(){
12443         this.mimicing = false;
12444         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12445         if(this.monitorTab){
12446             this.el.un("keydown", this.checkTab, this);
12447         }
12448         //this.wrap.removeClass('x-trigger-wrap-focus');
12449         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12450     },
12451
12452     // private
12453     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12454     validateBlur : function(e, t){
12455         return true;
12456     },
12457
12458     // private
12459     onDisable : function(){
12460         this.inputEl().dom.disabled = true;
12461         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12462         //if(this.wrap){
12463         //    this.wrap.addClass('x-item-disabled');
12464         //}
12465     },
12466
12467     // private
12468     onEnable : function(){
12469         this.inputEl().dom.disabled = false;
12470         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12471         //if(this.wrap){
12472         //    this.el.removeClass('x-item-disabled');
12473         //}
12474     },
12475
12476     // private
12477     onShow : function(){
12478         var ae = this.getActionEl();
12479         
12480         if(ae){
12481             ae.dom.style.display = '';
12482             ae.dom.style.visibility = 'visible';
12483         }
12484     },
12485
12486     // private
12487     
12488     onHide : function(){
12489         var ae = this.getActionEl();
12490         ae.dom.style.display = 'none';
12491     },
12492
12493     /**
12494      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12495      * by an implementing function.
12496      * @method
12497      * @param {EventObject} e
12498      */
12499     onTriggerClick : Roo.emptyFn
12500 });
12501  
12502 /*
12503 * Licence: LGPL
12504 */
12505
12506 /**
12507  * @class Roo.bootstrap.CardUploader
12508  * @extends Roo.bootstrap.Button
12509  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12510  * @cfg {Number} errorTimeout default 3000
12511  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12512  * @cfg {Array}  html The button text.
12513
12514  *
12515  * @constructor
12516  * Create a new CardUploader
12517  * @param {Object} config The config object
12518  */
12519
12520 Roo.bootstrap.CardUploader = function(config){
12521     
12522  
12523     
12524     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12525     
12526     
12527     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12528         return r.data.id
12529      });
12530     
12531      this.addEvents({
12532          // raw events
12533         /**
12534          * @event preview
12535          * When a image is clicked on - and needs to display a slideshow or similar..
12536          * @param {Roo.bootstrap.Card} this
12537          * @param {Object} The image information data 
12538          *
12539          */
12540         'preview' : true,
12541          /**
12542          * @event download
12543          * When a the download link is clicked
12544          * @param {Roo.bootstrap.Card} this
12545          * @param {Object} The image information data  contains 
12546          */
12547         'download' : true
12548         
12549     });
12550 };
12551  
12552 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12553     
12554      
12555     errorTimeout : 3000,
12556      
12557     images : false,
12558    
12559     fileCollection : false,
12560     allowBlank : true,
12561     
12562     getAutoCreate : function()
12563     {
12564         
12565         var cfg =  {
12566             cls :'form-group' ,
12567             cn : [
12568                
12569                 {
12570                     tag: 'label',
12571                    //cls : 'input-group-addon',
12572                     html : this.fieldLabel
12573
12574                 },
12575
12576                 {
12577                     tag: 'input',
12578                     type : 'hidden',
12579                     name : this.name,
12580                     value : this.value,
12581                     cls : 'd-none  form-control'
12582                 },
12583                 
12584                 {
12585                     tag: 'input',
12586                     multiple : 'multiple',
12587                     type : 'file',
12588                     cls : 'd-none  roo-card-upload-selector'
12589                 },
12590                 
12591                 {
12592                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12593                 },
12594                 {
12595                     cls : 'card-columns roo-card-uploader-container'
12596                 }
12597
12598             ]
12599         };
12600            
12601          
12602         return cfg;
12603     },
12604     
12605     getChildContainer : function() /// what children are added to.
12606     {
12607         return this.containerEl;
12608     },
12609    
12610     getButtonContainer : function() /// what children are added to.
12611     {
12612         return this.el.select(".roo-card-uploader-button-container").first();
12613     },
12614    
12615     initEvents : function()
12616     {
12617         
12618         Roo.bootstrap.Input.prototype.initEvents.call(this);
12619         
12620         var t = this;
12621         this.addxtype({
12622             xns: Roo.bootstrap,
12623
12624             xtype : 'Button',
12625             container_method : 'getButtonContainer' ,            
12626             html :  this.html, // fix changable?
12627             cls : 'w-100 ',
12628             listeners : {
12629                 'click' : function(btn, e) {
12630                     t.onClick(e);
12631                 }
12632             }
12633         });
12634         
12635         
12636         
12637         
12638         this.urlAPI = (window.createObjectURL && window) || 
12639                                 (window.URL && URL.revokeObjectURL && URL) || 
12640                                 (window.webkitURL && webkitURL);
12641                         
12642          
12643          
12644          
12645         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12646         
12647         this.selectorEl.on('change', this.onFileSelected, this);
12648         if (this.images) {
12649             var t = this;
12650             this.images.forEach(function(img) {
12651                 t.addCard(img)
12652             });
12653             this.images = false;
12654         }
12655         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12656          
12657        
12658     },
12659     
12660    
12661     onClick : function(e)
12662     {
12663         e.preventDefault();
12664          
12665         this.selectorEl.dom.click();
12666          
12667     },
12668     
12669     onFileSelected : function(e)
12670     {
12671         e.preventDefault();
12672         
12673         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12674             return;
12675         }
12676         
12677         Roo.each(this.selectorEl.dom.files, function(file){    
12678             this.addFile(file);
12679         }, this);
12680          
12681     },
12682     
12683       
12684     
12685       
12686     
12687     addFile : function(file)
12688     {
12689            
12690         if(typeof(file) === 'string'){
12691             throw "Add file by name?"; // should not happen
12692             return;
12693         }
12694         
12695         if(!file || !this.urlAPI){
12696             return;
12697         }
12698         
12699         // file;
12700         // file.type;
12701         
12702         var _this = this;
12703         
12704         
12705         var url = _this.urlAPI.createObjectURL( file);
12706            
12707         this.addCard({
12708             id : Roo.bootstrap.CardUploader.ID--,
12709             is_uploaded : false,
12710             src : url,
12711             srcfile : file,
12712             title : file.name,
12713             mimetype : file.type,
12714             preview : false,
12715             is_deleted : 0
12716         });
12717         
12718     },
12719     
12720     /**
12721      * addCard - add an Attachment to the uploader
12722      * @param data - the data about the image to upload
12723      *
12724      * {
12725           id : 123
12726           title : "Title of file",
12727           is_uploaded : false,
12728           src : "http://.....",
12729           srcfile : { the File upload object },
12730           mimetype : file.type,
12731           preview : false,
12732           is_deleted : 0
12733           .. any other data...
12734         }
12735      *
12736      * 
12737     */
12738     
12739     addCard : function (data)
12740     {
12741         // hidden input element?
12742         // if the file is not an image...
12743         //then we need to use something other that and header_image
12744         var t = this;
12745         //   remove.....
12746         var footer = [
12747             {
12748                 xns : Roo.bootstrap,
12749                 xtype : 'CardFooter',
12750                  items: [
12751                     {
12752                         xns : Roo.bootstrap,
12753                         xtype : 'Element',
12754                         cls : 'd-flex',
12755                         items : [
12756                             
12757                             {
12758                                 xns : Roo.bootstrap,
12759                                 xtype : 'Button',
12760                                 html : String.format("<small>{0}</small>", data.title),
12761                                 cls : 'col-10 text-left',
12762                                 size: 'sm',
12763                                 weight: 'link',
12764                                 fa : 'download',
12765                                 listeners : {
12766                                     click : function() {
12767                                      
12768                                         t.fireEvent( "download", t, data );
12769                                     }
12770                                 }
12771                             },
12772                           
12773                             {
12774                                 xns : Roo.bootstrap,
12775                                 xtype : 'Button',
12776                                 style: 'max-height: 28px; ',
12777                                 size : 'sm',
12778                                 weight: 'danger',
12779                                 cls : 'col-2',
12780                                 fa : 'times',
12781                                 listeners : {
12782                                     click : function() {
12783                                         t.removeCard(data.id)
12784                                     }
12785                                 }
12786                             }
12787                         ]
12788                     }
12789                     
12790                 ] 
12791             }
12792             
12793         ];
12794         
12795         var cn = this.addxtype(
12796             {
12797                  
12798                 xns : Roo.bootstrap,
12799                 xtype : 'Card',
12800                 closeable : true,
12801                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12802                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12803                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12804                 data : data,
12805                 html : false,
12806                  
12807                 items : footer,
12808                 initEvents : function() {
12809                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12810                     var card = this;
12811                     this.imgEl = this.el.select('.card-img-top').first();
12812                     if (this.imgEl) {
12813                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12814                         this.imgEl.set({ 'pointer' : 'cursor' });
12815                                   
12816                     }
12817                     this.getCardFooter().addClass('p-1');
12818                     
12819                   
12820                 }
12821                 
12822             }
12823         );
12824         // dont' really need ot update items.
12825         // this.items.push(cn);
12826         this.fileCollection.add(cn);
12827         
12828         if (!data.srcfile) {
12829             this.updateInput();
12830             return;
12831         }
12832             
12833         var _t = this;
12834         var reader = new FileReader();
12835         reader.addEventListener("load", function() {  
12836             data.srcdata =  reader.result;
12837             _t.updateInput();
12838         });
12839         reader.readAsDataURL(data.srcfile);
12840         
12841         
12842         
12843     },
12844     removeCard : function(id)
12845     {
12846         
12847         var card  = this.fileCollection.get(id);
12848         card.data.is_deleted = 1;
12849         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12850         //this.fileCollection.remove(card);
12851         //this.items = this.items.filter(function(e) { return e != card });
12852         // dont' really need ot update items.
12853         card.el.dom.parentNode.removeChild(card.el.dom);
12854         this.updateInput();
12855
12856         
12857     },
12858     reset: function()
12859     {
12860         this.fileCollection.each(function(card) {
12861             if (card.el.dom && card.el.dom.parentNode) {
12862                 card.el.dom.parentNode.removeChild(card.el.dom);
12863             }
12864         });
12865         this.fileCollection.clear();
12866         this.updateInput();
12867     },
12868     
12869     updateInput : function()
12870     {
12871          var data = [];
12872         this.fileCollection.each(function(e) {
12873             data.push(e.data);
12874             
12875         });
12876         this.inputEl().dom.value = JSON.stringify(data);
12877         
12878         
12879         
12880     }
12881     
12882     
12883 });
12884
12885
12886 Roo.bootstrap.CardUploader.ID = -1;/*
12887  * Based on:
12888  * Ext JS Library 1.1.1
12889  * Copyright(c) 2006-2007, Ext JS, LLC.
12890  *
12891  * Originally Released Under LGPL - original licence link has changed is not relivant.
12892  *
12893  * Fork - LGPL
12894  * <script type="text/javascript">
12895  */
12896
12897
12898 /**
12899  * @class Roo.data.SortTypes
12900  * @singleton
12901  * Defines the default sorting (casting?) comparison functions used when sorting data.
12902  */
12903 Roo.data.SortTypes = {
12904     /**
12905      * Default sort that does nothing
12906      * @param {Mixed} s The value being converted
12907      * @return {Mixed} The comparison value
12908      */
12909     none : function(s){
12910         return s;
12911     },
12912     
12913     /**
12914      * The regular expression used to strip tags
12915      * @type {RegExp}
12916      * @property
12917      */
12918     stripTagsRE : /<\/?[^>]+>/gi,
12919     
12920     /**
12921      * Strips all HTML tags to sort on text only
12922      * @param {Mixed} s The value being converted
12923      * @return {String} The comparison value
12924      */
12925     asText : function(s){
12926         return String(s).replace(this.stripTagsRE, "");
12927     },
12928     
12929     /**
12930      * Strips all HTML tags to sort on text only - Case insensitive
12931      * @param {Mixed} s The value being converted
12932      * @return {String} The comparison value
12933      */
12934     asUCText : function(s){
12935         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12936     },
12937     
12938     /**
12939      * Case insensitive string
12940      * @param {Mixed} s The value being converted
12941      * @return {String} The comparison value
12942      */
12943     asUCString : function(s) {
12944         return String(s).toUpperCase();
12945     },
12946     
12947     /**
12948      * Date sorting
12949      * @param {Mixed} s The value being converted
12950      * @return {Number} The comparison value
12951      */
12952     asDate : function(s) {
12953         if(!s){
12954             return 0;
12955         }
12956         if(s instanceof Date){
12957             return s.getTime();
12958         }
12959         return Date.parse(String(s));
12960     },
12961     
12962     /**
12963      * Float sorting
12964      * @param {Mixed} s The value being converted
12965      * @return {Float} The comparison value
12966      */
12967     asFloat : function(s) {
12968         var val = parseFloat(String(s).replace(/,/g, ""));
12969         if(isNaN(val)) {
12970             val = 0;
12971         }
12972         return val;
12973     },
12974     
12975     /**
12976      * Integer sorting
12977      * @param {Mixed} s The value being converted
12978      * @return {Number} The comparison value
12979      */
12980     asInt : function(s) {
12981         var val = parseInt(String(s).replace(/,/g, ""));
12982         if(isNaN(val)) {
12983             val = 0;
12984         }
12985         return val;
12986     }
12987 };/*
12988  * Based on:
12989  * Ext JS Library 1.1.1
12990  * Copyright(c) 2006-2007, Ext JS, LLC.
12991  *
12992  * Originally Released Under LGPL - original licence link has changed is not relivant.
12993  *
12994  * Fork - LGPL
12995  * <script type="text/javascript">
12996  */
12997
12998 /**
12999 * @class Roo.data.Record
13000  * Instances of this class encapsulate both record <em>definition</em> information, and record
13001  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13002  * to access Records cached in an {@link Roo.data.Store} object.<br>
13003  * <p>
13004  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13005  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13006  * objects.<br>
13007  * <p>
13008  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13009  * @constructor
13010  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13011  * {@link #create}. The parameters are the same.
13012  * @param {Array} data An associative Array of data values keyed by the field name.
13013  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13014  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13015  * not specified an integer id is generated.
13016  */
13017 Roo.data.Record = function(data, id){
13018     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13019     this.data = data;
13020 };
13021
13022 /**
13023  * Generate a constructor for a specific record layout.
13024  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13025  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13026  * Each field definition object may contain the following properties: <ul>
13027  * <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,
13028  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13029  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13030  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13031  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13032  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13033  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13034  * this may be omitted.</p></li>
13035  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13036  * <ul><li>auto (Default, implies no conversion)</li>
13037  * <li>string</li>
13038  * <li>int</li>
13039  * <li>float</li>
13040  * <li>boolean</li>
13041  * <li>date</li></ul></p></li>
13042  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13043  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13044  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13045  * by the Reader into an object that will be stored in the Record. It is passed the
13046  * following parameters:<ul>
13047  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13048  * </ul></p></li>
13049  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13050  * </ul>
13051  * <br>usage:<br><pre><code>
13052 var TopicRecord = Roo.data.Record.create(
13053     {name: 'title', mapping: 'topic_title'},
13054     {name: 'author', mapping: 'username'},
13055     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13056     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13057     {name: 'lastPoster', mapping: 'user2'},
13058     {name: 'excerpt', mapping: 'post_text'}
13059 );
13060
13061 var myNewRecord = new TopicRecord({
13062     title: 'Do my job please',
13063     author: 'noobie',
13064     totalPosts: 1,
13065     lastPost: new Date(),
13066     lastPoster: 'Animal',
13067     excerpt: 'No way dude!'
13068 });
13069 myStore.add(myNewRecord);
13070 </code></pre>
13071  * @method create
13072  * @static
13073  */
13074 Roo.data.Record.create = function(o){
13075     var f = function(){
13076         f.superclass.constructor.apply(this, arguments);
13077     };
13078     Roo.extend(f, Roo.data.Record);
13079     var p = f.prototype;
13080     p.fields = new Roo.util.MixedCollection(false, function(field){
13081         return field.name;
13082     });
13083     for(var i = 0, len = o.length; i < len; i++){
13084         p.fields.add(new Roo.data.Field(o[i]));
13085     }
13086     f.getField = function(name){
13087         return p.fields.get(name);  
13088     };
13089     return f;
13090 };
13091
13092 Roo.data.Record.AUTO_ID = 1000;
13093 Roo.data.Record.EDIT = 'edit';
13094 Roo.data.Record.REJECT = 'reject';
13095 Roo.data.Record.COMMIT = 'commit';
13096
13097 Roo.data.Record.prototype = {
13098     /**
13099      * Readonly flag - true if this record has been modified.
13100      * @type Boolean
13101      */
13102     dirty : false,
13103     editing : false,
13104     error: null,
13105     modified: null,
13106
13107     // private
13108     join : function(store){
13109         this.store = store;
13110     },
13111
13112     /**
13113      * Set the named field to the specified value.
13114      * @param {String} name The name of the field to set.
13115      * @param {Object} value The value to set the field to.
13116      */
13117     set : function(name, value){
13118         if(this.data[name] == value){
13119             return;
13120         }
13121         this.dirty = true;
13122         if(!this.modified){
13123             this.modified = {};
13124         }
13125         if(typeof this.modified[name] == 'undefined'){
13126             this.modified[name] = this.data[name];
13127         }
13128         this.data[name] = value;
13129         if(!this.editing && this.store){
13130             this.store.afterEdit(this);
13131         }       
13132     },
13133
13134     /**
13135      * Get the value of the named field.
13136      * @param {String} name The name of the field to get the value of.
13137      * @return {Object} The value of the field.
13138      */
13139     get : function(name){
13140         return this.data[name]; 
13141     },
13142
13143     // private
13144     beginEdit : function(){
13145         this.editing = true;
13146         this.modified = {}; 
13147     },
13148
13149     // private
13150     cancelEdit : function(){
13151         this.editing = false;
13152         delete this.modified;
13153     },
13154
13155     // private
13156     endEdit : function(){
13157         this.editing = false;
13158         if(this.dirty && this.store){
13159             this.store.afterEdit(this);
13160         }
13161     },
13162
13163     /**
13164      * Usually called by the {@link Roo.data.Store} which owns the Record.
13165      * Rejects all changes made to the Record since either creation, or the last commit operation.
13166      * Modified fields are reverted to their original values.
13167      * <p>
13168      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13169      * of reject operations.
13170      */
13171     reject : function(){
13172         var m = this.modified;
13173         for(var n in m){
13174             if(typeof m[n] != "function"){
13175                 this.data[n] = m[n];
13176             }
13177         }
13178         this.dirty = false;
13179         delete this.modified;
13180         this.editing = false;
13181         if(this.store){
13182             this.store.afterReject(this);
13183         }
13184     },
13185
13186     /**
13187      * Usually called by the {@link Roo.data.Store} which owns the Record.
13188      * Commits all changes made to the Record since either creation, or the last commit operation.
13189      * <p>
13190      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13191      * of commit operations.
13192      */
13193     commit : function(){
13194         this.dirty = false;
13195         delete this.modified;
13196         this.editing = false;
13197         if(this.store){
13198             this.store.afterCommit(this);
13199         }
13200     },
13201
13202     // private
13203     hasError : function(){
13204         return this.error != null;
13205     },
13206
13207     // private
13208     clearError : function(){
13209         this.error = null;
13210     },
13211
13212     /**
13213      * Creates a copy of this record.
13214      * @param {String} id (optional) A new record id if you don't want to use this record's id
13215      * @return {Record}
13216      */
13217     copy : function(newId) {
13218         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13219     }
13220 };/*
13221  * Based on:
13222  * Ext JS Library 1.1.1
13223  * Copyright(c) 2006-2007, Ext JS, LLC.
13224  *
13225  * Originally Released Under LGPL - original licence link has changed is not relivant.
13226  *
13227  * Fork - LGPL
13228  * <script type="text/javascript">
13229  */
13230
13231
13232
13233 /**
13234  * @class Roo.data.Store
13235  * @extends Roo.util.Observable
13236  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13237  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13238  * <p>
13239  * 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
13240  * has no knowledge of the format of the data returned by the Proxy.<br>
13241  * <p>
13242  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13243  * instances from the data object. These records are cached and made available through accessor functions.
13244  * @constructor
13245  * Creates a new Store.
13246  * @param {Object} config A config object containing the objects needed for the Store to access data,
13247  * and read the data into Records.
13248  */
13249 Roo.data.Store = function(config){
13250     this.data = new Roo.util.MixedCollection(false);
13251     this.data.getKey = function(o){
13252         return o.id;
13253     };
13254     this.baseParams = {};
13255     // private
13256     this.paramNames = {
13257         "start" : "start",
13258         "limit" : "limit",
13259         "sort" : "sort",
13260         "dir" : "dir",
13261         "multisort" : "_multisort"
13262     };
13263
13264     if(config && config.data){
13265         this.inlineData = config.data;
13266         delete config.data;
13267     }
13268
13269     Roo.apply(this, config);
13270     
13271     if(this.reader){ // reader passed
13272         this.reader = Roo.factory(this.reader, Roo.data);
13273         this.reader.xmodule = this.xmodule || false;
13274         if(!this.recordType){
13275             this.recordType = this.reader.recordType;
13276         }
13277         if(this.reader.onMetaChange){
13278             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13279         }
13280     }
13281
13282     if(this.recordType){
13283         this.fields = this.recordType.prototype.fields;
13284     }
13285     this.modified = [];
13286
13287     this.addEvents({
13288         /**
13289          * @event datachanged
13290          * Fires when the data cache has changed, and a widget which is using this Store
13291          * as a Record cache should refresh its view.
13292          * @param {Store} this
13293          */
13294         datachanged : true,
13295         /**
13296          * @event metachange
13297          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13298          * @param {Store} this
13299          * @param {Object} meta The JSON metadata
13300          */
13301         metachange : true,
13302         /**
13303          * @event add
13304          * Fires when Records have been added to the Store
13305          * @param {Store} this
13306          * @param {Roo.data.Record[]} records The array of Records added
13307          * @param {Number} index The index at which the record(s) were added
13308          */
13309         add : true,
13310         /**
13311          * @event remove
13312          * Fires when a Record has been removed from the Store
13313          * @param {Store} this
13314          * @param {Roo.data.Record} record The Record that was removed
13315          * @param {Number} index The index at which the record was removed
13316          */
13317         remove : true,
13318         /**
13319          * @event update
13320          * Fires when a Record has been updated
13321          * @param {Store} this
13322          * @param {Roo.data.Record} record The Record that was updated
13323          * @param {String} operation The update operation being performed.  Value may be one of:
13324          * <pre><code>
13325  Roo.data.Record.EDIT
13326  Roo.data.Record.REJECT
13327  Roo.data.Record.COMMIT
13328          * </code></pre>
13329          */
13330         update : true,
13331         /**
13332          * @event clear
13333          * Fires when the data cache has been cleared.
13334          * @param {Store} this
13335          */
13336         clear : true,
13337         /**
13338          * @event beforeload
13339          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13340          * the load action will be canceled.
13341          * @param {Store} this
13342          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13343          */
13344         beforeload : true,
13345         /**
13346          * @event beforeloadadd
13347          * Fires after a new set of Records has been loaded.
13348          * @param {Store} this
13349          * @param {Roo.data.Record[]} records The Records that were loaded
13350          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13351          */
13352         beforeloadadd : true,
13353         /**
13354          * @event load
13355          * Fires after a new set of Records has been loaded, before they are added to the store.
13356          * @param {Store} this
13357          * @param {Roo.data.Record[]} records The Records that were loaded
13358          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13359          * @params {Object} return from reader
13360          */
13361         load : true,
13362         /**
13363          * @event loadexception
13364          * Fires if an exception occurs in the Proxy during loading.
13365          * Called with the signature of the Proxy's "loadexception" event.
13366          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13367          * 
13368          * @param {Proxy} 
13369          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13370          * @param {Object} load options 
13371          * @param {Object} jsonData from your request (normally this contains the Exception)
13372          */
13373         loadexception : true
13374     });
13375     
13376     if(this.proxy){
13377         this.proxy = Roo.factory(this.proxy, Roo.data);
13378         this.proxy.xmodule = this.xmodule || false;
13379         this.relayEvents(this.proxy,  ["loadexception"]);
13380     }
13381     this.sortToggle = {};
13382     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13383
13384     Roo.data.Store.superclass.constructor.call(this);
13385
13386     if(this.inlineData){
13387         this.loadData(this.inlineData);
13388         delete this.inlineData;
13389     }
13390 };
13391
13392 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13393      /**
13394     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13395     * without a remote query - used by combo/forms at present.
13396     */
13397     
13398     /**
13399     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13400     */
13401     /**
13402     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13403     */
13404     /**
13405     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13406     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13407     */
13408     /**
13409     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13410     * on any HTTP request
13411     */
13412     /**
13413     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13414     */
13415     /**
13416     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13417     */
13418     multiSort: false,
13419     /**
13420     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13421     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13422     */
13423     remoteSort : false,
13424
13425     /**
13426     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13427      * loaded or when a record is removed. (defaults to false).
13428     */
13429     pruneModifiedRecords : false,
13430
13431     // private
13432     lastOptions : null,
13433
13434     /**
13435      * Add Records to the Store and fires the add event.
13436      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13437      */
13438     add : function(records){
13439         records = [].concat(records);
13440         for(var i = 0, len = records.length; i < len; i++){
13441             records[i].join(this);
13442         }
13443         var index = this.data.length;
13444         this.data.addAll(records);
13445         this.fireEvent("add", this, records, index);
13446     },
13447
13448     /**
13449      * Remove a Record from the Store and fires the remove event.
13450      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13451      */
13452     remove : function(record){
13453         var index = this.data.indexOf(record);
13454         this.data.removeAt(index);
13455  
13456         if(this.pruneModifiedRecords){
13457             this.modified.remove(record);
13458         }
13459         this.fireEvent("remove", this, record, index);
13460     },
13461
13462     /**
13463      * Remove all Records from the Store and fires the clear event.
13464      */
13465     removeAll : function(){
13466         this.data.clear();
13467         if(this.pruneModifiedRecords){
13468             this.modified = [];
13469         }
13470         this.fireEvent("clear", this);
13471     },
13472
13473     /**
13474      * Inserts Records to the Store at the given index and fires the add event.
13475      * @param {Number} index The start index at which to insert the passed Records.
13476      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13477      */
13478     insert : function(index, records){
13479         records = [].concat(records);
13480         for(var i = 0, len = records.length; i < len; i++){
13481             this.data.insert(index, records[i]);
13482             records[i].join(this);
13483         }
13484         this.fireEvent("add", this, records, index);
13485     },
13486
13487     /**
13488      * Get the index within the cache of the passed Record.
13489      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13490      * @return {Number} The index of the passed Record. Returns -1 if not found.
13491      */
13492     indexOf : function(record){
13493         return this.data.indexOf(record);
13494     },
13495
13496     /**
13497      * Get the index within the cache of the Record with the passed id.
13498      * @param {String} id The id of the Record to find.
13499      * @return {Number} The index of the Record. Returns -1 if not found.
13500      */
13501     indexOfId : function(id){
13502         return this.data.indexOfKey(id);
13503     },
13504
13505     /**
13506      * Get the Record with the specified id.
13507      * @param {String} id The id of the Record to find.
13508      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13509      */
13510     getById : function(id){
13511         return this.data.key(id);
13512     },
13513
13514     /**
13515      * Get the Record at the specified index.
13516      * @param {Number} index The index of the Record to find.
13517      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13518      */
13519     getAt : function(index){
13520         return this.data.itemAt(index);
13521     },
13522
13523     /**
13524      * Returns a range of Records between specified indices.
13525      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13526      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13527      * @return {Roo.data.Record[]} An array of Records
13528      */
13529     getRange : function(start, end){
13530         return this.data.getRange(start, end);
13531     },
13532
13533     // private
13534     storeOptions : function(o){
13535         o = Roo.apply({}, o);
13536         delete o.callback;
13537         delete o.scope;
13538         this.lastOptions = o;
13539     },
13540
13541     /**
13542      * Loads the Record cache from the configured Proxy using the configured Reader.
13543      * <p>
13544      * If using remote paging, then the first load call must specify the <em>start</em>
13545      * and <em>limit</em> properties in the options.params property to establish the initial
13546      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13547      * <p>
13548      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13549      * and this call will return before the new data has been loaded. Perform any post-processing
13550      * in a callback function, or in a "load" event handler.</strong>
13551      * <p>
13552      * @param {Object} options An object containing properties which control loading options:<ul>
13553      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13554      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13555      * passed the following arguments:<ul>
13556      * <li>r : Roo.data.Record[]</li>
13557      * <li>options: Options object from the load call</li>
13558      * <li>success: Boolean success indicator</li></ul></li>
13559      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13560      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13561      * </ul>
13562      */
13563     load : function(options){
13564         options = options || {};
13565         if(this.fireEvent("beforeload", this, options) !== false){
13566             this.storeOptions(options);
13567             var p = Roo.apply(options.params || {}, this.baseParams);
13568             // if meta was not loaded from remote source.. try requesting it.
13569             if (!this.reader.metaFromRemote) {
13570                 p._requestMeta = 1;
13571             }
13572             if(this.sortInfo && this.remoteSort){
13573                 var pn = this.paramNames;
13574                 p[pn["sort"]] = this.sortInfo.field;
13575                 p[pn["dir"]] = this.sortInfo.direction;
13576             }
13577             if (this.multiSort) {
13578                 var pn = this.paramNames;
13579                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13580             }
13581             
13582             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13583         }
13584     },
13585
13586     /**
13587      * Reloads the Record cache from the configured Proxy using the configured Reader and
13588      * the options from the last load operation performed.
13589      * @param {Object} options (optional) An object containing properties which may override the options
13590      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13591      * the most recently used options are reused).
13592      */
13593     reload : function(options){
13594         this.load(Roo.applyIf(options||{}, this.lastOptions));
13595     },
13596
13597     // private
13598     // Called as a callback by the Reader during a load operation.
13599     loadRecords : function(o, options, success){
13600         if(!o || success === false){
13601             if(success !== false){
13602                 this.fireEvent("load", this, [], options, o);
13603             }
13604             if(options.callback){
13605                 options.callback.call(options.scope || this, [], options, false);
13606             }
13607             return;
13608         }
13609         // if data returned failure - throw an exception.
13610         if (o.success === false) {
13611             // show a message if no listener is registered.
13612             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13613                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13614             }
13615             // loadmask wil be hooked into this..
13616             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13617             return;
13618         }
13619         var r = o.records, t = o.totalRecords || r.length;
13620         
13621         this.fireEvent("beforeloadadd", this, r, options, o);
13622         
13623         if(!options || options.add !== true){
13624             if(this.pruneModifiedRecords){
13625                 this.modified = [];
13626             }
13627             for(var i = 0, len = r.length; i < len; i++){
13628                 r[i].join(this);
13629             }
13630             if(this.snapshot){
13631                 this.data = this.snapshot;
13632                 delete this.snapshot;
13633             }
13634             this.data.clear();
13635             this.data.addAll(r);
13636             this.totalLength = t;
13637             this.applySort();
13638             this.fireEvent("datachanged", this);
13639         }else{
13640             this.totalLength = Math.max(t, this.data.length+r.length);
13641             this.add(r);
13642         }
13643         
13644         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13645                 
13646             var e = new Roo.data.Record({});
13647
13648             e.set(this.parent.displayField, this.parent.emptyTitle);
13649             e.set(this.parent.valueField, '');
13650
13651             this.insert(0, e);
13652         }
13653             
13654         this.fireEvent("load", this, r, options, o);
13655         if(options.callback){
13656             options.callback.call(options.scope || this, r, options, true);
13657         }
13658     },
13659
13660
13661     /**
13662      * Loads data from a passed data block. A Reader which understands the format of the data
13663      * must have been configured in the constructor.
13664      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13665      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13666      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13667      */
13668     loadData : function(o, append){
13669         var r = this.reader.readRecords(o);
13670         this.loadRecords(r, {add: append}, true);
13671     },
13672     
13673      /**
13674      * using 'cn' the nested child reader read the child array into it's child stores.
13675      * @param {Object} rec The record with a 'children array
13676      */
13677     loadDataFromChildren : function(rec)
13678     {
13679         this.loadData(this.reader.toLoadData(rec));
13680     },
13681     
13682
13683     /**
13684      * Gets the number of cached records.
13685      * <p>
13686      * <em>If using paging, this may not be the total size of the dataset. If the data object
13687      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13688      * the data set size</em>
13689      */
13690     getCount : function(){
13691         return this.data.length || 0;
13692     },
13693
13694     /**
13695      * Gets the total number of records in the dataset as returned by the server.
13696      * <p>
13697      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13698      * the dataset size</em>
13699      */
13700     getTotalCount : function(){
13701         return this.totalLength || 0;
13702     },
13703
13704     /**
13705      * Returns the sort state of the Store as an object with two properties:
13706      * <pre><code>
13707  field {String} The name of the field by which the Records are sorted
13708  direction {String} The sort order, "ASC" or "DESC"
13709      * </code></pre>
13710      */
13711     getSortState : function(){
13712         return this.sortInfo;
13713     },
13714
13715     // private
13716     applySort : function(){
13717         if(this.sortInfo && !this.remoteSort){
13718             var s = this.sortInfo, f = s.field;
13719             var st = this.fields.get(f).sortType;
13720             var fn = function(r1, r2){
13721                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13722                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13723             };
13724             this.data.sort(s.direction, fn);
13725             if(this.snapshot && this.snapshot != this.data){
13726                 this.snapshot.sort(s.direction, fn);
13727             }
13728         }
13729     },
13730
13731     /**
13732      * Sets the default sort column and order to be used by the next load operation.
13733      * @param {String} fieldName The name of the field to sort by.
13734      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13735      */
13736     setDefaultSort : function(field, dir){
13737         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13738     },
13739
13740     /**
13741      * Sort the Records.
13742      * If remote sorting is used, the sort is performed on the server, and the cache is
13743      * reloaded. If local sorting is used, the cache is sorted internally.
13744      * @param {String} fieldName The name of the field to sort by.
13745      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13746      */
13747     sort : function(fieldName, dir){
13748         var f = this.fields.get(fieldName);
13749         if(!dir){
13750             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13751             
13752             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13753                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13754             }else{
13755                 dir = f.sortDir;
13756             }
13757         }
13758         this.sortToggle[f.name] = dir;
13759         this.sortInfo = {field: f.name, direction: dir};
13760         if(!this.remoteSort){
13761             this.applySort();
13762             this.fireEvent("datachanged", this);
13763         }else{
13764             this.load(this.lastOptions);
13765         }
13766     },
13767
13768     /**
13769      * Calls the specified function for each of the Records in the cache.
13770      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13771      * Returning <em>false</em> aborts and exits the iteration.
13772      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13773      */
13774     each : function(fn, scope){
13775         this.data.each(fn, scope);
13776     },
13777
13778     /**
13779      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13780      * (e.g., during paging).
13781      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13782      */
13783     getModifiedRecords : function(){
13784         return this.modified;
13785     },
13786
13787     // private
13788     createFilterFn : function(property, value, anyMatch){
13789         if(!value.exec){ // not a regex
13790             value = String(value);
13791             if(value.length == 0){
13792                 return false;
13793             }
13794             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13795         }
13796         return function(r){
13797             return value.test(r.data[property]);
13798         };
13799     },
13800
13801     /**
13802      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13803      * @param {String} property A field on your records
13804      * @param {Number} start The record index to start at (defaults to 0)
13805      * @param {Number} end The last record index to include (defaults to length - 1)
13806      * @return {Number} The sum
13807      */
13808     sum : function(property, start, end){
13809         var rs = this.data.items, v = 0;
13810         start = start || 0;
13811         end = (end || end === 0) ? end : rs.length-1;
13812
13813         for(var i = start; i <= end; i++){
13814             v += (rs[i].data[property] || 0);
13815         }
13816         return v;
13817     },
13818
13819     /**
13820      * Filter the records by a specified property.
13821      * @param {String} field A field on your records
13822      * @param {String/RegExp} value Either a string that the field
13823      * should start with or a RegExp to test against the field
13824      * @param {Boolean} anyMatch True to match any part not just the beginning
13825      */
13826     filter : function(property, value, anyMatch){
13827         var fn = this.createFilterFn(property, value, anyMatch);
13828         return fn ? this.filterBy(fn) : this.clearFilter();
13829     },
13830
13831     /**
13832      * Filter by a function. The specified function will be called with each
13833      * record in this data source. If the function returns true the record is included,
13834      * otherwise it is filtered.
13835      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13836      * @param {Object} scope (optional) The scope of the function (defaults to this)
13837      */
13838     filterBy : function(fn, scope){
13839         this.snapshot = this.snapshot || this.data;
13840         this.data = this.queryBy(fn, scope||this);
13841         this.fireEvent("datachanged", this);
13842     },
13843
13844     /**
13845      * Query the records by a specified property.
13846      * @param {String} field A field on your records
13847      * @param {String/RegExp} value Either a string that the field
13848      * should start with or a RegExp to test against the field
13849      * @param {Boolean} anyMatch True to match any part not just the beginning
13850      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13851      */
13852     query : function(property, value, anyMatch){
13853         var fn = this.createFilterFn(property, value, anyMatch);
13854         return fn ? this.queryBy(fn) : this.data.clone();
13855     },
13856
13857     /**
13858      * Query by a function. The specified function will be called with each
13859      * record in this data source. If the function returns true the record is included
13860      * in the results.
13861      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13862      * @param {Object} scope (optional) The scope of the function (defaults to this)
13863       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13864      **/
13865     queryBy : function(fn, scope){
13866         var data = this.snapshot || this.data;
13867         return data.filterBy(fn, scope||this);
13868     },
13869
13870     /**
13871      * Collects unique values for a particular dataIndex from this store.
13872      * @param {String} dataIndex The property to collect
13873      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13874      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13875      * @return {Array} An array of the unique values
13876      **/
13877     collect : function(dataIndex, allowNull, bypassFilter){
13878         var d = (bypassFilter === true && this.snapshot) ?
13879                 this.snapshot.items : this.data.items;
13880         var v, sv, r = [], l = {};
13881         for(var i = 0, len = d.length; i < len; i++){
13882             v = d[i].data[dataIndex];
13883             sv = String(v);
13884             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13885                 l[sv] = true;
13886                 r[r.length] = v;
13887             }
13888         }
13889         return r;
13890     },
13891
13892     /**
13893      * Revert to a view of the Record cache with no filtering applied.
13894      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13895      */
13896     clearFilter : function(suppressEvent){
13897         if(this.snapshot && this.snapshot != this.data){
13898             this.data = this.snapshot;
13899             delete this.snapshot;
13900             if(suppressEvent !== true){
13901                 this.fireEvent("datachanged", this);
13902             }
13903         }
13904     },
13905
13906     // private
13907     afterEdit : function(record){
13908         if(this.modified.indexOf(record) == -1){
13909             this.modified.push(record);
13910         }
13911         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13912     },
13913     
13914     // private
13915     afterReject : function(record){
13916         this.modified.remove(record);
13917         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13918     },
13919
13920     // private
13921     afterCommit : function(record){
13922         this.modified.remove(record);
13923         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13924     },
13925
13926     /**
13927      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13928      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13929      */
13930     commitChanges : function(){
13931         var m = this.modified.slice(0);
13932         this.modified = [];
13933         for(var i = 0, len = m.length; i < len; i++){
13934             m[i].commit();
13935         }
13936     },
13937
13938     /**
13939      * Cancel outstanding changes on all changed records.
13940      */
13941     rejectChanges : function(){
13942         var m = this.modified.slice(0);
13943         this.modified = [];
13944         for(var i = 0, len = m.length; i < len; i++){
13945             m[i].reject();
13946         }
13947     },
13948
13949     onMetaChange : function(meta, rtype, o){
13950         this.recordType = rtype;
13951         this.fields = rtype.prototype.fields;
13952         delete this.snapshot;
13953         this.sortInfo = meta.sortInfo || this.sortInfo;
13954         this.modified = [];
13955         this.fireEvent('metachange', this, this.reader.meta);
13956     },
13957     
13958     moveIndex : function(data, type)
13959     {
13960         var index = this.indexOf(data);
13961         
13962         var newIndex = index + type;
13963         
13964         this.remove(data);
13965         
13966         this.insert(newIndex, data);
13967         
13968     }
13969 });/*
13970  * Based on:
13971  * Ext JS Library 1.1.1
13972  * Copyright(c) 2006-2007, Ext JS, LLC.
13973  *
13974  * Originally Released Under LGPL - original licence link has changed is not relivant.
13975  *
13976  * Fork - LGPL
13977  * <script type="text/javascript">
13978  */
13979
13980 /**
13981  * @class Roo.data.SimpleStore
13982  * @extends Roo.data.Store
13983  * Small helper class to make creating Stores from Array data easier.
13984  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13985  * @cfg {Array} fields An array of field definition objects, or field name strings.
13986  * @cfg {Object} an existing reader (eg. copied from another store)
13987  * @cfg {Array} data The multi-dimensional array of data
13988  * @constructor
13989  * @param {Object} config
13990  */
13991 Roo.data.SimpleStore = function(config)
13992 {
13993     Roo.data.SimpleStore.superclass.constructor.call(this, {
13994         isLocal : true,
13995         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13996                 id: config.id
13997             },
13998             Roo.data.Record.create(config.fields)
13999         ),
14000         proxy : new Roo.data.MemoryProxy(config.data)
14001     });
14002     this.load();
14003 };
14004 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14005  * Based on:
14006  * Ext JS Library 1.1.1
14007  * Copyright(c) 2006-2007, Ext JS, LLC.
14008  *
14009  * Originally Released Under LGPL - original licence link has changed is not relivant.
14010  *
14011  * Fork - LGPL
14012  * <script type="text/javascript">
14013  */
14014
14015 /**
14016 /**
14017  * @extends Roo.data.Store
14018  * @class Roo.data.JsonStore
14019  * Small helper class to make creating Stores for JSON data easier. <br/>
14020 <pre><code>
14021 var store = new Roo.data.JsonStore({
14022     url: 'get-images.php',
14023     root: 'images',
14024     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14025 });
14026 </code></pre>
14027  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14028  * JsonReader and HttpProxy (unless inline data is provided).</b>
14029  * @cfg {Array} fields An array of field definition objects, or field name strings.
14030  * @constructor
14031  * @param {Object} config
14032  */
14033 Roo.data.JsonStore = function(c){
14034     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14035         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14036         reader: new Roo.data.JsonReader(c, c.fields)
14037     }));
14038 };
14039 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14040  * Based on:
14041  * Ext JS Library 1.1.1
14042  * Copyright(c) 2006-2007, Ext JS, LLC.
14043  *
14044  * Originally Released Under LGPL - original licence link has changed is not relivant.
14045  *
14046  * Fork - LGPL
14047  * <script type="text/javascript">
14048  */
14049
14050  
14051 Roo.data.Field = function(config){
14052     if(typeof config == "string"){
14053         config = {name: config};
14054     }
14055     Roo.apply(this, config);
14056     
14057     if(!this.type){
14058         this.type = "auto";
14059     }
14060     
14061     var st = Roo.data.SortTypes;
14062     // named sortTypes are supported, here we look them up
14063     if(typeof this.sortType == "string"){
14064         this.sortType = st[this.sortType];
14065     }
14066     
14067     // set default sortType for strings and dates
14068     if(!this.sortType){
14069         switch(this.type){
14070             case "string":
14071                 this.sortType = st.asUCString;
14072                 break;
14073             case "date":
14074                 this.sortType = st.asDate;
14075                 break;
14076             default:
14077                 this.sortType = st.none;
14078         }
14079     }
14080
14081     // define once
14082     var stripRe = /[\$,%]/g;
14083
14084     // prebuilt conversion function for this field, instead of
14085     // switching every time we're reading a value
14086     if(!this.convert){
14087         var cv, dateFormat = this.dateFormat;
14088         switch(this.type){
14089             case "":
14090             case "auto":
14091             case undefined:
14092                 cv = function(v){ return v; };
14093                 break;
14094             case "string":
14095                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14096                 break;
14097             case "int":
14098                 cv = function(v){
14099                     return v !== undefined && v !== null && v !== '' ?
14100                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14101                     };
14102                 break;
14103             case "float":
14104                 cv = function(v){
14105                     return v !== undefined && v !== null && v !== '' ?
14106                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14107                     };
14108                 break;
14109             case "bool":
14110             case "boolean":
14111                 cv = function(v){ return v === true || v === "true" || v == 1; };
14112                 break;
14113             case "date":
14114                 cv = function(v){
14115                     if(!v){
14116                         return '';
14117                     }
14118                     if(v instanceof Date){
14119                         return v;
14120                     }
14121                     if(dateFormat){
14122                         if(dateFormat == "timestamp"){
14123                             return new Date(v*1000);
14124                         }
14125                         return Date.parseDate(v, dateFormat);
14126                     }
14127                     var parsed = Date.parse(v);
14128                     return parsed ? new Date(parsed) : null;
14129                 };
14130              break;
14131             
14132         }
14133         this.convert = cv;
14134     }
14135 };
14136
14137 Roo.data.Field.prototype = {
14138     dateFormat: null,
14139     defaultValue: "",
14140     mapping: null,
14141     sortType : null,
14142     sortDir : "ASC"
14143 };/*
14144  * Based on:
14145  * Ext JS Library 1.1.1
14146  * Copyright(c) 2006-2007, Ext JS, LLC.
14147  *
14148  * Originally Released Under LGPL - original licence link has changed is not relivant.
14149  *
14150  * Fork - LGPL
14151  * <script type="text/javascript">
14152  */
14153  
14154 // Base class for reading structured data from a data source.  This class is intended to be
14155 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14156
14157 /**
14158  * @class Roo.data.DataReader
14159  * Base class for reading structured data from a data source.  This class is intended to be
14160  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14161  */
14162
14163 Roo.data.DataReader = function(meta, recordType){
14164     
14165     this.meta = meta;
14166     
14167     this.recordType = recordType instanceof Array ? 
14168         Roo.data.Record.create(recordType) : recordType;
14169 };
14170
14171 Roo.data.DataReader.prototype = {
14172     
14173     
14174     readerType : 'Data',
14175      /**
14176      * Create an empty record
14177      * @param {Object} data (optional) - overlay some values
14178      * @return {Roo.data.Record} record created.
14179      */
14180     newRow :  function(d) {
14181         var da =  {};
14182         this.recordType.prototype.fields.each(function(c) {
14183             switch( c.type) {
14184                 case 'int' : da[c.name] = 0; break;
14185                 case 'date' : da[c.name] = new Date(); break;
14186                 case 'float' : da[c.name] = 0.0; break;
14187                 case 'boolean' : da[c.name] = false; break;
14188                 default : da[c.name] = ""; break;
14189             }
14190             
14191         });
14192         return new this.recordType(Roo.apply(da, d));
14193     }
14194     
14195     
14196 };/*
14197  * Based on:
14198  * Ext JS Library 1.1.1
14199  * Copyright(c) 2006-2007, Ext JS, LLC.
14200  *
14201  * Originally Released Under LGPL - original licence link has changed is not relivant.
14202  *
14203  * Fork - LGPL
14204  * <script type="text/javascript">
14205  */
14206
14207 /**
14208  * @class Roo.data.DataProxy
14209  * @extends Roo.data.Observable
14210  * This class is an abstract base class for implementations which provide retrieval of
14211  * unformatted data objects.<br>
14212  * <p>
14213  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14214  * (of the appropriate type which knows how to parse the data object) to provide a block of
14215  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14216  * <p>
14217  * Custom implementations must implement the load method as described in
14218  * {@link Roo.data.HttpProxy#load}.
14219  */
14220 Roo.data.DataProxy = function(){
14221     this.addEvents({
14222         /**
14223          * @event beforeload
14224          * Fires before a network request is made to retrieve a data object.
14225          * @param {Object} This DataProxy object.
14226          * @param {Object} params The params parameter to the load function.
14227          */
14228         beforeload : true,
14229         /**
14230          * @event load
14231          * Fires before the load method's callback is called.
14232          * @param {Object} This DataProxy object.
14233          * @param {Object} o The data object.
14234          * @param {Object} arg The callback argument object passed to the load function.
14235          */
14236         load : true,
14237         /**
14238          * @event loadexception
14239          * Fires if an Exception occurs during data retrieval.
14240          * @param {Object} This DataProxy object.
14241          * @param {Object} o The data object.
14242          * @param {Object} arg The callback argument object passed to the load function.
14243          * @param {Object} e The Exception.
14244          */
14245         loadexception : true
14246     });
14247     Roo.data.DataProxy.superclass.constructor.call(this);
14248 };
14249
14250 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14251
14252     /**
14253      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14254      */
14255 /*
14256  * Based on:
14257  * Ext JS Library 1.1.1
14258  * Copyright(c) 2006-2007, Ext JS, LLC.
14259  *
14260  * Originally Released Under LGPL - original licence link has changed is not relivant.
14261  *
14262  * Fork - LGPL
14263  * <script type="text/javascript">
14264  */
14265 /**
14266  * @class Roo.data.MemoryProxy
14267  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14268  * to the Reader when its load method is called.
14269  * @constructor
14270  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14271  */
14272 Roo.data.MemoryProxy = function(data){
14273     if (data.data) {
14274         data = data.data;
14275     }
14276     Roo.data.MemoryProxy.superclass.constructor.call(this);
14277     this.data = data;
14278 };
14279
14280 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14281     
14282     /**
14283      * Load data from the requested source (in this case an in-memory
14284      * data object passed to the constructor), read the data object into
14285      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14286      * process that block using the passed callback.
14287      * @param {Object} params This parameter is not used by the MemoryProxy class.
14288      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14289      * object into a block of Roo.data.Records.
14290      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14291      * The function must be passed <ul>
14292      * <li>The Record block object</li>
14293      * <li>The "arg" argument from the load function</li>
14294      * <li>A boolean success indicator</li>
14295      * </ul>
14296      * @param {Object} scope The scope in which to call the callback
14297      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14298      */
14299     load : function(params, reader, callback, scope, arg){
14300         params = params || {};
14301         var result;
14302         try {
14303             result = reader.readRecords(params.data ? params.data :this.data);
14304         }catch(e){
14305             this.fireEvent("loadexception", this, arg, null, e);
14306             callback.call(scope, null, arg, false);
14307             return;
14308         }
14309         callback.call(scope, result, arg, true);
14310     },
14311     
14312     // private
14313     update : function(params, records){
14314         
14315     }
14316 });/*
14317  * Based on:
14318  * Ext JS Library 1.1.1
14319  * Copyright(c) 2006-2007, Ext JS, LLC.
14320  *
14321  * Originally Released Under LGPL - original licence link has changed is not relivant.
14322  *
14323  * Fork - LGPL
14324  * <script type="text/javascript">
14325  */
14326 /**
14327  * @class Roo.data.HttpProxy
14328  * @extends Roo.data.DataProxy
14329  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14330  * configured to reference a certain URL.<br><br>
14331  * <p>
14332  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14333  * from which the running page was served.<br><br>
14334  * <p>
14335  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14336  * <p>
14337  * Be aware that to enable the browser to parse an XML document, the server must set
14338  * the Content-Type header in the HTTP response to "text/xml".
14339  * @constructor
14340  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14341  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14342  * will be used to make the request.
14343  */
14344 Roo.data.HttpProxy = function(conn){
14345     Roo.data.HttpProxy.superclass.constructor.call(this);
14346     // is conn a conn config or a real conn?
14347     this.conn = conn;
14348     this.useAjax = !conn || !conn.events;
14349   
14350 };
14351
14352 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14353     // thse are take from connection...
14354     
14355     /**
14356      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14357      */
14358     /**
14359      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14360      * extra parameters to each request made by this object. (defaults to undefined)
14361      */
14362     /**
14363      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14364      *  to each request made by this object. (defaults to undefined)
14365      */
14366     /**
14367      * @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)
14368      */
14369     /**
14370      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14371      */
14372      /**
14373      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14374      * @type Boolean
14375      */
14376   
14377
14378     /**
14379      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14380      * @type Boolean
14381      */
14382     /**
14383      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14384      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14385      * a finer-grained basis than the DataProxy events.
14386      */
14387     getConnection : function(){
14388         return this.useAjax ? Roo.Ajax : this.conn;
14389     },
14390
14391     /**
14392      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14393      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14394      * process that block using the passed callback.
14395      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14396      * for the request to the remote server.
14397      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14398      * object into a block of Roo.data.Records.
14399      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14400      * The function must be passed <ul>
14401      * <li>The Record block object</li>
14402      * <li>The "arg" argument from the load function</li>
14403      * <li>A boolean success indicator</li>
14404      * </ul>
14405      * @param {Object} scope The scope in which to call the callback
14406      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14407      */
14408     load : function(params, reader, callback, scope, arg){
14409         if(this.fireEvent("beforeload", this, params) !== false){
14410             var  o = {
14411                 params : params || {},
14412                 request: {
14413                     callback : callback,
14414                     scope : scope,
14415                     arg : arg
14416                 },
14417                 reader: reader,
14418                 callback : this.loadResponse,
14419                 scope: this
14420             };
14421             if(this.useAjax){
14422                 Roo.applyIf(o, this.conn);
14423                 if(this.activeRequest){
14424                     Roo.Ajax.abort(this.activeRequest);
14425                 }
14426                 this.activeRequest = Roo.Ajax.request(o);
14427             }else{
14428                 this.conn.request(o);
14429             }
14430         }else{
14431             callback.call(scope||this, null, arg, false);
14432         }
14433     },
14434
14435     // private
14436     loadResponse : function(o, success, response){
14437         delete this.activeRequest;
14438         if(!success){
14439             this.fireEvent("loadexception", this, o, response);
14440             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14441             return;
14442         }
14443         var result;
14444         try {
14445             result = o.reader.read(response);
14446         }catch(e){
14447             this.fireEvent("loadexception", this, o, response, e);
14448             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14449             return;
14450         }
14451         
14452         this.fireEvent("load", this, o, o.request.arg);
14453         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14454     },
14455
14456     // private
14457     update : function(dataSet){
14458
14459     },
14460
14461     // private
14462     updateResponse : function(dataSet){
14463
14464     }
14465 });/*
14466  * Based on:
14467  * Ext JS Library 1.1.1
14468  * Copyright(c) 2006-2007, Ext JS, LLC.
14469  *
14470  * Originally Released Under LGPL - original licence link has changed is not relivant.
14471  *
14472  * Fork - LGPL
14473  * <script type="text/javascript">
14474  */
14475
14476 /**
14477  * @class Roo.data.ScriptTagProxy
14478  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14479  * other than the originating domain of the running page.<br><br>
14480  * <p>
14481  * <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
14482  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14483  * <p>
14484  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14485  * source code that is used as the source inside a &lt;script> tag.<br><br>
14486  * <p>
14487  * In order for the browser to process the returned data, the server must wrap the data object
14488  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14489  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14490  * depending on whether the callback name was passed:
14491  * <p>
14492  * <pre><code>
14493 boolean scriptTag = false;
14494 String cb = request.getParameter("callback");
14495 if (cb != null) {
14496     scriptTag = true;
14497     response.setContentType("text/javascript");
14498 } else {
14499     response.setContentType("application/x-json");
14500 }
14501 Writer out = response.getWriter();
14502 if (scriptTag) {
14503     out.write(cb + "(");
14504 }
14505 out.print(dataBlock.toJsonString());
14506 if (scriptTag) {
14507     out.write(");");
14508 }
14509 </pre></code>
14510  *
14511  * @constructor
14512  * @param {Object} config A configuration object.
14513  */
14514 Roo.data.ScriptTagProxy = function(config){
14515     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14516     Roo.apply(this, config);
14517     this.head = document.getElementsByTagName("head")[0];
14518 };
14519
14520 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14521
14522 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14523     /**
14524      * @cfg {String} url The URL from which to request the data object.
14525      */
14526     /**
14527      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14528      */
14529     timeout : 30000,
14530     /**
14531      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14532      * the server the name of the callback function set up by the load call to process the returned data object.
14533      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14534      * javascript output which calls this named function passing the data object as its only parameter.
14535      */
14536     callbackParam : "callback",
14537     /**
14538      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14539      * name to the request.
14540      */
14541     nocache : true,
14542
14543     /**
14544      * Load data from the configured URL, read the data object into
14545      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14546      * process that block using the passed callback.
14547      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14548      * for the request to the remote server.
14549      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14550      * object into a block of Roo.data.Records.
14551      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14552      * The function must be passed <ul>
14553      * <li>The Record block object</li>
14554      * <li>The "arg" argument from the load function</li>
14555      * <li>A boolean success indicator</li>
14556      * </ul>
14557      * @param {Object} scope The scope in which to call the callback
14558      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14559      */
14560     load : function(params, reader, callback, scope, arg){
14561         if(this.fireEvent("beforeload", this, params) !== false){
14562
14563             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14564
14565             var url = this.url;
14566             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14567             if(this.nocache){
14568                 url += "&_dc=" + (new Date().getTime());
14569             }
14570             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14571             var trans = {
14572                 id : transId,
14573                 cb : "stcCallback"+transId,
14574                 scriptId : "stcScript"+transId,
14575                 params : params,
14576                 arg : arg,
14577                 url : url,
14578                 callback : callback,
14579                 scope : scope,
14580                 reader : reader
14581             };
14582             var conn = this;
14583
14584             window[trans.cb] = function(o){
14585                 conn.handleResponse(o, trans);
14586             };
14587
14588             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14589
14590             if(this.autoAbort !== false){
14591                 this.abort();
14592             }
14593
14594             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14595
14596             var script = document.createElement("script");
14597             script.setAttribute("src", url);
14598             script.setAttribute("type", "text/javascript");
14599             script.setAttribute("id", trans.scriptId);
14600             this.head.appendChild(script);
14601
14602             this.trans = trans;
14603         }else{
14604             callback.call(scope||this, null, arg, false);
14605         }
14606     },
14607
14608     // private
14609     isLoading : function(){
14610         return this.trans ? true : false;
14611     },
14612
14613     /**
14614      * Abort the current server request.
14615      */
14616     abort : function(){
14617         if(this.isLoading()){
14618             this.destroyTrans(this.trans);
14619         }
14620     },
14621
14622     // private
14623     destroyTrans : function(trans, isLoaded){
14624         this.head.removeChild(document.getElementById(trans.scriptId));
14625         clearTimeout(trans.timeoutId);
14626         if(isLoaded){
14627             window[trans.cb] = undefined;
14628             try{
14629                 delete window[trans.cb];
14630             }catch(e){}
14631         }else{
14632             // if hasn't been loaded, wait for load to remove it to prevent script error
14633             window[trans.cb] = function(){
14634                 window[trans.cb] = undefined;
14635                 try{
14636                     delete window[trans.cb];
14637                 }catch(e){}
14638             };
14639         }
14640     },
14641
14642     // private
14643     handleResponse : function(o, trans){
14644         this.trans = false;
14645         this.destroyTrans(trans, true);
14646         var result;
14647         try {
14648             result = trans.reader.readRecords(o);
14649         }catch(e){
14650             this.fireEvent("loadexception", this, o, trans.arg, e);
14651             trans.callback.call(trans.scope||window, null, trans.arg, false);
14652             return;
14653         }
14654         this.fireEvent("load", this, o, trans.arg);
14655         trans.callback.call(trans.scope||window, result, trans.arg, true);
14656     },
14657
14658     // private
14659     handleFailure : function(trans){
14660         this.trans = false;
14661         this.destroyTrans(trans, false);
14662         this.fireEvent("loadexception", this, null, trans.arg);
14663         trans.callback.call(trans.scope||window, null, trans.arg, false);
14664     }
14665 });/*
14666  * Based on:
14667  * Ext JS Library 1.1.1
14668  * Copyright(c) 2006-2007, Ext JS, LLC.
14669  *
14670  * Originally Released Under LGPL - original licence link has changed is not relivant.
14671  *
14672  * Fork - LGPL
14673  * <script type="text/javascript">
14674  */
14675
14676 /**
14677  * @class Roo.data.JsonReader
14678  * @extends Roo.data.DataReader
14679  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14680  * based on mappings in a provided Roo.data.Record constructor.
14681  * 
14682  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14683  * in the reply previously. 
14684  * 
14685  * <p>
14686  * Example code:
14687  * <pre><code>
14688 var RecordDef = Roo.data.Record.create([
14689     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14690     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14691 ]);
14692 var myReader = new Roo.data.JsonReader({
14693     totalProperty: "results",    // The property which contains the total dataset size (optional)
14694     root: "rows",                // The property which contains an Array of row objects
14695     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14696 }, RecordDef);
14697 </code></pre>
14698  * <p>
14699  * This would consume a JSON file like this:
14700  * <pre><code>
14701 { 'results': 2, 'rows': [
14702     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14703     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14704 }
14705 </code></pre>
14706  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14707  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14708  * paged from the remote server.
14709  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14710  * @cfg {String} root name of the property which contains the Array of row objects.
14711  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14712  * @cfg {Array} fields Array of field definition objects
14713  * @constructor
14714  * Create a new JsonReader
14715  * @param {Object} meta Metadata configuration options
14716  * @param {Object} recordType Either an Array of field definition objects,
14717  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14718  */
14719 Roo.data.JsonReader = function(meta, recordType){
14720     
14721     meta = meta || {};
14722     // set some defaults:
14723     Roo.applyIf(meta, {
14724         totalProperty: 'total',
14725         successProperty : 'success',
14726         root : 'data',
14727         id : 'id'
14728     });
14729     
14730     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14731 };
14732 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14733     
14734     readerType : 'Json',
14735     
14736     /**
14737      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14738      * Used by Store query builder to append _requestMeta to params.
14739      * 
14740      */
14741     metaFromRemote : false,
14742     /**
14743      * This method is only used by a DataProxy which has retrieved data from a remote server.
14744      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14745      * @return {Object} data A data block which is used by an Roo.data.Store object as
14746      * a cache of Roo.data.Records.
14747      */
14748     read : function(response){
14749         var json = response.responseText;
14750        
14751         var o = /* eval:var:o */ eval("("+json+")");
14752         if(!o) {
14753             throw {message: "JsonReader.read: Json object not found"};
14754         }
14755         
14756         if(o.metaData){
14757             
14758             delete this.ef;
14759             this.metaFromRemote = true;
14760             this.meta = o.metaData;
14761             this.recordType = Roo.data.Record.create(o.metaData.fields);
14762             this.onMetaChange(this.meta, this.recordType, o);
14763         }
14764         return this.readRecords(o);
14765     },
14766
14767     // private function a store will implement
14768     onMetaChange : function(meta, recordType, o){
14769
14770     },
14771
14772     /**
14773          * @ignore
14774          */
14775     simpleAccess: function(obj, subsc) {
14776         return obj[subsc];
14777     },
14778
14779         /**
14780          * @ignore
14781          */
14782     getJsonAccessor: function(){
14783         var re = /[\[\.]/;
14784         return function(expr) {
14785             try {
14786                 return(re.test(expr))
14787                     ? new Function("obj", "return obj." + expr)
14788                     : function(obj){
14789                         return obj[expr];
14790                     };
14791             } catch(e){}
14792             return Roo.emptyFn;
14793         };
14794     }(),
14795
14796     /**
14797      * Create a data block containing Roo.data.Records from an XML document.
14798      * @param {Object} o An object which contains an Array of row objects in the property specified
14799      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14800      * which contains the total size of the dataset.
14801      * @return {Object} data A data block which is used by an Roo.data.Store object as
14802      * a cache of Roo.data.Records.
14803      */
14804     readRecords : function(o){
14805         /**
14806          * After any data loads, the raw JSON data is available for further custom processing.
14807          * @type Object
14808          */
14809         this.o = o;
14810         var s = this.meta, Record = this.recordType,
14811             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14812
14813 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14814         if (!this.ef) {
14815             if(s.totalProperty) {
14816                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14817                 }
14818                 if(s.successProperty) {
14819                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14820                 }
14821                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14822                 if (s.id) {
14823                         var g = this.getJsonAccessor(s.id);
14824                         this.getId = function(rec) {
14825                                 var r = g(rec);  
14826                                 return (r === undefined || r === "") ? null : r;
14827                         };
14828                 } else {
14829                         this.getId = function(){return null;};
14830                 }
14831             this.ef = [];
14832             for(var jj = 0; jj < fl; jj++){
14833                 f = fi[jj];
14834                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14835                 this.ef[jj] = this.getJsonAccessor(map);
14836             }
14837         }
14838
14839         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14840         if(s.totalProperty){
14841             var vt = parseInt(this.getTotal(o), 10);
14842             if(!isNaN(vt)){
14843                 totalRecords = vt;
14844             }
14845         }
14846         if(s.successProperty){
14847             var vs = this.getSuccess(o);
14848             if(vs === false || vs === 'false'){
14849                 success = false;
14850             }
14851         }
14852         var records = [];
14853         for(var i = 0; i < c; i++){
14854                 var n = root[i];
14855             var values = {};
14856             var id = this.getId(n);
14857             for(var j = 0; j < fl; j++){
14858                 f = fi[j];
14859             var v = this.ef[j](n);
14860             if (!f.convert) {
14861                 Roo.log('missing convert for ' + f.name);
14862                 Roo.log(f);
14863                 continue;
14864             }
14865             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14866             }
14867             var record = new Record(values, id);
14868             record.json = n;
14869             records[i] = record;
14870         }
14871         return {
14872             raw : o,
14873             success : success,
14874             records : records,
14875             totalRecords : totalRecords
14876         };
14877     },
14878     // used when loading children.. @see loadDataFromChildren
14879     toLoadData: function(rec)
14880     {
14881         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14882         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14883         return { data : data, total : data.length };
14884         
14885     }
14886 });/*
14887  * Based on:
14888  * Ext JS Library 1.1.1
14889  * Copyright(c) 2006-2007, Ext JS, LLC.
14890  *
14891  * Originally Released Under LGPL - original licence link has changed is not relivant.
14892  *
14893  * Fork - LGPL
14894  * <script type="text/javascript">
14895  */
14896
14897 /**
14898  * @class Roo.data.ArrayReader
14899  * @extends Roo.data.DataReader
14900  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14901  * Each element of that Array represents a row of data fields. The
14902  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14903  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14904  * <p>
14905  * Example code:.
14906  * <pre><code>
14907 var RecordDef = Roo.data.Record.create([
14908     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14909     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14910 ]);
14911 var myReader = new Roo.data.ArrayReader({
14912     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14913 }, RecordDef);
14914 </code></pre>
14915  * <p>
14916  * This would consume an Array like this:
14917  * <pre><code>
14918 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14919   </code></pre>
14920  
14921  * @constructor
14922  * Create a new JsonReader
14923  * @param {Object} meta Metadata configuration options.
14924  * @param {Object|Array} recordType Either an Array of field definition objects
14925  * 
14926  * @cfg {Array} fields Array of field definition objects
14927  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14928  * as specified to {@link Roo.data.Record#create},
14929  * or an {@link Roo.data.Record} object
14930  *
14931  * 
14932  * created using {@link Roo.data.Record#create}.
14933  */
14934 Roo.data.ArrayReader = function(meta, recordType)
14935 {    
14936     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14937 };
14938
14939 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14940     
14941       /**
14942      * Create a data block containing Roo.data.Records from an XML document.
14943      * @param {Object} o An Array of row objects which represents the dataset.
14944      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14945      * a cache of Roo.data.Records.
14946      */
14947     readRecords : function(o)
14948     {
14949         var sid = this.meta ? this.meta.id : null;
14950         var recordType = this.recordType, fields = recordType.prototype.fields;
14951         var records = [];
14952         var root = o;
14953         for(var i = 0; i < root.length; i++){
14954                 var n = root[i];
14955             var values = {};
14956             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14957             for(var j = 0, jlen = fields.length; j < jlen; j++){
14958                 var f = fields.items[j];
14959                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14960                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14961                 v = f.convert(v);
14962                 values[f.name] = v;
14963             }
14964             var record = new recordType(values, id);
14965             record.json = n;
14966             records[records.length] = record;
14967         }
14968         return {
14969             records : records,
14970             totalRecords : records.length
14971         };
14972     },
14973     // used when loading children.. @see loadDataFromChildren
14974     toLoadData: function(rec)
14975     {
14976         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14977         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14978         
14979     }
14980     
14981     
14982 });/*
14983  * - LGPL
14984  * * 
14985  */
14986
14987 /**
14988  * @class Roo.bootstrap.ComboBox
14989  * @extends Roo.bootstrap.TriggerField
14990  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14991  * @cfg {Boolean} append (true|false) default false
14992  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14993  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14994  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14995  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14996  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14997  * @cfg {Boolean} animate default true
14998  * @cfg {Boolean} emptyResultText only for touch device
14999  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15000  * @cfg {String} emptyTitle default ''
15001  * @cfg {Number} width fixed with? experimental
15002  * @constructor
15003  * Create a new ComboBox.
15004  * @param {Object} config Configuration options
15005  */
15006 Roo.bootstrap.ComboBox = function(config){
15007     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15008     this.addEvents({
15009         /**
15010          * @event expand
15011          * Fires when the dropdown list is expanded
15012         * @param {Roo.bootstrap.ComboBox} combo This combo box
15013         */
15014         'expand' : true,
15015         /**
15016          * @event collapse
15017          * Fires when the dropdown list is collapsed
15018         * @param {Roo.bootstrap.ComboBox} combo This combo box
15019         */
15020         'collapse' : true,
15021         /**
15022          * @event beforeselect
15023          * Fires before a list item is selected. Return false to cancel the selection.
15024         * @param {Roo.bootstrap.ComboBox} combo This combo box
15025         * @param {Roo.data.Record} record The data record returned from the underlying store
15026         * @param {Number} index The index of the selected item in the dropdown list
15027         */
15028         'beforeselect' : true,
15029         /**
15030          * @event select
15031          * Fires when a list item is selected
15032         * @param {Roo.bootstrap.ComboBox} combo This combo box
15033         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15034         * @param {Number} index The index of the selected item in the dropdown list
15035         */
15036         'select' : true,
15037         /**
15038          * @event beforequery
15039          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15040          * The event object passed has these properties:
15041         * @param {Roo.bootstrap.ComboBox} combo This combo box
15042         * @param {String} query The query
15043         * @param {Boolean} forceAll true to force "all" query
15044         * @param {Boolean} cancel true to cancel the query
15045         * @param {Object} e The query event object
15046         */
15047         'beforequery': true,
15048          /**
15049          * @event add
15050          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15051         * @param {Roo.bootstrap.ComboBox} combo This combo box
15052         */
15053         'add' : true,
15054         /**
15055          * @event edit
15056          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15057         * @param {Roo.bootstrap.ComboBox} combo This combo box
15058         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15059         */
15060         'edit' : true,
15061         /**
15062          * @event remove
15063          * Fires when the remove value from the combobox array
15064         * @param {Roo.bootstrap.ComboBox} combo This combo box
15065         */
15066         'remove' : true,
15067         /**
15068          * @event afterremove
15069          * Fires when the remove value from the combobox array
15070         * @param {Roo.bootstrap.ComboBox} combo This combo box
15071         */
15072         'afterremove' : true,
15073         /**
15074          * @event specialfilter
15075          * Fires when specialfilter
15076             * @param {Roo.bootstrap.ComboBox} combo This combo box
15077             */
15078         'specialfilter' : true,
15079         /**
15080          * @event tick
15081          * Fires when tick the element
15082             * @param {Roo.bootstrap.ComboBox} combo This combo box
15083             */
15084         'tick' : true,
15085         /**
15086          * @event touchviewdisplay
15087          * Fires when touch view require special display (default is using displayField)
15088             * @param {Roo.bootstrap.ComboBox} combo This combo box
15089             * @param {Object} cfg set html .
15090             */
15091         'touchviewdisplay' : true
15092         
15093     });
15094     
15095     this.item = [];
15096     this.tickItems = [];
15097     
15098     this.selectedIndex = -1;
15099     if(this.mode == 'local'){
15100         if(config.queryDelay === undefined){
15101             this.queryDelay = 10;
15102         }
15103         if(config.minChars === undefined){
15104             this.minChars = 0;
15105         }
15106     }
15107 };
15108
15109 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15110      
15111     /**
15112      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15113      * rendering into an Roo.Editor, defaults to false)
15114      */
15115     /**
15116      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15117      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15118      */
15119     /**
15120      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15121      */
15122     /**
15123      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15124      * the dropdown list (defaults to undefined, with no header element)
15125      */
15126
15127      /**
15128      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15129      */
15130      
15131      /**
15132      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15133      */
15134     listWidth: undefined,
15135     /**
15136      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15137      * mode = 'remote' or 'text' if mode = 'local')
15138      */
15139     displayField: undefined,
15140     
15141     /**
15142      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15143      * mode = 'remote' or 'value' if mode = 'local'). 
15144      * Note: use of a valueField requires the user make a selection
15145      * in order for a value to be mapped.
15146      */
15147     valueField: undefined,
15148     /**
15149      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15150      */
15151     modalTitle : '',
15152     
15153     /**
15154      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15155      * field's data value (defaults to the underlying DOM element's name)
15156      */
15157     hiddenName: undefined,
15158     /**
15159      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15160      */
15161     listClass: '',
15162     /**
15163      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15164      */
15165     selectedClass: 'active',
15166     
15167     /**
15168      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15169      */
15170     shadow:'sides',
15171     /**
15172      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15173      * anchor positions (defaults to 'tl-bl')
15174      */
15175     listAlign: 'tl-bl?',
15176     /**
15177      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15178      */
15179     maxHeight: 300,
15180     /**
15181      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15182      * query specified by the allQuery config option (defaults to 'query')
15183      */
15184     triggerAction: 'query',
15185     /**
15186      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15187      * (defaults to 4, does not apply if editable = false)
15188      */
15189     minChars : 4,
15190     /**
15191      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15192      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15193      */
15194     typeAhead: false,
15195     /**
15196      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15197      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15198      */
15199     queryDelay: 500,
15200     /**
15201      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15202      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15203      */
15204     pageSize: 0,
15205     /**
15206      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15207      * when editable = true (defaults to false)
15208      */
15209     selectOnFocus:false,
15210     /**
15211      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15212      */
15213     queryParam: 'query',
15214     /**
15215      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15216      * when mode = 'remote' (defaults to 'Loading...')
15217      */
15218     loadingText: 'Loading...',
15219     /**
15220      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15221      */
15222     resizable: false,
15223     /**
15224      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15225      */
15226     handleHeight : 8,
15227     /**
15228      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15229      * traditional select (defaults to true)
15230      */
15231     editable: true,
15232     /**
15233      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15234      */
15235     allQuery: '',
15236     /**
15237      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15238      */
15239     mode: 'remote',
15240     /**
15241      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15242      * listWidth has a higher value)
15243      */
15244     minListWidth : 70,
15245     /**
15246      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15247      * allow the user to set arbitrary text into the field (defaults to false)
15248      */
15249     forceSelection:false,
15250     /**
15251      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15252      * if typeAhead = true (defaults to 250)
15253      */
15254     typeAheadDelay : 250,
15255     /**
15256      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15257      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15258      */
15259     valueNotFoundText : undefined,
15260     /**
15261      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15262      */
15263     blockFocus : false,
15264     
15265     /**
15266      * @cfg {Boolean} disableClear Disable showing of clear button.
15267      */
15268     disableClear : false,
15269     /**
15270      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15271      */
15272     alwaysQuery : false,
15273     
15274     /**
15275      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15276      */
15277     multiple : false,
15278     
15279     /**
15280      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15281      */
15282     invalidClass : "has-warning",
15283     
15284     /**
15285      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15286      */
15287     validClass : "has-success",
15288     
15289     /**
15290      * @cfg {Boolean} specialFilter (true|false) special filter default false
15291      */
15292     specialFilter : false,
15293     
15294     /**
15295      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15296      */
15297     mobileTouchView : true,
15298     
15299     /**
15300      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15301      */
15302     useNativeIOS : false,
15303     
15304     /**
15305      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15306      */
15307     mobile_restrict_height : false,
15308     
15309     ios_options : false,
15310     
15311     //private
15312     addicon : false,
15313     editicon: false,
15314     
15315     page: 0,
15316     hasQuery: false,
15317     append: false,
15318     loadNext: false,
15319     autoFocus : true,
15320     tickable : false,
15321     btnPosition : 'right',
15322     triggerList : true,
15323     showToggleBtn : true,
15324     animate : true,
15325     emptyResultText: 'Empty',
15326     triggerText : 'Select',
15327     emptyTitle : '',
15328     width : false,
15329     
15330     // element that contains real text value.. (when hidden is used..)
15331     
15332     getAutoCreate : function()
15333     {   
15334         var cfg = false;
15335         //render
15336         /*
15337          * Render classic select for iso
15338          */
15339         
15340         if(Roo.isIOS && this.useNativeIOS){
15341             cfg = this.getAutoCreateNativeIOS();
15342             return cfg;
15343         }
15344         
15345         /*
15346          * Touch Devices
15347          */
15348         
15349         if(Roo.isTouch && this.mobileTouchView){
15350             cfg = this.getAutoCreateTouchView();
15351             return cfg;;
15352         }
15353         
15354         /*
15355          *  Normal ComboBox
15356          */
15357         if(!this.tickable){
15358             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15359             return cfg;
15360         }
15361         
15362         /*
15363          *  ComboBox with tickable selections
15364          */
15365              
15366         var align = this.labelAlign || this.parentLabelAlign();
15367         
15368         cfg = {
15369             cls : 'form-group roo-combobox-tickable' //input-group
15370         };
15371         
15372         var btn_text_select = '';
15373         var btn_text_done = '';
15374         var btn_text_cancel = '';
15375         
15376         if (this.btn_text_show) {
15377             btn_text_select = 'Select';
15378             btn_text_done = 'Done';
15379             btn_text_cancel = 'Cancel'; 
15380         }
15381         
15382         var buttons = {
15383             tag : 'div',
15384             cls : 'tickable-buttons',
15385             cn : [
15386                 {
15387                     tag : 'button',
15388                     type : 'button',
15389                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15390                     //html : this.triggerText
15391                     html: btn_text_select
15392                 },
15393                 {
15394                     tag : 'button',
15395                     type : 'button',
15396                     name : 'ok',
15397                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15398                     //html : 'Done'
15399                     html: btn_text_done
15400                 },
15401                 {
15402                     tag : 'button',
15403                     type : 'button',
15404                     name : 'cancel',
15405                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15406                     //html : 'Cancel'
15407                     html: btn_text_cancel
15408                 }
15409             ]
15410         };
15411         
15412         if(this.editable){
15413             buttons.cn.unshift({
15414                 tag: 'input',
15415                 cls: 'roo-select2-search-field-input'
15416             });
15417         }
15418         
15419         var _this = this;
15420         
15421         Roo.each(buttons.cn, function(c){
15422             if (_this.size) {
15423                 c.cls += ' btn-' + _this.size;
15424             }
15425
15426             if (_this.disabled) {
15427                 c.disabled = true;
15428             }
15429         });
15430         
15431         var box = {
15432             tag: 'div',
15433             style : 'display: contents',
15434             cn: [
15435                 {
15436                     tag: 'input',
15437                     type : 'hidden',
15438                     cls: 'form-hidden-field'
15439                 },
15440                 {
15441                     tag: 'ul',
15442                     cls: 'roo-select2-choices',
15443                     cn:[
15444                         {
15445                             tag: 'li',
15446                             cls: 'roo-select2-search-field',
15447                             cn: [
15448                                 buttons
15449                             ]
15450                         }
15451                     ]
15452                 }
15453             ]
15454         };
15455         
15456         var combobox = {
15457             cls: 'roo-select2-container input-group roo-select2-container-multi',
15458             cn: [
15459                 
15460                 box
15461 //                {
15462 //                    tag: 'ul',
15463 //                    cls: 'typeahead typeahead-long dropdown-menu',
15464 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15465 //                }
15466             ]
15467         };
15468         
15469         if(this.hasFeedback && !this.allowBlank){
15470             
15471             var feedback = {
15472                 tag: 'span',
15473                 cls: 'glyphicon form-control-feedback'
15474             };
15475
15476             combobox.cn.push(feedback);
15477         }
15478         
15479         
15480         
15481         var indicator = {
15482             tag : 'i',
15483             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15484             tooltip : 'This field is required'
15485         };
15486         if (Roo.bootstrap.version == 4) {
15487             indicator = {
15488                 tag : 'i',
15489                 style : 'display:none'
15490             };
15491         }
15492         if (align ==='left' && this.fieldLabel.length) {
15493             
15494             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15495             
15496             cfg.cn = [
15497                 indicator,
15498                 {
15499                     tag: 'label',
15500                     'for' :  id,
15501                     cls : 'control-label col-form-label',
15502                     html : this.fieldLabel
15503
15504                 },
15505                 {
15506                     cls : "", 
15507                     cn: [
15508                         combobox
15509                     ]
15510                 }
15511
15512             ];
15513             
15514             var labelCfg = cfg.cn[1];
15515             var contentCfg = cfg.cn[2];
15516             
15517
15518             if(this.indicatorpos == 'right'){
15519                 
15520                 cfg.cn = [
15521                     {
15522                         tag: 'label',
15523                         'for' :  id,
15524                         cls : 'control-label col-form-label',
15525                         cn : [
15526                             {
15527                                 tag : 'span',
15528                                 html : this.fieldLabel
15529                             },
15530                             indicator
15531                         ]
15532                     },
15533                     {
15534                         cls : "",
15535                         cn: [
15536                             combobox
15537                         ]
15538                     }
15539
15540                 ];
15541                 
15542                 
15543                 
15544                 labelCfg = cfg.cn[0];
15545                 contentCfg = cfg.cn[1];
15546             
15547             }
15548             
15549             if(this.labelWidth > 12){
15550                 labelCfg.style = "width: " + this.labelWidth + 'px';
15551             }
15552             if(this.width * 1 > 0){
15553                 contentCfg.style = "width: " + this.width + 'px';
15554             }
15555             if(this.labelWidth < 13 && this.labelmd == 0){
15556                 this.labelmd = this.labelWidth;
15557             }
15558             
15559             if(this.labellg > 0){
15560                 labelCfg.cls += ' col-lg-' + this.labellg;
15561                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15562             }
15563             
15564             if(this.labelmd > 0){
15565                 labelCfg.cls += ' col-md-' + this.labelmd;
15566                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15567             }
15568             
15569             if(this.labelsm > 0){
15570                 labelCfg.cls += ' col-sm-' + this.labelsm;
15571                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15572             }
15573             
15574             if(this.labelxs > 0){
15575                 labelCfg.cls += ' col-xs-' + this.labelxs;
15576                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15577             }
15578                 
15579                 
15580         } else if ( this.fieldLabel.length) {
15581 //                Roo.log(" label");
15582                  cfg.cn = [
15583                    indicator,
15584                     {
15585                         tag: 'label',
15586                         //cls : 'input-group-addon',
15587                         html : this.fieldLabel
15588                     },
15589                     combobox
15590                 ];
15591                 
15592                 if(this.indicatorpos == 'right'){
15593                     cfg.cn = [
15594                         {
15595                             tag: 'label',
15596                             //cls : 'input-group-addon',
15597                             html : this.fieldLabel
15598                         },
15599                         indicator,
15600                         combobox
15601                     ];
15602                     
15603                 }
15604
15605         } else {
15606             
15607 //                Roo.log(" no label && no align");
15608                 cfg = combobox
15609                      
15610                 
15611         }
15612          
15613         var settings=this;
15614         ['xs','sm','md','lg'].map(function(size){
15615             if (settings[size]) {
15616                 cfg.cls += ' col-' + size + '-' + settings[size];
15617             }
15618         });
15619         
15620         return cfg;
15621         
15622     },
15623     
15624     _initEventsCalled : false,
15625     
15626     // private
15627     initEvents: function()
15628     {   
15629         if (this._initEventsCalled) { // as we call render... prevent looping...
15630             return;
15631         }
15632         this._initEventsCalled = true;
15633         
15634         if (!this.store) {
15635             throw "can not find store for combo";
15636         }
15637         
15638         this.indicator = this.indicatorEl();
15639         
15640         this.store = Roo.factory(this.store, Roo.data);
15641         this.store.parent = this;
15642         
15643         // if we are building from html. then this element is so complex, that we can not really
15644         // use the rendered HTML.
15645         // so we have to trash and replace the previous code.
15646         if (Roo.XComponent.build_from_html) {
15647             // remove this element....
15648             var e = this.el.dom, k=0;
15649             while (e ) { e = e.previousSibling;  ++k;}
15650
15651             this.el.remove();
15652             
15653             this.el=false;
15654             this.rendered = false;
15655             
15656             this.render(this.parent().getChildContainer(true), k);
15657         }
15658         
15659         if(Roo.isIOS && this.useNativeIOS){
15660             this.initIOSView();
15661             return;
15662         }
15663         
15664         /*
15665          * Touch Devices
15666          */
15667         
15668         if(Roo.isTouch && this.mobileTouchView){
15669             this.initTouchView();
15670             return;
15671         }
15672         
15673         if(this.tickable){
15674             this.initTickableEvents();
15675             return;
15676         }
15677         
15678         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15679         
15680         if(this.hiddenName){
15681             
15682             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15683             
15684             this.hiddenField.dom.value =
15685                 this.hiddenValue !== undefined ? this.hiddenValue :
15686                 this.value !== undefined ? this.value : '';
15687
15688             // prevent input submission
15689             this.el.dom.removeAttribute('name');
15690             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15691              
15692              
15693         }
15694         //if(Roo.isGecko){
15695         //    this.el.dom.setAttribute('autocomplete', 'off');
15696         //}
15697         
15698         var cls = 'x-combo-list';
15699         
15700         //this.list = new Roo.Layer({
15701         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15702         //});
15703         
15704         var _this = this;
15705         
15706         (function(){
15707             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15708             _this.list.setWidth(lw);
15709         }).defer(100);
15710         
15711         this.list.on('mouseover', this.onViewOver, this);
15712         this.list.on('mousemove', this.onViewMove, this);
15713         this.list.on('scroll', this.onViewScroll, this);
15714         
15715         /*
15716         this.list.swallowEvent('mousewheel');
15717         this.assetHeight = 0;
15718
15719         if(this.title){
15720             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15721             this.assetHeight += this.header.getHeight();
15722         }
15723
15724         this.innerList = this.list.createChild({cls:cls+'-inner'});
15725         this.innerList.on('mouseover', this.onViewOver, this);
15726         this.innerList.on('mousemove', this.onViewMove, this);
15727         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15728         
15729         if(this.allowBlank && !this.pageSize && !this.disableClear){
15730             this.footer = this.list.createChild({cls:cls+'-ft'});
15731             this.pageTb = new Roo.Toolbar(this.footer);
15732            
15733         }
15734         if(this.pageSize){
15735             this.footer = this.list.createChild({cls:cls+'-ft'});
15736             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15737                     {pageSize: this.pageSize});
15738             
15739         }
15740         
15741         if (this.pageTb && this.allowBlank && !this.disableClear) {
15742             var _this = this;
15743             this.pageTb.add(new Roo.Toolbar.Fill(), {
15744                 cls: 'x-btn-icon x-btn-clear',
15745                 text: '&#160;',
15746                 handler: function()
15747                 {
15748                     _this.collapse();
15749                     _this.clearValue();
15750                     _this.onSelect(false, -1);
15751                 }
15752             });
15753         }
15754         if (this.footer) {
15755             this.assetHeight += this.footer.getHeight();
15756         }
15757         */
15758             
15759         if(!this.tpl){
15760             this.tpl = Roo.bootstrap.version == 4 ?
15761                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15762                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15763         }
15764
15765         this.view = new Roo.View(this.list, this.tpl, {
15766             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15767         });
15768         //this.view.wrapEl.setDisplayed(false);
15769         this.view.on('click', this.onViewClick, this);
15770         
15771         
15772         this.store.on('beforeload', this.onBeforeLoad, this);
15773         this.store.on('load', this.onLoad, this);
15774         this.store.on('loadexception', this.onLoadException, this);
15775         /*
15776         if(this.resizable){
15777             this.resizer = new Roo.Resizable(this.list,  {
15778                pinned:true, handles:'se'
15779             });
15780             this.resizer.on('resize', function(r, w, h){
15781                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15782                 this.listWidth = w;
15783                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15784                 this.restrictHeight();
15785             }, this);
15786             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15787         }
15788         */
15789         if(!this.editable){
15790             this.editable = true;
15791             this.setEditable(false);
15792         }
15793         
15794         /*
15795         
15796         if (typeof(this.events.add.listeners) != 'undefined') {
15797             
15798             this.addicon = this.wrap.createChild(
15799                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15800        
15801             this.addicon.on('click', function(e) {
15802                 this.fireEvent('add', this);
15803             }, this);
15804         }
15805         if (typeof(this.events.edit.listeners) != 'undefined') {
15806             
15807             this.editicon = this.wrap.createChild(
15808                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15809             if (this.addicon) {
15810                 this.editicon.setStyle('margin-left', '40px');
15811             }
15812             this.editicon.on('click', function(e) {
15813                 
15814                 // we fire even  if inothing is selected..
15815                 this.fireEvent('edit', this, this.lastData );
15816                 
15817             }, this);
15818         }
15819         */
15820         
15821         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15822             "up" : function(e){
15823                 this.inKeyMode = true;
15824                 this.selectPrev();
15825             },
15826
15827             "down" : function(e){
15828                 if(!this.isExpanded()){
15829                     this.onTriggerClick();
15830                 }else{
15831                     this.inKeyMode = true;
15832                     this.selectNext();
15833                 }
15834             },
15835
15836             "enter" : function(e){
15837 //                this.onViewClick();
15838                 //return true;
15839                 this.collapse();
15840                 
15841                 if(this.fireEvent("specialkey", this, e)){
15842                     this.onViewClick(false);
15843                 }
15844                 
15845                 return true;
15846             },
15847
15848             "esc" : function(e){
15849                 this.collapse();
15850             },
15851
15852             "tab" : function(e){
15853                 this.collapse();
15854                 
15855                 if(this.fireEvent("specialkey", this, e)){
15856                     this.onViewClick(false);
15857                 }
15858                 
15859                 return true;
15860             },
15861
15862             scope : this,
15863
15864             doRelay : function(foo, bar, hname){
15865                 if(hname == 'down' || this.scope.isExpanded()){
15866                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15867                 }
15868                 return true;
15869             },
15870
15871             forceKeyDown: true
15872         });
15873         
15874         
15875         this.queryDelay = Math.max(this.queryDelay || 10,
15876                 this.mode == 'local' ? 10 : 250);
15877         
15878         
15879         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15880         
15881         if(this.typeAhead){
15882             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15883         }
15884         if(this.editable !== false){
15885             this.inputEl().on("keyup", this.onKeyUp, this);
15886         }
15887         if(this.forceSelection){
15888             this.inputEl().on('blur', this.doForce, this);
15889         }
15890         
15891         if(this.multiple){
15892             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15893             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15894         }
15895     },
15896     
15897     initTickableEvents: function()
15898     {   
15899         this.createList();
15900         
15901         if(this.hiddenName){
15902             
15903             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15904             
15905             this.hiddenField.dom.value =
15906                 this.hiddenValue !== undefined ? this.hiddenValue :
15907                 this.value !== undefined ? this.value : '';
15908
15909             // prevent input submission
15910             this.el.dom.removeAttribute('name');
15911             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15912              
15913              
15914         }
15915         
15916 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15917         
15918         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15919         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15920         if(this.triggerList){
15921             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15922         }
15923          
15924         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15925         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15926         
15927         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15928         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15929         
15930         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15931         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15932         
15933         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15934         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15935         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15936         
15937         this.okBtn.hide();
15938         this.cancelBtn.hide();
15939         
15940         var _this = this;
15941         
15942         (function(){
15943             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15944             _this.list.setWidth(lw);
15945         }).defer(100);
15946         
15947         this.list.on('mouseover', this.onViewOver, this);
15948         this.list.on('mousemove', this.onViewMove, this);
15949         
15950         this.list.on('scroll', this.onViewScroll, this);
15951         
15952         if(!this.tpl){
15953             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15954                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15955         }
15956
15957         this.view = new Roo.View(this.list, this.tpl, {
15958             singleSelect:true,
15959             tickable:true,
15960             parent:this,
15961             store: this.store,
15962             selectedClass: this.selectedClass
15963         });
15964         
15965         //this.view.wrapEl.setDisplayed(false);
15966         this.view.on('click', this.onViewClick, this);
15967         
15968         
15969         
15970         this.store.on('beforeload', this.onBeforeLoad, this);
15971         this.store.on('load', this.onLoad, this);
15972         this.store.on('loadexception', this.onLoadException, this);
15973         
15974         if(this.editable){
15975             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15976                 "up" : function(e){
15977                     this.inKeyMode = true;
15978                     this.selectPrev();
15979                 },
15980
15981                 "down" : function(e){
15982                     this.inKeyMode = true;
15983                     this.selectNext();
15984                 },
15985
15986                 "enter" : function(e){
15987                     if(this.fireEvent("specialkey", this, e)){
15988                         this.onViewClick(false);
15989                     }
15990                     
15991                     return true;
15992                 },
15993
15994                 "esc" : function(e){
15995                     this.onTickableFooterButtonClick(e, false, false);
15996                 },
15997
15998                 "tab" : function(e){
15999                     this.fireEvent("specialkey", this, e);
16000                     
16001                     this.onTickableFooterButtonClick(e, false, false);
16002                     
16003                     return true;
16004                 },
16005
16006                 scope : this,
16007
16008                 doRelay : function(e, fn, key){
16009                     if(this.scope.isExpanded()){
16010                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16011                     }
16012                     return true;
16013                 },
16014
16015                 forceKeyDown: true
16016             });
16017         }
16018         
16019         this.queryDelay = Math.max(this.queryDelay || 10,
16020                 this.mode == 'local' ? 10 : 250);
16021         
16022         
16023         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16024         
16025         if(this.typeAhead){
16026             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16027         }
16028         
16029         if(this.editable !== false){
16030             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16031         }
16032         
16033         this.indicator = this.indicatorEl();
16034         
16035         if(this.indicator){
16036             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16037             this.indicator.hide();
16038         }
16039         
16040     },
16041
16042     onDestroy : function(){
16043         if(this.view){
16044             this.view.setStore(null);
16045             this.view.el.removeAllListeners();
16046             this.view.el.remove();
16047             this.view.purgeListeners();
16048         }
16049         if(this.list){
16050             this.list.dom.innerHTML  = '';
16051         }
16052         
16053         if(this.store){
16054             this.store.un('beforeload', this.onBeforeLoad, this);
16055             this.store.un('load', this.onLoad, this);
16056             this.store.un('loadexception', this.onLoadException, this);
16057         }
16058         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16059     },
16060
16061     // private
16062     fireKey : function(e){
16063         if(e.isNavKeyPress() && !this.list.isVisible()){
16064             this.fireEvent("specialkey", this, e);
16065         }
16066     },
16067
16068     // private
16069     onResize: function(w, h)
16070     {
16071         
16072         
16073 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16074 //        
16075 //        if(typeof w != 'number'){
16076 //            // we do not handle it!?!?
16077 //            return;
16078 //        }
16079 //        var tw = this.trigger.getWidth();
16080 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16081 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16082 //        var x = w - tw;
16083 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16084 //            
16085 //        //this.trigger.setStyle('left', x+'px');
16086 //        
16087 //        if(this.list && this.listWidth === undefined){
16088 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16089 //            this.list.setWidth(lw);
16090 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16091 //        }
16092         
16093     
16094         
16095     },
16096
16097     /**
16098      * Allow or prevent the user from directly editing the field text.  If false is passed,
16099      * the user will only be able to select from the items defined in the dropdown list.  This method
16100      * is the runtime equivalent of setting the 'editable' config option at config time.
16101      * @param {Boolean} value True to allow the user to directly edit the field text
16102      */
16103     setEditable : function(value){
16104         if(value == this.editable){
16105             return;
16106         }
16107         this.editable = value;
16108         if(!value){
16109             this.inputEl().dom.setAttribute('readOnly', true);
16110             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16111             this.inputEl().addClass('x-combo-noedit');
16112         }else{
16113             this.inputEl().dom.setAttribute('readOnly', false);
16114             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16115             this.inputEl().removeClass('x-combo-noedit');
16116         }
16117     },
16118
16119     // private
16120     
16121     onBeforeLoad : function(combo,opts){
16122         if(!this.hasFocus){
16123             return;
16124         }
16125          if (!opts.add) {
16126             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16127          }
16128         this.restrictHeight();
16129         this.selectedIndex = -1;
16130     },
16131
16132     // private
16133     onLoad : function(){
16134         
16135         this.hasQuery = false;
16136         
16137         if(!this.hasFocus){
16138             return;
16139         }
16140         
16141         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16142             this.loading.hide();
16143         }
16144         
16145         if(this.store.getCount() > 0){
16146             
16147             this.expand();
16148             this.restrictHeight();
16149             if(this.lastQuery == this.allQuery){
16150                 if(this.editable && !this.tickable){
16151                     this.inputEl().dom.select();
16152                 }
16153                 
16154                 if(
16155                     !this.selectByValue(this.value, true) &&
16156                     this.autoFocus && 
16157                     (
16158                         !this.store.lastOptions ||
16159                         typeof(this.store.lastOptions.add) == 'undefined' || 
16160                         this.store.lastOptions.add != true
16161                     )
16162                 ){
16163                     this.select(0, true);
16164                 }
16165             }else{
16166                 if(this.autoFocus){
16167                     this.selectNext();
16168                 }
16169                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16170                     this.taTask.delay(this.typeAheadDelay);
16171                 }
16172             }
16173         }else{
16174             this.onEmptyResults();
16175         }
16176         
16177         //this.el.focus();
16178     },
16179     // private
16180     onLoadException : function()
16181     {
16182         this.hasQuery = false;
16183         
16184         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16185             this.loading.hide();
16186         }
16187         
16188         if(this.tickable && this.editable){
16189             return;
16190         }
16191         
16192         this.collapse();
16193         // only causes errors at present
16194         //Roo.log(this.store.reader.jsonData);
16195         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16196             // fixme
16197             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16198         //}
16199         
16200         
16201     },
16202     // private
16203     onTypeAhead : function(){
16204         if(this.store.getCount() > 0){
16205             var r = this.store.getAt(0);
16206             var newValue = r.data[this.displayField];
16207             var len = newValue.length;
16208             var selStart = this.getRawValue().length;
16209             
16210             if(selStart != len){
16211                 this.setRawValue(newValue);
16212                 this.selectText(selStart, newValue.length);
16213             }
16214         }
16215     },
16216
16217     // private
16218     onSelect : function(record, index){
16219         
16220         if(this.fireEvent('beforeselect', this, record, index) !== false){
16221         
16222             this.setFromData(index > -1 ? record.data : false);
16223             
16224             this.collapse();
16225             this.fireEvent('select', this, record, index);
16226         }
16227     },
16228
16229     /**
16230      * Returns the currently selected field value or empty string if no value is set.
16231      * @return {String} value The selected value
16232      */
16233     getValue : function()
16234     {
16235         if(Roo.isIOS && this.useNativeIOS){
16236             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16237         }
16238         
16239         if(this.multiple){
16240             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16241         }
16242         
16243         if(this.valueField){
16244             return typeof this.value != 'undefined' ? this.value : '';
16245         }else{
16246             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16247         }
16248     },
16249     
16250     getRawValue : function()
16251     {
16252         if(Roo.isIOS && this.useNativeIOS){
16253             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16254         }
16255         
16256         var v = this.inputEl().getValue();
16257         
16258         return v;
16259     },
16260
16261     /**
16262      * Clears any text/value currently set in the field
16263      */
16264     clearValue : function(){
16265         
16266         if(this.hiddenField){
16267             this.hiddenField.dom.value = '';
16268         }
16269         this.value = '';
16270         this.setRawValue('');
16271         this.lastSelectionText = '';
16272         this.lastData = false;
16273         
16274         var close = this.closeTriggerEl();
16275         
16276         if(close){
16277             close.hide();
16278         }
16279         
16280         this.validate();
16281         
16282     },
16283
16284     /**
16285      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16286      * will be displayed in the field.  If the value does not match the data value of an existing item,
16287      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16288      * Otherwise the field will be blank (although the value will still be set).
16289      * @param {String} value The value to match
16290      */
16291     setValue : function(v)
16292     {
16293         if(Roo.isIOS && this.useNativeIOS){
16294             this.setIOSValue(v);
16295             return;
16296         }
16297         
16298         if(this.multiple){
16299             this.syncValue();
16300             return;
16301         }
16302         
16303         var text = v;
16304         if(this.valueField){
16305             var r = this.findRecord(this.valueField, v);
16306             if(r){
16307                 text = r.data[this.displayField];
16308             }else if(this.valueNotFoundText !== undefined){
16309                 text = this.valueNotFoundText;
16310             }
16311         }
16312         this.lastSelectionText = text;
16313         if(this.hiddenField){
16314             this.hiddenField.dom.value = v;
16315         }
16316         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16317         this.value = v;
16318         
16319         var close = this.closeTriggerEl();
16320         
16321         if(close){
16322             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16323         }
16324         
16325         this.validate();
16326     },
16327     /**
16328      * @property {Object} the last set data for the element
16329      */
16330     
16331     lastData : false,
16332     /**
16333      * Sets the value of the field based on a object which is related to the record format for the store.
16334      * @param {Object} value the value to set as. or false on reset?
16335      */
16336     setFromData : function(o){
16337         
16338         if(this.multiple){
16339             this.addItem(o);
16340             return;
16341         }
16342             
16343         var dv = ''; // display value
16344         var vv = ''; // value value..
16345         this.lastData = o;
16346         if (this.displayField) {
16347             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16348         } else {
16349             // this is an error condition!!!
16350             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16351         }
16352         
16353         if(this.valueField){
16354             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16355         }
16356         
16357         var close = this.closeTriggerEl();
16358         
16359         if(close){
16360             if(dv.length || vv * 1 > 0){
16361                 close.show() ;
16362                 this.blockFocus=true;
16363             } else {
16364                 close.hide();
16365             }             
16366         }
16367         
16368         if(this.hiddenField){
16369             this.hiddenField.dom.value = vv;
16370             
16371             this.lastSelectionText = dv;
16372             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16373             this.value = vv;
16374             return;
16375         }
16376         // no hidden field.. - we store the value in 'value', but still display
16377         // display field!!!!
16378         this.lastSelectionText = dv;
16379         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16380         this.value = vv;
16381         
16382         
16383         
16384     },
16385     // private
16386     reset : function(){
16387         // overridden so that last data is reset..
16388         
16389         if(this.multiple){
16390             this.clearItem();
16391             return;
16392         }
16393         
16394         this.setValue(this.originalValue);
16395         //this.clearInvalid();
16396         this.lastData = false;
16397         if (this.view) {
16398             this.view.clearSelections();
16399         }
16400         
16401         this.validate();
16402     },
16403     // private
16404     findRecord : function(prop, value){
16405         var record;
16406         if(this.store.getCount() > 0){
16407             this.store.each(function(r){
16408                 if(r.data[prop] == value){
16409                     record = r;
16410                     return false;
16411                 }
16412                 return true;
16413             });
16414         }
16415         return record;
16416     },
16417     
16418     getName: function()
16419     {
16420         // returns hidden if it's set..
16421         if (!this.rendered) {return ''};
16422         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16423         
16424     },
16425     // private
16426     onViewMove : function(e, t){
16427         this.inKeyMode = false;
16428     },
16429
16430     // private
16431     onViewOver : function(e, t){
16432         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16433             return;
16434         }
16435         var item = this.view.findItemFromChild(t);
16436         
16437         if(item){
16438             var index = this.view.indexOf(item);
16439             this.select(index, false);
16440         }
16441     },
16442
16443     // private
16444     onViewClick : function(view, doFocus, el, e)
16445     {
16446         var index = this.view.getSelectedIndexes()[0];
16447         
16448         var r = this.store.getAt(index);
16449         
16450         if(this.tickable){
16451             
16452             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16453                 return;
16454             }
16455             
16456             var rm = false;
16457             var _this = this;
16458             
16459             Roo.each(this.tickItems, function(v,k){
16460                 
16461                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16462                     Roo.log(v);
16463                     _this.tickItems.splice(k, 1);
16464                     
16465                     if(typeof(e) == 'undefined' && view == false){
16466                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16467                     }
16468                     
16469                     rm = true;
16470                     return;
16471                 }
16472             });
16473             
16474             if(rm){
16475                 return;
16476             }
16477             
16478             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16479                 this.tickItems.push(r.data);
16480             }
16481             
16482             if(typeof(e) == 'undefined' && view == false){
16483                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16484             }
16485                     
16486             return;
16487         }
16488         
16489         if(r){
16490             this.onSelect(r, index);
16491         }
16492         if(doFocus !== false && !this.blockFocus){
16493             this.inputEl().focus();
16494         }
16495     },
16496
16497     // private
16498     restrictHeight : function(){
16499         //this.innerList.dom.style.height = '';
16500         //var inner = this.innerList.dom;
16501         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16502         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16503         //this.list.beginUpdate();
16504         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16505         this.list.alignTo(this.inputEl(), this.listAlign);
16506         this.list.alignTo(this.inputEl(), this.listAlign);
16507         //this.list.endUpdate();
16508     },
16509
16510     // private
16511     onEmptyResults : function(){
16512         
16513         if(this.tickable && this.editable){
16514             this.hasFocus = false;
16515             this.restrictHeight();
16516             return;
16517         }
16518         
16519         this.collapse();
16520     },
16521
16522     /**
16523      * Returns true if the dropdown list is expanded, else false.
16524      */
16525     isExpanded : function(){
16526         return this.list.isVisible();
16527     },
16528
16529     /**
16530      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16531      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16532      * @param {String} value The data value of the item to select
16533      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16534      * selected item if it is not currently in view (defaults to true)
16535      * @return {Boolean} True if the value matched an item in the list, else false
16536      */
16537     selectByValue : function(v, scrollIntoView){
16538         if(v !== undefined && v !== null){
16539             var r = this.findRecord(this.valueField || this.displayField, v);
16540             if(r){
16541                 this.select(this.store.indexOf(r), scrollIntoView);
16542                 return true;
16543             }
16544         }
16545         return false;
16546     },
16547
16548     /**
16549      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16550      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16551      * @param {Number} index The zero-based index of the list item to select
16552      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16553      * selected item if it is not currently in view (defaults to true)
16554      */
16555     select : function(index, scrollIntoView){
16556         this.selectedIndex = index;
16557         this.view.select(index);
16558         if(scrollIntoView !== false){
16559             var el = this.view.getNode(index);
16560             /*
16561              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16562              */
16563             if(el){
16564                 this.list.scrollChildIntoView(el, false);
16565             }
16566         }
16567     },
16568
16569     // private
16570     selectNext : function(){
16571         var ct = this.store.getCount();
16572         if(ct > 0){
16573             if(this.selectedIndex == -1){
16574                 this.select(0);
16575             }else if(this.selectedIndex < ct-1){
16576                 this.select(this.selectedIndex+1);
16577             }
16578         }
16579     },
16580
16581     // private
16582     selectPrev : function(){
16583         var ct = this.store.getCount();
16584         if(ct > 0){
16585             if(this.selectedIndex == -1){
16586                 this.select(0);
16587             }else if(this.selectedIndex != 0){
16588                 this.select(this.selectedIndex-1);
16589             }
16590         }
16591     },
16592
16593     // private
16594     onKeyUp : function(e){
16595         if(this.editable !== false && !e.isSpecialKey()){
16596             this.lastKey = e.getKey();
16597             this.dqTask.delay(this.queryDelay);
16598         }
16599     },
16600
16601     // private
16602     validateBlur : function(){
16603         return !this.list || !this.list.isVisible();   
16604     },
16605
16606     // private
16607     initQuery : function(){
16608         
16609         var v = this.getRawValue();
16610         
16611         if(this.tickable && this.editable){
16612             v = this.tickableInputEl().getValue();
16613         }
16614         
16615         this.doQuery(v);
16616     },
16617
16618     // private
16619     doForce : function(){
16620         if(this.inputEl().dom.value.length > 0){
16621             this.inputEl().dom.value =
16622                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16623              
16624         }
16625     },
16626
16627     /**
16628      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16629      * query allowing the query action to be canceled if needed.
16630      * @param {String} query The SQL query to execute
16631      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16632      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16633      * saved in the current store (defaults to false)
16634      */
16635     doQuery : function(q, forceAll){
16636         
16637         if(q === undefined || q === null){
16638             q = '';
16639         }
16640         var qe = {
16641             query: q,
16642             forceAll: forceAll,
16643             combo: this,
16644             cancel:false
16645         };
16646         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16647             return false;
16648         }
16649         q = qe.query;
16650         
16651         forceAll = qe.forceAll;
16652         if(forceAll === true || (q.length >= this.minChars)){
16653             
16654             this.hasQuery = true;
16655             
16656             if(this.lastQuery != q || this.alwaysQuery){
16657                 this.lastQuery = q;
16658                 if(this.mode == 'local'){
16659                     this.selectedIndex = -1;
16660                     if(forceAll){
16661                         this.store.clearFilter();
16662                     }else{
16663                         
16664                         if(this.specialFilter){
16665                             this.fireEvent('specialfilter', this);
16666                             this.onLoad();
16667                             return;
16668                         }
16669                         
16670                         this.store.filter(this.displayField, q);
16671                     }
16672                     
16673                     this.store.fireEvent("datachanged", this.store);
16674                     
16675                     this.onLoad();
16676                     
16677                     
16678                 }else{
16679                     
16680                     this.store.baseParams[this.queryParam] = q;
16681                     
16682                     var options = {params : this.getParams(q)};
16683                     
16684                     if(this.loadNext){
16685                         options.add = true;
16686                         options.params.start = this.page * this.pageSize;
16687                     }
16688                     
16689                     this.store.load(options);
16690                     
16691                     /*
16692                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16693                      *  we should expand the list on onLoad
16694                      *  so command out it
16695                      */
16696 //                    this.expand();
16697                 }
16698             }else{
16699                 this.selectedIndex = -1;
16700                 this.onLoad();   
16701             }
16702         }
16703         
16704         this.loadNext = false;
16705     },
16706     
16707     // private
16708     getParams : function(q){
16709         var p = {};
16710         //p[this.queryParam] = q;
16711         
16712         if(this.pageSize){
16713             p.start = 0;
16714             p.limit = this.pageSize;
16715         }
16716         return p;
16717     },
16718
16719     /**
16720      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16721      */
16722     collapse : function(){
16723         if(!this.isExpanded()){
16724             return;
16725         }
16726         
16727         this.list.hide();
16728         
16729         this.hasFocus = false;
16730         
16731         if(this.tickable){
16732             this.okBtn.hide();
16733             this.cancelBtn.hide();
16734             this.trigger.show();
16735             
16736             if(this.editable){
16737                 this.tickableInputEl().dom.value = '';
16738                 this.tickableInputEl().blur();
16739             }
16740             
16741         }
16742         
16743         Roo.get(document).un('mousedown', this.collapseIf, this);
16744         Roo.get(document).un('mousewheel', this.collapseIf, this);
16745         if (!this.editable) {
16746             Roo.get(document).un('keydown', this.listKeyPress, this);
16747         }
16748         this.fireEvent('collapse', this);
16749         
16750         this.validate();
16751     },
16752
16753     // private
16754     collapseIf : function(e){
16755         var in_combo  = e.within(this.el);
16756         var in_list =  e.within(this.list);
16757         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16758         
16759         if (in_combo || in_list || is_list) {
16760             //e.stopPropagation();
16761             return;
16762         }
16763         
16764         if(this.tickable){
16765             this.onTickableFooterButtonClick(e, false, false);
16766         }
16767
16768         this.collapse();
16769         
16770     },
16771
16772     /**
16773      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16774      */
16775     expand : function(){
16776        
16777         if(this.isExpanded() || !this.hasFocus){
16778             return;
16779         }
16780         
16781         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16782         this.list.setWidth(lw);
16783         
16784         Roo.log('expand');
16785         
16786         this.list.show();
16787         
16788         this.restrictHeight();
16789         
16790         if(this.tickable){
16791             
16792             this.tickItems = Roo.apply([], this.item);
16793             
16794             this.okBtn.show();
16795             this.cancelBtn.show();
16796             this.trigger.hide();
16797             
16798             if(this.editable){
16799                 this.tickableInputEl().focus();
16800             }
16801             
16802         }
16803         
16804         Roo.get(document).on('mousedown', this.collapseIf, this);
16805         Roo.get(document).on('mousewheel', this.collapseIf, this);
16806         if (!this.editable) {
16807             Roo.get(document).on('keydown', this.listKeyPress, this);
16808         }
16809         
16810         this.fireEvent('expand', this);
16811     },
16812
16813     // private
16814     // Implements the default empty TriggerField.onTriggerClick function
16815     onTriggerClick : function(e)
16816     {
16817         Roo.log('trigger click');
16818         
16819         if(this.disabled || !this.triggerList){
16820             return;
16821         }
16822         
16823         this.page = 0;
16824         this.loadNext = false;
16825         
16826         if(this.isExpanded()){
16827             this.collapse();
16828             if (!this.blockFocus) {
16829                 this.inputEl().focus();
16830             }
16831             
16832         }else {
16833             this.hasFocus = true;
16834             if(this.triggerAction == 'all') {
16835                 this.doQuery(this.allQuery, true);
16836             } else {
16837                 this.doQuery(this.getRawValue());
16838             }
16839             if (!this.blockFocus) {
16840                 this.inputEl().focus();
16841             }
16842         }
16843     },
16844     
16845     onTickableTriggerClick : function(e)
16846     {
16847         if(this.disabled){
16848             return;
16849         }
16850         
16851         this.page = 0;
16852         this.loadNext = false;
16853         this.hasFocus = true;
16854         
16855         if(this.triggerAction == 'all') {
16856             this.doQuery(this.allQuery, true);
16857         } else {
16858             this.doQuery(this.getRawValue());
16859         }
16860     },
16861     
16862     onSearchFieldClick : function(e)
16863     {
16864         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16865             this.onTickableFooterButtonClick(e, false, false);
16866             return;
16867         }
16868         
16869         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16870             return;
16871         }
16872         
16873         this.page = 0;
16874         this.loadNext = false;
16875         this.hasFocus = true;
16876         
16877         if(this.triggerAction == 'all') {
16878             this.doQuery(this.allQuery, true);
16879         } else {
16880             this.doQuery(this.getRawValue());
16881         }
16882     },
16883     
16884     listKeyPress : function(e)
16885     {
16886         //Roo.log('listkeypress');
16887         // scroll to first matching element based on key pres..
16888         if (e.isSpecialKey()) {
16889             return false;
16890         }
16891         var k = String.fromCharCode(e.getKey()).toUpperCase();
16892         //Roo.log(k);
16893         var match  = false;
16894         var csel = this.view.getSelectedNodes();
16895         var cselitem = false;
16896         if (csel.length) {
16897             var ix = this.view.indexOf(csel[0]);
16898             cselitem  = this.store.getAt(ix);
16899             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16900                 cselitem = false;
16901             }
16902             
16903         }
16904         
16905         this.store.each(function(v) { 
16906             if (cselitem) {
16907                 // start at existing selection.
16908                 if (cselitem.id == v.id) {
16909                     cselitem = false;
16910                 }
16911                 return true;
16912             }
16913                 
16914             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16915                 match = this.store.indexOf(v);
16916                 return false;
16917             }
16918             return true;
16919         }, this);
16920         
16921         if (match === false) {
16922             return true; // no more action?
16923         }
16924         // scroll to?
16925         this.view.select(match);
16926         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16927         sn.scrollIntoView(sn.dom.parentNode, false);
16928     },
16929     
16930     onViewScroll : function(e, t){
16931         
16932         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){
16933             return;
16934         }
16935         
16936         this.hasQuery = true;
16937         
16938         this.loading = this.list.select('.loading', true).first();
16939         
16940         if(this.loading === null){
16941             this.list.createChild({
16942                 tag: 'div',
16943                 cls: 'loading roo-select2-more-results roo-select2-active',
16944                 html: 'Loading more results...'
16945             });
16946             
16947             this.loading = this.list.select('.loading', true).first();
16948             
16949             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16950             
16951             this.loading.hide();
16952         }
16953         
16954         this.loading.show();
16955         
16956         var _combo = this;
16957         
16958         this.page++;
16959         this.loadNext = true;
16960         
16961         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16962         
16963         return;
16964     },
16965     
16966     addItem : function(o)
16967     {   
16968         var dv = ''; // display value
16969         
16970         if (this.displayField) {
16971             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16972         } else {
16973             // this is an error condition!!!
16974             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16975         }
16976         
16977         if(!dv.length){
16978             return;
16979         }
16980         
16981         var choice = this.choices.createChild({
16982             tag: 'li',
16983             cls: 'roo-select2-search-choice',
16984             cn: [
16985                 {
16986                     tag: 'div',
16987                     html: dv
16988                 },
16989                 {
16990                     tag: 'a',
16991                     href: '#',
16992                     cls: 'roo-select2-search-choice-close fa fa-times',
16993                     tabindex: '-1'
16994                 }
16995             ]
16996             
16997         }, this.searchField);
16998         
16999         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17000         
17001         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17002         
17003         this.item.push(o);
17004         
17005         this.lastData = o;
17006         
17007         this.syncValue();
17008         
17009         this.inputEl().dom.value = '';
17010         
17011         this.validate();
17012     },
17013     
17014     onRemoveItem : function(e, _self, o)
17015     {
17016         e.preventDefault();
17017         
17018         this.lastItem = Roo.apply([], this.item);
17019         
17020         var index = this.item.indexOf(o.data) * 1;
17021         
17022         if( index < 0){
17023             Roo.log('not this item?!');
17024             return;
17025         }
17026         
17027         this.item.splice(index, 1);
17028         o.item.remove();
17029         
17030         this.syncValue();
17031         
17032         this.fireEvent('remove', this, e);
17033         
17034         this.validate();
17035         
17036     },
17037     
17038     syncValue : function()
17039     {
17040         if(!this.item.length){
17041             this.clearValue();
17042             return;
17043         }
17044             
17045         var value = [];
17046         var _this = this;
17047         Roo.each(this.item, function(i){
17048             if(_this.valueField){
17049                 value.push(i[_this.valueField]);
17050                 return;
17051             }
17052
17053             value.push(i);
17054         });
17055
17056         this.value = value.join(',');
17057
17058         if(this.hiddenField){
17059             this.hiddenField.dom.value = this.value;
17060         }
17061         
17062         this.store.fireEvent("datachanged", this.store);
17063         
17064         this.validate();
17065     },
17066     
17067     clearItem : function()
17068     {
17069         if(!this.multiple){
17070             return;
17071         }
17072         
17073         this.item = [];
17074         
17075         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17076            c.remove();
17077         });
17078         
17079         this.syncValue();
17080         
17081         this.validate();
17082         
17083         if(this.tickable && !Roo.isTouch){
17084             this.view.refresh();
17085         }
17086     },
17087     
17088     inputEl: function ()
17089     {
17090         if(Roo.isIOS && this.useNativeIOS){
17091             return this.el.select('select.roo-ios-select', true).first();
17092         }
17093         
17094         if(Roo.isTouch && this.mobileTouchView){
17095             return this.el.select('input.form-control',true).first();
17096         }
17097         
17098         if(this.tickable){
17099             return this.searchField;
17100         }
17101         
17102         return this.el.select('input.form-control',true).first();
17103     },
17104     
17105     onTickableFooterButtonClick : function(e, btn, el)
17106     {
17107         e.preventDefault();
17108         
17109         this.lastItem = Roo.apply([], this.item);
17110         
17111         if(btn && btn.name == 'cancel'){
17112             this.tickItems = Roo.apply([], this.item);
17113             this.collapse();
17114             return;
17115         }
17116         
17117         this.clearItem();
17118         
17119         var _this = this;
17120         
17121         Roo.each(this.tickItems, function(o){
17122             _this.addItem(o);
17123         });
17124         
17125         this.collapse();
17126         
17127     },
17128     
17129     validate : function()
17130     {
17131         if(this.getVisibilityEl().hasClass('hidden')){
17132             return true;
17133         }
17134         
17135         var v = this.getRawValue();
17136         
17137         if(this.multiple){
17138             v = this.getValue();
17139         }
17140         
17141         if(this.disabled || this.allowBlank || v.length){
17142             this.markValid();
17143             return true;
17144         }
17145         
17146         this.markInvalid();
17147         return false;
17148     },
17149     
17150     tickableInputEl : function()
17151     {
17152         if(!this.tickable || !this.editable){
17153             return this.inputEl();
17154         }
17155         
17156         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17157     },
17158     
17159     
17160     getAutoCreateTouchView : function()
17161     {
17162         var id = Roo.id();
17163         
17164         var cfg = {
17165             cls: 'form-group' //input-group
17166         };
17167         
17168         var input =  {
17169             tag: 'input',
17170             id : id,
17171             type : this.inputType,
17172             cls : 'form-control x-combo-noedit',
17173             autocomplete: 'new-password',
17174             placeholder : this.placeholder || '',
17175             readonly : true
17176         };
17177         
17178         if (this.name) {
17179             input.name = this.name;
17180         }
17181         
17182         if (this.size) {
17183             input.cls += ' input-' + this.size;
17184         }
17185         
17186         if (this.disabled) {
17187             input.disabled = true;
17188         }
17189         
17190         var inputblock = {
17191             cls : 'roo-combobox-wrap',
17192             cn : [
17193                 input
17194             ]
17195         };
17196         
17197         if(this.before){
17198             inputblock.cls += ' input-group';
17199             
17200             inputblock.cn.unshift({
17201                 tag :'span',
17202                 cls : 'input-group-addon input-group-prepend input-group-text',
17203                 html : this.before
17204             });
17205         }
17206         
17207         if(this.removable && !this.multiple){
17208             inputblock.cls += ' roo-removable';
17209             
17210             inputblock.cn.push({
17211                 tag: 'button',
17212                 html : 'x',
17213                 cls : 'roo-combo-removable-btn close'
17214             });
17215         }
17216
17217         if(this.hasFeedback && !this.allowBlank){
17218             
17219             inputblock.cls += ' has-feedback';
17220             
17221             inputblock.cn.push({
17222                 tag: 'span',
17223                 cls: 'glyphicon form-control-feedback'
17224             });
17225             
17226         }
17227         
17228         if (this.after) {
17229             
17230             inputblock.cls += (this.before) ? '' : ' input-group';
17231             
17232             inputblock.cn.push({
17233                 tag :'span',
17234                 cls : 'input-group-addon input-group-append input-group-text',
17235                 html : this.after
17236             });
17237         }
17238
17239         
17240         var ibwrap = inputblock;
17241         
17242         if(this.multiple){
17243             ibwrap = {
17244                 tag: 'ul',
17245                 cls: 'roo-select2-choices',
17246                 cn:[
17247                     {
17248                         tag: 'li',
17249                         cls: 'roo-select2-search-field',
17250                         cn: [
17251
17252                             inputblock
17253                         ]
17254                     }
17255                 ]
17256             };
17257         
17258             
17259         }
17260         
17261         var combobox = {
17262             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17263             cn: [
17264                 {
17265                     tag: 'input',
17266                     type : 'hidden',
17267                     cls: 'form-hidden-field'
17268                 },
17269                 ibwrap
17270             ]
17271         };
17272         
17273         if(!this.multiple && this.showToggleBtn){
17274             
17275             var caret = {
17276                 cls: 'caret'
17277             };
17278             
17279             if (this.caret != false) {
17280                 caret = {
17281                      tag: 'i',
17282                      cls: 'fa fa-' + this.caret
17283                 };
17284                 
17285             }
17286             
17287             combobox.cn.push({
17288                 tag :'span',
17289                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17290                 cn : [
17291                     Roo.bootstrap.version == 3 ? caret : '',
17292                     {
17293                         tag: 'span',
17294                         cls: 'combobox-clear',
17295                         cn  : [
17296                             {
17297                                 tag : 'i',
17298                                 cls: 'icon-remove'
17299                             }
17300                         ]
17301                     }
17302                 ]
17303
17304             })
17305         }
17306         
17307         if(this.multiple){
17308             combobox.cls += ' roo-select2-container-multi';
17309         }
17310         
17311         var align = this.labelAlign || this.parentLabelAlign();
17312         
17313         if (align ==='left' && this.fieldLabel.length) {
17314
17315             cfg.cn = [
17316                 {
17317                    tag : 'i',
17318                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17319                    tooltip : 'This field is required'
17320                 },
17321                 {
17322                     tag: 'label',
17323                     cls : 'control-label col-form-label',
17324                     html : this.fieldLabel
17325
17326                 },
17327                 {
17328                     cls : 'roo-combobox-wrap ', 
17329                     cn: [
17330                         combobox
17331                     ]
17332                 }
17333             ];
17334             
17335             var labelCfg = cfg.cn[1];
17336             var contentCfg = cfg.cn[2];
17337             
17338
17339             if(this.indicatorpos == 'right'){
17340                 cfg.cn = [
17341                     {
17342                         tag: 'label',
17343                         'for' :  id,
17344                         cls : 'control-label col-form-label',
17345                         cn : [
17346                             {
17347                                 tag : 'span',
17348                                 html : this.fieldLabel
17349                             },
17350                             {
17351                                 tag : 'i',
17352                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17353                                 tooltip : 'This field is required'
17354                             }
17355                         ]
17356                     },
17357                     {
17358                         cls : "roo-combobox-wrap ",
17359                         cn: [
17360                             combobox
17361                         ]
17362                     }
17363
17364                 ];
17365                 
17366                 labelCfg = cfg.cn[0];
17367                 contentCfg = cfg.cn[1];
17368             }
17369             
17370            
17371             
17372             if(this.labelWidth > 12){
17373                 labelCfg.style = "width: " + this.labelWidth + 'px';
17374             }
17375            
17376             if(this.labelWidth < 13 && this.labelmd == 0){
17377                 this.labelmd = this.labelWidth;
17378             }
17379             
17380             if(this.labellg > 0){
17381                 labelCfg.cls += ' col-lg-' + this.labellg;
17382                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17383             }
17384             
17385             if(this.labelmd > 0){
17386                 labelCfg.cls += ' col-md-' + this.labelmd;
17387                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17388             }
17389             
17390             if(this.labelsm > 0){
17391                 labelCfg.cls += ' col-sm-' + this.labelsm;
17392                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17393             }
17394             
17395             if(this.labelxs > 0){
17396                 labelCfg.cls += ' col-xs-' + this.labelxs;
17397                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17398             }
17399                 
17400                 
17401         } else if ( this.fieldLabel.length) {
17402             cfg.cn = [
17403                 {
17404                    tag : 'i',
17405                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17406                    tooltip : 'This field is required'
17407                 },
17408                 {
17409                     tag: 'label',
17410                     cls : 'control-label',
17411                     html : this.fieldLabel
17412
17413                 },
17414                 {
17415                     cls : '', 
17416                     cn: [
17417                         combobox
17418                     ]
17419                 }
17420             ];
17421             
17422             if(this.indicatorpos == 'right'){
17423                 cfg.cn = [
17424                     {
17425                         tag: 'label',
17426                         cls : 'control-label',
17427                         html : this.fieldLabel,
17428                         cn : [
17429                             {
17430                                tag : 'i',
17431                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17432                                tooltip : 'This field is required'
17433                             }
17434                         ]
17435                     },
17436                     {
17437                         cls : '', 
17438                         cn: [
17439                             combobox
17440                         ]
17441                     }
17442                 ];
17443             }
17444         } else {
17445             cfg.cn = combobox;    
17446         }
17447         
17448         
17449         var settings = this;
17450         
17451         ['xs','sm','md','lg'].map(function(size){
17452             if (settings[size]) {
17453                 cfg.cls += ' col-' + size + '-' + settings[size];
17454             }
17455         });
17456         
17457         return cfg;
17458     },
17459     
17460     initTouchView : function()
17461     {
17462         this.renderTouchView();
17463         
17464         this.touchViewEl.on('scroll', function(){
17465             this.el.dom.scrollTop = 0;
17466         }, this);
17467         
17468         this.originalValue = this.getValue();
17469         
17470         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17471         
17472         this.inputEl().on("click", this.showTouchView, this);
17473         if (this.triggerEl) {
17474             this.triggerEl.on("click", this.showTouchView, this);
17475         }
17476         
17477         
17478         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17479         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17480         
17481         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17482         
17483         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17484         this.store.on('load', this.onTouchViewLoad, this);
17485         this.store.on('loadexception', this.onTouchViewLoadException, this);
17486         
17487         if(this.hiddenName){
17488             
17489             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17490             
17491             this.hiddenField.dom.value =
17492                 this.hiddenValue !== undefined ? this.hiddenValue :
17493                 this.value !== undefined ? this.value : '';
17494         
17495             this.el.dom.removeAttribute('name');
17496             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17497         }
17498         
17499         if(this.multiple){
17500             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17501             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17502         }
17503         
17504         if(this.removable && !this.multiple){
17505             var close = this.closeTriggerEl();
17506             if(close){
17507                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17508                 close.on('click', this.removeBtnClick, this, close);
17509             }
17510         }
17511         /*
17512          * fix the bug in Safari iOS8
17513          */
17514         this.inputEl().on("focus", function(e){
17515             document.activeElement.blur();
17516         }, this);
17517         
17518         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17519         
17520         return;
17521         
17522         
17523     },
17524     
17525     renderTouchView : function()
17526     {
17527         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17528         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17529         
17530         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17531         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17532         
17533         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17534         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17535         this.touchViewBodyEl.setStyle('overflow', 'auto');
17536         
17537         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17538         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17539         
17540         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17541         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17542         
17543     },
17544     
17545     showTouchView : function()
17546     {
17547         if(this.disabled){
17548             return;
17549         }
17550         
17551         this.touchViewHeaderEl.hide();
17552
17553         if(this.modalTitle.length){
17554             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17555             this.touchViewHeaderEl.show();
17556         }
17557
17558         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17559         this.touchViewEl.show();
17560
17561         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17562         
17563         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17564         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17565
17566         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17567
17568         if(this.modalTitle.length){
17569             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17570         }
17571         
17572         this.touchViewBodyEl.setHeight(bodyHeight);
17573
17574         if(this.animate){
17575             var _this = this;
17576             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17577         }else{
17578             this.touchViewEl.addClass(['in','show']);
17579         }
17580         
17581         if(this._touchViewMask){
17582             Roo.get(document.body).addClass("x-body-masked");
17583             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17584             this._touchViewMask.setStyle('z-index', 10000);
17585             this._touchViewMask.addClass('show');
17586         }
17587         
17588         this.doTouchViewQuery();
17589         
17590     },
17591     
17592     hideTouchView : function()
17593     {
17594         this.touchViewEl.removeClass(['in','show']);
17595
17596         if(this.animate){
17597             var _this = this;
17598             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17599         }else{
17600             this.touchViewEl.setStyle('display', 'none');
17601         }
17602         
17603         if(this._touchViewMask){
17604             this._touchViewMask.removeClass('show');
17605             Roo.get(document.body).removeClass("x-body-masked");
17606         }
17607     },
17608     
17609     setTouchViewValue : function()
17610     {
17611         if(this.multiple){
17612             this.clearItem();
17613         
17614             var _this = this;
17615
17616             Roo.each(this.tickItems, function(o){
17617                 this.addItem(o);
17618             }, this);
17619         }
17620         
17621         this.hideTouchView();
17622     },
17623     
17624     doTouchViewQuery : function()
17625     {
17626         var qe = {
17627             query: '',
17628             forceAll: true,
17629             combo: this,
17630             cancel:false
17631         };
17632         
17633         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17634             return false;
17635         }
17636         
17637         if(!this.alwaysQuery || this.mode == 'local'){
17638             this.onTouchViewLoad();
17639             return;
17640         }
17641         
17642         this.store.load();
17643     },
17644     
17645     onTouchViewBeforeLoad : function(combo,opts)
17646     {
17647         return;
17648     },
17649
17650     // private
17651     onTouchViewLoad : function()
17652     {
17653         if(this.store.getCount() < 1){
17654             this.onTouchViewEmptyResults();
17655             return;
17656         }
17657         
17658         this.clearTouchView();
17659         
17660         var rawValue = this.getRawValue();
17661         
17662         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17663         
17664         this.tickItems = [];
17665         
17666         this.store.data.each(function(d, rowIndex){
17667             var row = this.touchViewListGroup.createChild(template);
17668             
17669             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17670                 row.addClass(d.data.cls);
17671             }
17672             
17673             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17674                 var cfg = {
17675                     data : d.data,
17676                     html : d.data[this.displayField]
17677                 };
17678                 
17679                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17680                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17681                 }
17682             }
17683             row.removeClass('selected');
17684             if(!this.multiple && this.valueField &&
17685                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17686             {
17687                 // radio buttons..
17688                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689                 row.addClass('selected');
17690             }
17691             
17692             if(this.multiple && this.valueField &&
17693                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17694             {
17695                 
17696                 // checkboxes...
17697                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17698                 this.tickItems.push(d.data);
17699             }
17700             
17701             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17702             
17703         }, this);
17704         
17705         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17706         
17707         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17708
17709         if(this.modalTitle.length){
17710             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17711         }
17712
17713         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17714         
17715         if(this.mobile_restrict_height && listHeight < bodyHeight){
17716             this.touchViewBodyEl.setHeight(listHeight);
17717         }
17718         
17719         var _this = this;
17720         
17721         if(firstChecked && listHeight > bodyHeight){
17722             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17723         }
17724         
17725     },
17726     
17727     onTouchViewLoadException : function()
17728     {
17729         this.hideTouchView();
17730     },
17731     
17732     onTouchViewEmptyResults : function()
17733     {
17734         this.clearTouchView();
17735         
17736         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17737         
17738         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17739         
17740     },
17741     
17742     clearTouchView : function()
17743     {
17744         this.touchViewListGroup.dom.innerHTML = '';
17745     },
17746     
17747     onTouchViewClick : function(e, el, o)
17748     {
17749         e.preventDefault();
17750         
17751         var row = o.row;
17752         var rowIndex = o.rowIndex;
17753         
17754         var r = this.store.getAt(rowIndex);
17755         
17756         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17757             
17758             if(!this.multiple){
17759                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17760                     c.dom.removeAttribute('checked');
17761                 }, this);
17762
17763                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17764
17765                 this.setFromData(r.data);
17766
17767                 var close = this.closeTriggerEl();
17768
17769                 if(close){
17770                     close.show();
17771                 }
17772
17773                 this.hideTouchView();
17774
17775                 this.fireEvent('select', this, r, rowIndex);
17776
17777                 return;
17778             }
17779
17780             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17781                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17782                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17783                 return;
17784             }
17785
17786             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17787             this.addItem(r.data);
17788             this.tickItems.push(r.data);
17789         }
17790     },
17791     
17792     getAutoCreateNativeIOS : function()
17793     {
17794         var cfg = {
17795             cls: 'form-group' //input-group,
17796         };
17797         
17798         var combobox =  {
17799             tag: 'select',
17800             cls : 'roo-ios-select'
17801         };
17802         
17803         if (this.name) {
17804             combobox.name = this.name;
17805         }
17806         
17807         if (this.disabled) {
17808             combobox.disabled = true;
17809         }
17810         
17811         var settings = this;
17812         
17813         ['xs','sm','md','lg'].map(function(size){
17814             if (settings[size]) {
17815                 cfg.cls += ' col-' + size + '-' + settings[size];
17816             }
17817         });
17818         
17819         cfg.cn = combobox;
17820         
17821         return cfg;
17822         
17823     },
17824     
17825     initIOSView : function()
17826     {
17827         this.store.on('load', this.onIOSViewLoad, this);
17828         
17829         return;
17830     },
17831     
17832     onIOSViewLoad : function()
17833     {
17834         if(this.store.getCount() < 1){
17835             return;
17836         }
17837         
17838         this.clearIOSView();
17839         
17840         if(this.allowBlank) {
17841             
17842             var default_text = '-- SELECT --';
17843             
17844             if(this.placeholder.length){
17845                 default_text = this.placeholder;
17846             }
17847             
17848             if(this.emptyTitle.length){
17849                 default_text += ' - ' + this.emptyTitle + ' -';
17850             }
17851             
17852             var opt = this.inputEl().createChild({
17853                 tag: 'option',
17854                 value : 0,
17855                 html : default_text
17856             });
17857             
17858             var o = {};
17859             o[this.valueField] = 0;
17860             o[this.displayField] = default_text;
17861             
17862             this.ios_options.push({
17863                 data : o,
17864                 el : opt
17865             });
17866             
17867         }
17868         
17869         this.store.data.each(function(d, rowIndex){
17870             
17871             var html = '';
17872             
17873             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17874                 html = d.data[this.displayField];
17875             }
17876             
17877             var value = '';
17878             
17879             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17880                 value = d.data[this.valueField];
17881             }
17882             
17883             var option = {
17884                 tag: 'option',
17885                 value : value,
17886                 html : html
17887             };
17888             
17889             if(this.value == d.data[this.valueField]){
17890                 option['selected'] = true;
17891             }
17892             
17893             var opt = this.inputEl().createChild(option);
17894             
17895             this.ios_options.push({
17896                 data : d.data,
17897                 el : opt
17898             });
17899             
17900         }, this);
17901         
17902         this.inputEl().on('change', function(){
17903            this.fireEvent('select', this);
17904         }, this);
17905         
17906     },
17907     
17908     clearIOSView: function()
17909     {
17910         this.inputEl().dom.innerHTML = '';
17911         
17912         this.ios_options = [];
17913     },
17914     
17915     setIOSValue: function(v)
17916     {
17917         this.value = v;
17918         
17919         if(!this.ios_options){
17920             return;
17921         }
17922         
17923         Roo.each(this.ios_options, function(opts){
17924            
17925            opts.el.dom.removeAttribute('selected');
17926            
17927            if(opts.data[this.valueField] != v){
17928                return;
17929            }
17930            
17931            opts.el.dom.setAttribute('selected', true);
17932            
17933         }, this);
17934     }
17935
17936     /** 
17937     * @cfg {Boolean} grow 
17938     * @hide 
17939     */
17940     /** 
17941     * @cfg {Number} growMin 
17942     * @hide 
17943     */
17944     /** 
17945     * @cfg {Number} growMax 
17946     * @hide 
17947     */
17948     /**
17949      * @hide
17950      * @method autoSize
17951      */
17952 });
17953
17954 Roo.apply(Roo.bootstrap.ComboBox,  {
17955     
17956     header : {
17957         tag: 'div',
17958         cls: 'modal-header',
17959         cn: [
17960             {
17961                 tag: 'h4',
17962                 cls: 'modal-title'
17963             }
17964         ]
17965     },
17966     
17967     body : {
17968         tag: 'div',
17969         cls: 'modal-body',
17970         cn: [
17971             {
17972                 tag: 'ul',
17973                 cls: 'list-group'
17974             }
17975         ]
17976     },
17977     
17978     listItemRadio : {
17979         tag: 'li',
17980         cls: 'list-group-item',
17981         cn: [
17982             {
17983                 tag: 'span',
17984                 cls: 'roo-combobox-list-group-item-value'
17985             },
17986             {
17987                 tag: 'div',
17988                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17989                 cn: [
17990                     {
17991                         tag: 'input',
17992                         type: 'radio'
17993                     },
17994                     {
17995                         tag: 'label'
17996                     }
17997                 ]
17998             }
17999         ]
18000     },
18001     
18002     listItemCheckbox : {
18003         tag: 'li',
18004         cls: 'list-group-item',
18005         cn: [
18006             {
18007                 tag: 'span',
18008                 cls: 'roo-combobox-list-group-item-value'
18009             },
18010             {
18011                 tag: 'div',
18012                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18013                 cn: [
18014                     {
18015                         tag: 'input',
18016                         type: 'checkbox'
18017                     },
18018                     {
18019                         tag: 'label'
18020                     }
18021                 ]
18022             }
18023         ]
18024     },
18025     
18026     emptyResult : {
18027         tag: 'div',
18028         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18029     },
18030     
18031     footer : {
18032         tag: 'div',
18033         cls: 'modal-footer',
18034         cn: [
18035             {
18036                 tag: 'div',
18037                 cls: 'row',
18038                 cn: [
18039                     {
18040                         tag: 'div',
18041                         cls: 'col-xs-6 text-left',
18042                         cn: {
18043                             tag: 'button',
18044                             cls: 'btn btn-danger roo-touch-view-cancel',
18045                             html: 'Cancel'
18046                         }
18047                     },
18048                     {
18049                         tag: 'div',
18050                         cls: 'col-xs-6 text-right',
18051                         cn: {
18052                             tag: 'button',
18053                             cls: 'btn btn-success roo-touch-view-ok',
18054                             html: 'OK'
18055                         }
18056                     }
18057                 ]
18058             }
18059         ]
18060         
18061     }
18062 });
18063
18064 Roo.apply(Roo.bootstrap.ComboBox,  {
18065     
18066     touchViewTemplate : {
18067         tag: 'div',
18068         cls: 'modal fade roo-combobox-touch-view',
18069         cn: [
18070             {
18071                 tag: 'div',
18072                 cls: 'modal-dialog',
18073                 style : 'position:fixed', // we have to fix position....
18074                 cn: [
18075                     {
18076                         tag: 'div',
18077                         cls: 'modal-content',
18078                         cn: [
18079                             Roo.bootstrap.ComboBox.header,
18080                             Roo.bootstrap.ComboBox.body,
18081                             Roo.bootstrap.ComboBox.footer
18082                         ]
18083                     }
18084                 ]
18085             }
18086         ]
18087     }
18088 });/*
18089  * Based on:
18090  * Ext JS Library 1.1.1
18091  * Copyright(c) 2006-2007, Ext JS, LLC.
18092  *
18093  * Originally Released Under LGPL - original licence link has changed is not relivant.
18094  *
18095  * Fork - LGPL
18096  * <script type="text/javascript">
18097  */
18098
18099 /**
18100  * @class Roo.View
18101  * @extends Roo.util.Observable
18102  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18103  * This class also supports single and multi selection modes. <br>
18104  * Create a data model bound view:
18105  <pre><code>
18106  var store = new Roo.data.Store(...);
18107
18108  var view = new Roo.View({
18109     el : "my-element",
18110     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18111  
18112     singleSelect: true,
18113     selectedClass: "ydataview-selected",
18114     store: store
18115  });
18116
18117  // listen for node click?
18118  view.on("click", function(vw, index, node, e){
18119  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18120  });
18121
18122  // load XML data
18123  dataModel.load("foobar.xml");
18124  </code></pre>
18125  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18126  * <br><br>
18127  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18128  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18129  * 
18130  * Note: old style constructor is still suported (container, template, config)
18131  * 
18132  * @constructor
18133  * Create a new View
18134  * @param {Object} config The config object
18135  * 
18136  */
18137 Roo.View = function(config, depreciated_tpl, depreciated_config){
18138     
18139     this.parent = false;
18140     
18141     if (typeof(depreciated_tpl) == 'undefined') {
18142         // new way.. - universal constructor.
18143         Roo.apply(this, config);
18144         this.el  = Roo.get(this.el);
18145     } else {
18146         // old format..
18147         this.el  = Roo.get(config);
18148         this.tpl = depreciated_tpl;
18149         Roo.apply(this, depreciated_config);
18150     }
18151     this.wrapEl  = this.el.wrap().wrap();
18152     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18153     
18154     
18155     if(typeof(this.tpl) == "string"){
18156         this.tpl = new Roo.Template(this.tpl);
18157     } else {
18158         // support xtype ctors..
18159         this.tpl = new Roo.factory(this.tpl, Roo);
18160     }
18161     
18162     
18163     this.tpl.compile();
18164     
18165     /** @private */
18166     this.addEvents({
18167         /**
18168          * @event beforeclick
18169          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
18176         /**
18177          * @event click
18178          * Fires when a template node is clicked.
18179          * @param {Roo.View} this
18180          * @param {Number} index The index of the target node
18181          * @param {HTMLElement} node The target node
18182          * @param {Roo.EventObject} e The raw event object
18183          */
18184             "click" : true,
18185         /**
18186          * @event dblclick
18187          * Fires when a template node is double clicked.
18188          * @param {Roo.View} this
18189          * @param {Number} index The index of the target node
18190          * @param {HTMLElement} node The target node
18191          * @param {Roo.EventObject} e The raw event object
18192          */
18193             "dblclick" : true,
18194         /**
18195          * @event contextmenu
18196          * Fires when a template node is right clicked.
18197          * @param {Roo.View} this
18198          * @param {Number} index The index of the target node
18199          * @param {HTMLElement} node The target node
18200          * @param {Roo.EventObject} e The raw event object
18201          */
18202             "contextmenu" : true,
18203         /**
18204          * @event selectionchange
18205          * Fires when the selected nodes change.
18206          * @param {Roo.View} this
18207          * @param {Array} selections Array of the selected nodes
18208          */
18209             "selectionchange" : true,
18210     
18211         /**
18212          * @event beforeselect
18213          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18214          * @param {Roo.View} this
18215          * @param {HTMLElement} node The node to be selected
18216          * @param {Array} selections Array of currently selected nodes
18217          */
18218             "beforeselect" : true,
18219         /**
18220          * @event preparedata
18221          * Fires on every row to render, to allow you to change the data.
18222          * @param {Roo.View} this
18223          * @param {Object} data to be rendered (change this)
18224          */
18225           "preparedata" : true
18226           
18227           
18228         });
18229
18230
18231
18232     this.el.on({
18233         "click": this.onClick,
18234         "dblclick": this.onDblClick,
18235         "contextmenu": this.onContextMenu,
18236         scope:this
18237     });
18238
18239     this.selections = [];
18240     this.nodes = [];
18241     this.cmp = new Roo.CompositeElementLite([]);
18242     if(this.store){
18243         this.store = Roo.factory(this.store, Roo.data);
18244         this.setStore(this.store, true);
18245     }
18246     
18247     if ( this.footer && this.footer.xtype) {
18248            
18249          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18250         
18251         this.footer.dataSource = this.store;
18252         this.footer.container = fctr;
18253         this.footer = Roo.factory(this.footer, Roo);
18254         fctr.insertFirst(this.el);
18255         
18256         // this is a bit insane - as the paging toolbar seems to detach the el..
18257 //        dom.parentNode.parentNode.parentNode
18258          // they get detached?
18259     }
18260     
18261     
18262     Roo.View.superclass.constructor.call(this);
18263     
18264     
18265 };
18266
18267 Roo.extend(Roo.View, Roo.util.Observable, {
18268     
18269      /**
18270      * @cfg {Roo.data.Store} store Data store to load data from.
18271      */
18272     store : false,
18273     
18274     /**
18275      * @cfg {String|Roo.Element} el The container element.
18276      */
18277     el : '',
18278     
18279     /**
18280      * @cfg {String|Roo.Template} tpl The template used by this View 
18281      */
18282     tpl : false,
18283     /**
18284      * @cfg {String} dataName the named area of the template to use as the data area
18285      *                          Works with domtemplates roo-name="name"
18286      */
18287     dataName: false,
18288     /**
18289      * @cfg {String} selectedClass The css class to add to selected nodes
18290      */
18291     selectedClass : "x-view-selected",
18292      /**
18293      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18294      */
18295     emptyText : "",
18296     
18297     /**
18298      * @cfg {String} text to display on mask (default Loading)
18299      */
18300     mask : false,
18301     /**
18302      * @cfg {Boolean} multiSelect Allow multiple selection
18303      */
18304     multiSelect : false,
18305     /**
18306      * @cfg {Boolean} singleSelect Allow single selection
18307      */
18308     singleSelect:  false,
18309     
18310     /**
18311      * @cfg {Boolean} toggleSelect - selecting 
18312      */
18313     toggleSelect : false,
18314     
18315     /**
18316      * @cfg {Boolean} tickable - selecting 
18317      */
18318     tickable : false,
18319     
18320     /**
18321      * Returns the element this view is bound to.
18322      * @return {Roo.Element}
18323      */
18324     getEl : function(){
18325         return this.wrapEl;
18326     },
18327     
18328     
18329
18330     /**
18331      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18332      */
18333     refresh : function(){
18334         //Roo.log('refresh');
18335         var t = this.tpl;
18336         
18337         // if we are using something like 'domtemplate', then
18338         // the what gets used is:
18339         // t.applySubtemplate(NAME, data, wrapping data..)
18340         // the outer template then get' applied with
18341         //     the store 'extra data'
18342         // and the body get's added to the
18343         //      roo-name="data" node?
18344         //      <span class='roo-tpl-{name}'></span> ?????
18345         
18346         
18347         
18348         this.clearSelections();
18349         this.el.update("");
18350         var html = [];
18351         var records = this.store.getRange();
18352         if(records.length < 1) {
18353             
18354             // is this valid??  = should it render a template??
18355             
18356             this.el.update(this.emptyText);
18357             return;
18358         }
18359         var el = this.el;
18360         if (this.dataName) {
18361             this.el.update(t.apply(this.store.meta)); //????
18362             el = this.el.child('.roo-tpl-' + this.dataName);
18363         }
18364         
18365         for(var i = 0, len = records.length; i < len; i++){
18366             var data = this.prepareData(records[i].data, i, records[i]);
18367             this.fireEvent("preparedata", this, data, i, records[i]);
18368             
18369             var d = Roo.apply({}, data);
18370             
18371             if(this.tickable){
18372                 Roo.apply(d, {'roo-id' : Roo.id()});
18373                 
18374                 var _this = this;
18375             
18376                 Roo.each(this.parent.item, function(item){
18377                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18378                         return;
18379                     }
18380                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18381                 });
18382             }
18383             
18384             html[html.length] = Roo.util.Format.trim(
18385                 this.dataName ?
18386                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18387                     t.apply(d)
18388             );
18389         }
18390         
18391         
18392         
18393         el.update(html.join(""));
18394         this.nodes = el.dom.childNodes;
18395         this.updateIndexes(0);
18396     },
18397     
18398
18399     /**
18400      * Function to override to reformat the data that is sent to
18401      * the template for each node.
18402      * DEPRICATED - use the preparedata event handler.
18403      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18404      * a JSON object for an UpdateManager bound view).
18405      */
18406     prepareData : function(data, index, record)
18407     {
18408         this.fireEvent("preparedata", this, data, index, record);
18409         return data;
18410     },
18411
18412     onUpdate : function(ds, record){
18413         // Roo.log('on update');   
18414         this.clearSelections();
18415         var index = this.store.indexOf(record);
18416         var n = this.nodes[index];
18417         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18418         n.parentNode.removeChild(n);
18419         this.updateIndexes(index, index);
18420     },
18421
18422     
18423     
18424 // --------- FIXME     
18425     onAdd : function(ds, records, index)
18426     {
18427         //Roo.log(['on Add', ds, records, index] );        
18428         this.clearSelections();
18429         if(this.nodes.length == 0){
18430             this.refresh();
18431             return;
18432         }
18433         var n = this.nodes[index];
18434         for(var i = 0, len = records.length; i < len; i++){
18435             var d = this.prepareData(records[i].data, i, records[i]);
18436             if(n){
18437                 this.tpl.insertBefore(n, d);
18438             }else{
18439                 
18440                 this.tpl.append(this.el, d);
18441             }
18442         }
18443         this.updateIndexes(index);
18444     },
18445
18446     onRemove : function(ds, record, index){
18447        // Roo.log('onRemove');
18448         this.clearSelections();
18449         var el = this.dataName  ?
18450             this.el.child('.roo-tpl-' + this.dataName) :
18451             this.el; 
18452         
18453         el.dom.removeChild(this.nodes[index]);
18454         this.updateIndexes(index);
18455     },
18456
18457     /**
18458      * Refresh an individual node.
18459      * @param {Number} index
18460      */
18461     refreshNode : function(index){
18462         this.onUpdate(this.store, this.store.getAt(index));
18463     },
18464
18465     updateIndexes : function(startIndex, endIndex){
18466         var ns = this.nodes;
18467         startIndex = startIndex || 0;
18468         endIndex = endIndex || ns.length - 1;
18469         for(var i = startIndex; i <= endIndex; i++){
18470             ns[i].nodeIndex = i;
18471         }
18472     },
18473
18474     /**
18475      * Changes the data store this view uses and refresh the view.
18476      * @param {Store} store
18477      */
18478     setStore : function(store, initial){
18479         if(!initial && this.store){
18480             this.store.un("datachanged", this.refresh);
18481             this.store.un("add", this.onAdd);
18482             this.store.un("remove", this.onRemove);
18483             this.store.un("update", this.onUpdate);
18484             this.store.un("clear", this.refresh);
18485             this.store.un("beforeload", this.onBeforeLoad);
18486             this.store.un("load", this.onLoad);
18487             this.store.un("loadexception", this.onLoad);
18488         }
18489         if(store){
18490           
18491             store.on("datachanged", this.refresh, this);
18492             store.on("add", this.onAdd, this);
18493             store.on("remove", this.onRemove, this);
18494             store.on("update", this.onUpdate, this);
18495             store.on("clear", this.refresh, this);
18496             store.on("beforeload", this.onBeforeLoad, this);
18497             store.on("load", this.onLoad, this);
18498             store.on("loadexception", this.onLoad, this);
18499         }
18500         
18501         if(store){
18502             this.refresh();
18503         }
18504     },
18505     /**
18506      * onbeforeLoad - masks the loading area.
18507      *
18508      */
18509     onBeforeLoad : function(store,opts)
18510     {
18511          //Roo.log('onBeforeLoad');   
18512         if (!opts.add) {
18513             this.el.update("");
18514         }
18515         this.el.mask(this.mask ? this.mask : "Loading" ); 
18516     },
18517     onLoad : function ()
18518     {
18519         this.el.unmask();
18520     },
18521     
18522
18523     /**
18524      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18525      * @param {HTMLElement} node
18526      * @return {HTMLElement} The template node
18527      */
18528     findItemFromChild : function(node){
18529         var el = this.dataName  ?
18530             this.el.child('.roo-tpl-' + this.dataName,true) :
18531             this.el.dom; 
18532         
18533         if(!node || node.parentNode == el){
18534                     return node;
18535             }
18536             var p = node.parentNode;
18537             while(p && p != el){
18538             if(p.parentNode == el){
18539                 return p;
18540             }
18541             p = p.parentNode;
18542         }
18543             return null;
18544     },
18545
18546     /** @ignore */
18547     onClick : function(e){
18548         var item = this.findItemFromChild(e.getTarget());
18549         if(item){
18550             var index = this.indexOf(item);
18551             if(this.onItemClick(item, index, e) !== false){
18552                 this.fireEvent("click", this, index, item, e);
18553             }
18554         }else{
18555             this.clearSelections();
18556         }
18557     },
18558
18559     /** @ignore */
18560     onContextMenu : function(e){
18561         var item = this.findItemFromChild(e.getTarget());
18562         if(item){
18563             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18564         }
18565     },
18566
18567     /** @ignore */
18568     onDblClick : function(e){
18569         var item = this.findItemFromChild(e.getTarget());
18570         if(item){
18571             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18572         }
18573     },
18574
18575     onItemClick : function(item, index, e)
18576     {
18577         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18578             return false;
18579         }
18580         if (this.toggleSelect) {
18581             var m = this.isSelected(item) ? 'unselect' : 'select';
18582             //Roo.log(m);
18583             var _t = this;
18584             _t[m](item, true, false);
18585             return true;
18586         }
18587         if(this.multiSelect || this.singleSelect){
18588             if(this.multiSelect && e.shiftKey && this.lastSelection){
18589                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18590             }else{
18591                 this.select(item, this.multiSelect && e.ctrlKey);
18592                 this.lastSelection = item;
18593             }
18594             
18595             if(!this.tickable){
18596                 e.preventDefault();
18597             }
18598             
18599         }
18600         return true;
18601     },
18602
18603     /**
18604      * Get the number of selected nodes.
18605      * @return {Number}
18606      */
18607     getSelectionCount : function(){
18608         return this.selections.length;
18609     },
18610
18611     /**
18612      * Get the currently selected nodes.
18613      * @return {Array} An array of HTMLElements
18614      */
18615     getSelectedNodes : function(){
18616         return this.selections;
18617     },
18618
18619     /**
18620      * Get the indexes of the selected nodes.
18621      * @return {Array}
18622      */
18623     getSelectedIndexes : function(){
18624         var indexes = [], s = this.selections;
18625         for(var i = 0, len = s.length; i < len; i++){
18626             indexes.push(s[i].nodeIndex);
18627         }
18628         return indexes;
18629     },
18630
18631     /**
18632      * Clear all selections
18633      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18634      */
18635     clearSelections : function(suppressEvent){
18636         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18637             this.cmp.elements = this.selections;
18638             this.cmp.removeClass(this.selectedClass);
18639             this.selections = [];
18640             if(!suppressEvent){
18641                 this.fireEvent("selectionchange", this, this.selections);
18642             }
18643         }
18644     },
18645
18646     /**
18647      * Returns true if the passed node is selected
18648      * @param {HTMLElement/Number} node The node or node index
18649      * @return {Boolean}
18650      */
18651     isSelected : function(node){
18652         var s = this.selections;
18653         if(s.length < 1){
18654             return false;
18655         }
18656         node = this.getNode(node);
18657         return s.indexOf(node) !== -1;
18658     },
18659
18660     /**
18661      * Selects nodes.
18662      * @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
18663      * @param {Boolean} keepExisting (optional) true to keep existing selections
18664      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18665      */
18666     select : function(nodeInfo, keepExisting, suppressEvent){
18667         if(nodeInfo instanceof Array){
18668             if(!keepExisting){
18669                 this.clearSelections(true);
18670             }
18671             for(var i = 0, len = nodeInfo.length; i < len; i++){
18672                 this.select(nodeInfo[i], true, true);
18673             }
18674             return;
18675         } 
18676         var node = this.getNode(nodeInfo);
18677         if(!node || this.isSelected(node)){
18678             return; // already selected.
18679         }
18680         if(!keepExisting){
18681             this.clearSelections(true);
18682         }
18683         
18684         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18685             Roo.fly(node).addClass(this.selectedClass);
18686             this.selections.push(node);
18687             if(!suppressEvent){
18688                 this.fireEvent("selectionchange", this, this.selections);
18689             }
18690         }
18691         
18692         
18693     },
18694       /**
18695      * Unselects nodes.
18696      * @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
18697      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18698      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18699      */
18700     unselect : function(nodeInfo, keepExisting, suppressEvent)
18701     {
18702         if(nodeInfo instanceof Array){
18703             Roo.each(this.selections, function(s) {
18704                 this.unselect(s, nodeInfo);
18705             }, this);
18706             return;
18707         }
18708         var node = this.getNode(nodeInfo);
18709         if(!node || !this.isSelected(node)){
18710             //Roo.log("not selected");
18711             return; // not selected.
18712         }
18713         // fireevent???
18714         var ns = [];
18715         Roo.each(this.selections, function(s) {
18716             if (s == node ) {
18717                 Roo.fly(node).removeClass(this.selectedClass);
18718
18719                 return;
18720             }
18721             ns.push(s);
18722         },this);
18723         
18724         this.selections= ns;
18725         this.fireEvent("selectionchange", this, this.selections);
18726     },
18727
18728     /**
18729      * Gets a template node.
18730      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18731      * @return {HTMLElement} The node or null if it wasn't found
18732      */
18733     getNode : function(nodeInfo){
18734         if(typeof nodeInfo == "string"){
18735             return document.getElementById(nodeInfo);
18736         }else if(typeof nodeInfo == "number"){
18737             return this.nodes[nodeInfo];
18738         }
18739         return nodeInfo;
18740     },
18741
18742     /**
18743      * Gets a range template nodes.
18744      * @param {Number} startIndex
18745      * @param {Number} endIndex
18746      * @return {Array} An array of nodes
18747      */
18748     getNodes : function(start, end){
18749         var ns = this.nodes;
18750         start = start || 0;
18751         end = typeof end == "undefined" ? ns.length - 1 : end;
18752         var nodes = [];
18753         if(start <= end){
18754             for(var i = start; i <= end; i++){
18755                 nodes.push(ns[i]);
18756             }
18757         } else{
18758             for(var i = start; i >= end; i--){
18759                 nodes.push(ns[i]);
18760             }
18761         }
18762         return nodes;
18763     },
18764
18765     /**
18766      * Finds the index of the passed node
18767      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18768      * @return {Number} The index of the node or -1
18769      */
18770     indexOf : function(node){
18771         node = this.getNode(node);
18772         if(typeof node.nodeIndex == "number"){
18773             return node.nodeIndex;
18774         }
18775         var ns = this.nodes;
18776         for(var i = 0, len = ns.length; i < len; i++){
18777             if(ns[i] == node){
18778                 return i;
18779             }
18780         }
18781         return -1;
18782     }
18783 });
18784 /*
18785  * - LGPL
18786  *
18787  * based on jquery fullcalendar
18788  * 
18789  */
18790
18791 Roo.bootstrap = Roo.bootstrap || {};
18792 /**
18793  * @class Roo.bootstrap.Calendar
18794  * @extends Roo.bootstrap.Component
18795  * Bootstrap Calendar class
18796  * @cfg {Boolean} loadMask (true|false) default false
18797  * @cfg {Object} header generate the user specific header of the calendar, default false
18798
18799  * @constructor
18800  * Create a new Container
18801  * @param {Object} config The config object
18802  */
18803
18804
18805
18806 Roo.bootstrap.Calendar = function(config){
18807     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18808      this.addEvents({
18809         /**
18810              * @event select
18811              * Fires when a date is selected
18812              * @param {DatePicker} this
18813              * @param {Date} date The selected date
18814              */
18815         'select': true,
18816         /**
18817              * @event monthchange
18818              * Fires when the displayed month changes 
18819              * @param {DatePicker} this
18820              * @param {Date} date The selected month
18821              */
18822         'monthchange': true,
18823         /**
18824              * @event evententer
18825              * Fires when mouse over an event
18826              * @param {Calendar} this
18827              * @param {event} Event
18828              */
18829         'evententer': true,
18830         /**
18831              * @event eventleave
18832              * Fires when the mouse leaves an
18833              * @param {Calendar} this
18834              * @param {event}
18835              */
18836         'eventleave': true,
18837         /**
18838              * @event eventclick
18839              * Fires when the mouse click an
18840              * @param {Calendar} this
18841              * @param {event}
18842              */
18843         'eventclick': true
18844         
18845     });
18846
18847 };
18848
18849 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18850     
18851      /**
18852      * @cfg {Number} startDay
18853      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18854      */
18855     startDay : 0,
18856     
18857     loadMask : false,
18858     
18859     header : false,
18860       
18861     getAutoCreate : function(){
18862         
18863         
18864         var fc_button = function(name, corner, style, content ) {
18865             return Roo.apply({},{
18866                 tag : 'span',
18867                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18868                          (corner.length ?
18869                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18870                             ''
18871                         ),
18872                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18873                 unselectable: 'on'
18874             });
18875         };
18876         
18877         var header = {};
18878         
18879         if(!this.header){
18880             header = {
18881                 tag : 'table',
18882                 cls : 'fc-header',
18883                 style : 'width:100%',
18884                 cn : [
18885                     {
18886                         tag: 'tr',
18887                         cn : [
18888                             {
18889                                 tag : 'td',
18890                                 cls : 'fc-header-left',
18891                                 cn : [
18892                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18893                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18894                                     { tag: 'span', cls: 'fc-header-space' },
18895                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18896
18897
18898                                 ]
18899                             },
18900
18901                             {
18902                                 tag : 'td',
18903                                 cls : 'fc-header-center',
18904                                 cn : [
18905                                     {
18906                                         tag: 'span',
18907                                         cls: 'fc-header-title',
18908                                         cn : {
18909                                             tag: 'H2',
18910                                             html : 'month / year'
18911                                         }
18912                                     }
18913
18914                                 ]
18915                             },
18916                             {
18917                                 tag : 'td',
18918                                 cls : 'fc-header-right',
18919                                 cn : [
18920                               /*      fc_button('month', 'left', '', 'month' ),
18921                                     fc_button('week', '', '', 'week' ),
18922                                     fc_button('day', 'right', '', 'day' )
18923                                 */    
18924
18925                                 ]
18926                             }
18927
18928                         ]
18929                     }
18930                 ]
18931             };
18932         }
18933         
18934         header = this.header;
18935         
18936        
18937         var cal_heads = function() {
18938             var ret = [];
18939             // fixme - handle this.
18940             
18941             for (var i =0; i < Date.dayNames.length; i++) {
18942                 var d = Date.dayNames[i];
18943                 ret.push({
18944                     tag: 'th',
18945                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18946                     html : d.substring(0,3)
18947                 });
18948                 
18949             }
18950             ret[0].cls += ' fc-first';
18951             ret[6].cls += ' fc-last';
18952             return ret;
18953         };
18954         var cal_cell = function(n) {
18955             return  {
18956                 tag: 'td',
18957                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18958                 cn : [
18959                     {
18960                         cn : [
18961                             {
18962                                 cls: 'fc-day-number',
18963                                 html: 'D'
18964                             },
18965                             {
18966                                 cls: 'fc-day-content',
18967                              
18968                                 cn : [
18969                                      {
18970                                         style: 'position: relative;' // height: 17px;
18971                                     }
18972                                 ]
18973                             }
18974                             
18975                             
18976                         ]
18977                     }
18978                 ]
18979                 
18980             }
18981         };
18982         var cal_rows = function() {
18983             
18984             var ret = [];
18985             for (var r = 0; r < 6; r++) {
18986                 var row= {
18987                     tag : 'tr',
18988                     cls : 'fc-week',
18989                     cn : []
18990                 };
18991                 
18992                 for (var i =0; i < Date.dayNames.length; i++) {
18993                     var d = Date.dayNames[i];
18994                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18995
18996                 }
18997                 row.cn[0].cls+=' fc-first';
18998                 row.cn[0].cn[0].style = 'min-height:90px';
18999                 row.cn[6].cls+=' fc-last';
19000                 ret.push(row);
19001                 
19002             }
19003             ret[0].cls += ' fc-first';
19004             ret[4].cls += ' fc-prev-last';
19005             ret[5].cls += ' fc-last';
19006             return ret;
19007             
19008         };
19009         
19010         var cal_table = {
19011             tag: 'table',
19012             cls: 'fc-border-separate',
19013             style : 'width:100%',
19014             cellspacing  : 0,
19015             cn : [
19016                 { 
19017                     tag: 'thead',
19018                     cn : [
19019                         { 
19020                             tag: 'tr',
19021                             cls : 'fc-first fc-last',
19022                             cn : cal_heads()
19023                         }
19024                     ]
19025                 },
19026                 { 
19027                     tag: 'tbody',
19028                     cn : cal_rows()
19029                 }
19030                   
19031             ]
19032         };
19033          
19034          var cfg = {
19035             cls : 'fc fc-ltr',
19036             cn : [
19037                 header,
19038                 {
19039                     cls : 'fc-content',
19040                     style : "position: relative;",
19041                     cn : [
19042                         {
19043                             cls : 'fc-view fc-view-month fc-grid',
19044                             style : 'position: relative',
19045                             unselectable : 'on',
19046                             cn : [
19047                                 {
19048                                     cls : 'fc-event-container',
19049                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19050                                 },
19051                                 cal_table
19052                             ]
19053                         }
19054                     ]
19055     
19056                 }
19057            ] 
19058             
19059         };
19060         
19061          
19062         
19063         return cfg;
19064     },
19065     
19066     
19067     initEvents : function()
19068     {
19069         if(!this.store){
19070             throw "can not find store for calendar";
19071         }
19072         
19073         var mark = {
19074             tag: "div",
19075             cls:"x-dlg-mask",
19076             style: "text-align:center",
19077             cn: [
19078                 {
19079                     tag: "div",
19080                     style: "background-color:white;width:50%;margin:250 auto",
19081                     cn: [
19082                         {
19083                             tag: "img",
19084                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19085                         },
19086                         {
19087                             tag: "span",
19088                             html: "Loading"
19089                         }
19090                         
19091                     ]
19092                 }
19093             ]
19094         };
19095         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19096         
19097         var size = this.el.select('.fc-content', true).first().getSize();
19098         this.maskEl.setSize(size.width, size.height);
19099         this.maskEl.enableDisplayMode("block");
19100         if(!this.loadMask){
19101             this.maskEl.hide();
19102         }
19103         
19104         this.store = Roo.factory(this.store, Roo.data);
19105         this.store.on('load', this.onLoad, this);
19106         this.store.on('beforeload', this.onBeforeLoad, this);
19107         
19108         this.resize();
19109         
19110         this.cells = this.el.select('.fc-day',true);
19111         //Roo.log(this.cells);
19112         this.textNodes = this.el.query('.fc-day-number');
19113         this.cells.addClassOnOver('fc-state-hover');
19114         
19115         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19116         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19117         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19118         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19119         
19120         this.on('monthchange', this.onMonthChange, this);
19121         
19122         this.update(new Date().clearTime());
19123     },
19124     
19125     resize : function() {
19126         var sz  = this.el.getSize();
19127         
19128         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19129         this.el.select('.fc-day-content div',true).setHeight(34);
19130     },
19131     
19132     
19133     // private
19134     showPrevMonth : function(e){
19135         this.update(this.activeDate.add("mo", -1));
19136     },
19137     showToday : function(e){
19138         this.update(new Date().clearTime());
19139     },
19140     // private
19141     showNextMonth : function(e){
19142         this.update(this.activeDate.add("mo", 1));
19143     },
19144
19145     // private
19146     showPrevYear : function(){
19147         this.update(this.activeDate.add("y", -1));
19148     },
19149
19150     // private
19151     showNextYear : function(){
19152         this.update(this.activeDate.add("y", 1));
19153     },
19154
19155     
19156    // private
19157     update : function(date)
19158     {
19159         var vd = this.activeDate;
19160         this.activeDate = date;
19161 //        if(vd && this.el){
19162 //            var t = date.getTime();
19163 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19164 //                Roo.log('using add remove');
19165 //                
19166 //                this.fireEvent('monthchange', this, date);
19167 //                
19168 //                this.cells.removeClass("fc-state-highlight");
19169 //                this.cells.each(function(c){
19170 //                   if(c.dateValue == t){
19171 //                       c.addClass("fc-state-highlight");
19172 //                       setTimeout(function(){
19173 //                            try{c.dom.firstChild.focus();}catch(e){}
19174 //                       }, 50);
19175 //                       return false;
19176 //                   }
19177 //                   return true;
19178 //                });
19179 //                return;
19180 //            }
19181 //        }
19182         
19183         var days = date.getDaysInMonth();
19184         
19185         var firstOfMonth = date.getFirstDateOfMonth();
19186         var startingPos = firstOfMonth.getDay()-this.startDay;
19187         
19188         if(startingPos < this.startDay){
19189             startingPos += 7;
19190         }
19191         
19192         var pm = date.add(Date.MONTH, -1);
19193         var prevStart = pm.getDaysInMonth()-startingPos;
19194 //        
19195         this.cells = this.el.select('.fc-day',true);
19196         this.textNodes = this.el.query('.fc-day-number');
19197         this.cells.addClassOnOver('fc-state-hover');
19198         
19199         var cells = this.cells.elements;
19200         var textEls = this.textNodes;
19201         
19202         Roo.each(cells, function(cell){
19203             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19204         });
19205         
19206         days += startingPos;
19207
19208         // convert everything to numbers so it's fast
19209         var day = 86400000;
19210         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19211         //Roo.log(d);
19212         //Roo.log(pm);
19213         //Roo.log(prevStart);
19214         
19215         var today = new Date().clearTime().getTime();
19216         var sel = date.clearTime().getTime();
19217         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19218         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19219         var ddMatch = this.disabledDatesRE;
19220         var ddText = this.disabledDatesText;
19221         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19222         var ddaysText = this.disabledDaysText;
19223         var format = this.format;
19224         
19225         var setCellClass = function(cal, cell){
19226             cell.row = 0;
19227             cell.events = [];
19228             cell.more = [];
19229             //Roo.log('set Cell Class');
19230             cell.title = "";
19231             var t = d.getTime();
19232             
19233             //Roo.log(d);
19234             
19235             cell.dateValue = t;
19236             if(t == today){
19237                 cell.className += " fc-today";
19238                 cell.className += " fc-state-highlight";
19239                 cell.title = cal.todayText;
19240             }
19241             if(t == sel){
19242                 // disable highlight in other month..
19243                 //cell.className += " fc-state-highlight";
19244                 
19245             }
19246             // disabling
19247             if(t < min) {
19248                 cell.className = " fc-state-disabled";
19249                 cell.title = cal.minText;
19250                 return;
19251             }
19252             if(t > max) {
19253                 cell.className = " fc-state-disabled";
19254                 cell.title = cal.maxText;
19255                 return;
19256             }
19257             if(ddays){
19258                 if(ddays.indexOf(d.getDay()) != -1){
19259                     cell.title = ddaysText;
19260                     cell.className = " fc-state-disabled";
19261                 }
19262             }
19263             if(ddMatch && format){
19264                 var fvalue = d.dateFormat(format);
19265                 if(ddMatch.test(fvalue)){
19266                     cell.title = ddText.replace("%0", fvalue);
19267                     cell.className = " fc-state-disabled";
19268                 }
19269             }
19270             
19271             if (!cell.initialClassName) {
19272                 cell.initialClassName = cell.dom.className;
19273             }
19274             
19275             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19276         };
19277
19278         var i = 0;
19279         
19280         for(; i < startingPos; i++) {
19281             textEls[i].innerHTML = (++prevStart);
19282             d.setDate(d.getDate()+1);
19283             
19284             cells[i].className = "fc-past fc-other-month";
19285             setCellClass(this, cells[i]);
19286         }
19287         
19288         var intDay = 0;
19289         
19290         for(; i < days; i++){
19291             intDay = i - startingPos + 1;
19292             textEls[i].innerHTML = (intDay);
19293             d.setDate(d.getDate()+1);
19294             
19295             cells[i].className = ''; // "x-date-active";
19296             setCellClass(this, cells[i]);
19297         }
19298         var extraDays = 0;
19299         
19300         for(; i < 42; i++) {
19301             textEls[i].innerHTML = (++extraDays);
19302             d.setDate(d.getDate()+1);
19303             
19304             cells[i].className = "fc-future fc-other-month";
19305             setCellClass(this, cells[i]);
19306         }
19307         
19308         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19309         
19310         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19311         
19312         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19313         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19314         
19315         if(totalRows != 6){
19316             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19317             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19318         }
19319         
19320         this.fireEvent('monthchange', this, date);
19321         
19322         
19323         /*
19324         if(!this.internalRender){
19325             var main = this.el.dom.firstChild;
19326             var w = main.offsetWidth;
19327             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19328             Roo.fly(main).setWidth(w);
19329             this.internalRender = true;
19330             // opera does not respect the auto grow header center column
19331             // then, after it gets a width opera refuses to recalculate
19332             // without a second pass
19333             if(Roo.isOpera && !this.secondPass){
19334                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19335                 this.secondPass = true;
19336                 this.update.defer(10, this, [date]);
19337             }
19338         }
19339         */
19340         
19341     },
19342     
19343     findCell : function(dt) {
19344         dt = dt.clearTime().getTime();
19345         var ret = false;
19346         this.cells.each(function(c){
19347             //Roo.log("check " +c.dateValue + '?=' + dt);
19348             if(c.dateValue == dt){
19349                 ret = c;
19350                 return false;
19351             }
19352             return true;
19353         });
19354         
19355         return ret;
19356     },
19357     
19358     findCells : function(ev) {
19359         var s = ev.start.clone().clearTime().getTime();
19360        // Roo.log(s);
19361         var e= ev.end.clone().clearTime().getTime();
19362        // Roo.log(e);
19363         var ret = [];
19364         this.cells.each(function(c){
19365              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19366             
19367             if(c.dateValue > e){
19368                 return ;
19369             }
19370             if(c.dateValue < s){
19371                 return ;
19372             }
19373             ret.push(c);
19374         });
19375         
19376         return ret;    
19377     },
19378     
19379 //    findBestRow: function(cells)
19380 //    {
19381 //        var ret = 0;
19382 //        
19383 //        for (var i =0 ; i < cells.length;i++) {
19384 //            ret  = Math.max(cells[i].rows || 0,ret);
19385 //        }
19386 //        return ret;
19387 //        
19388 //    },
19389     
19390     
19391     addItem : function(ev)
19392     {
19393         // look for vertical location slot in
19394         var cells = this.findCells(ev);
19395         
19396 //        ev.row = this.findBestRow(cells);
19397         
19398         // work out the location.
19399         
19400         var crow = false;
19401         var rows = [];
19402         for(var i =0; i < cells.length; i++) {
19403             
19404             cells[i].row = cells[0].row;
19405             
19406             if(i == 0){
19407                 cells[i].row = cells[i].row + 1;
19408             }
19409             
19410             if (!crow) {
19411                 crow = {
19412                     start : cells[i],
19413                     end :  cells[i]
19414                 };
19415                 continue;
19416             }
19417             if (crow.start.getY() == cells[i].getY()) {
19418                 // on same row.
19419                 crow.end = cells[i];
19420                 continue;
19421             }
19422             // different row.
19423             rows.push(crow);
19424             crow = {
19425                 start: cells[i],
19426                 end : cells[i]
19427             };
19428             
19429         }
19430         
19431         rows.push(crow);
19432         ev.els = [];
19433         ev.rows = rows;
19434         ev.cells = cells;
19435         
19436         cells[0].events.push(ev);
19437         
19438         this.calevents.push(ev);
19439     },
19440     
19441     clearEvents: function() {
19442         
19443         if(!this.calevents){
19444             return;
19445         }
19446         
19447         Roo.each(this.cells.elements, function(c){
19448             c.row = 0;
19449             c.events = [];
19450             c.more = [];
19451         });
19452         
19453         Roo.each(this.calevents, function(e) {
19454             Roo.each(e.els, function(el) {
19455                 el.un('mouseenter' ,this.onEventEnter, this);
19456                 el.un('mouseleave' ,this.onEventLeave, this);
19457                 el.remove();
19458             },this);
19459         },this);
19460         
19461         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19462             e.remove();
19463         });
19464         
19465     },
19466     
19467     renderEvents: function()
19468     {   
19469         var _this = this;
19470         
19471         this.cells.each(function(c) {
19472             
19473             if(c.row < 5){
19474                 return;
19475             }
19476             
19477             var ev = c.events;
19478             
19479             var r = 4;
19480             if(c.row != c.events.length){
19481                 r = 4 - (4 - (c.row - c.events.length));
19482             }
19483             
19484             c.events = ev.slice(0, r);
19485             c.more = ev.slice(r);
19486             
19487             if(c.more.length && c.more.length == 1){
19488                 c.events.push(c.more.pop());
19489             }
19490             
19491             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19492             
19493         });
19494             
19495         this.cells.each(function(c) {
19496             
19497             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19498             
19499             
19500             for (var e = 0; e < c.events.length; e++){
19501                 var ev = c.events[e];
19502                 var rows = ev.rows;
19503                 
19504                 for(var i = 0; i < rows.length; i++) {
19505                 
19506                     // how many rows should it span..
19507
19508                     var  cfg = {
19509                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19510                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19511
19512                         unselectable : "on",
19513                         cn : [
19514                             {
19515                                 cls: 'fc-event-inner',
19516                                 cn : [
19517     //                                {
19518     //                                  tag:'span',
19519     //                                  cls: 'fc-event-time',
19520     //                                  html : cells.length > 1 ? '' : ev.time
19521     //                                },
19522                                     {
19523                                       tag:'span',
19524                                       cls: 'fc-event-title',
19525                                       html : String.format('{0}', ev.title)
19526                                     }
19527
19528
19529                                 ]
19530                             },
19531                             {
19532                                 cls: 'ui-resizable-handle ui-resizable-e',
19533                                 html : '&nbsp;&nbsp;&nbsp'
19534                             }
19535
19536                         ]
19537                     };
19538
19539                     if (i == 0) {
19540                         cfg.cls += ' fc-event-start';
19541                     }
19542                     if ((i+1) == rows.length) {
19543                         cfg.cls += ' fc-event-end';
19544                     }
19545
19546                     var ctr = _this.el.select('.fc-event-container',true).first();
19547                     var cg = ctr.createChild(cfg);
19548
19549                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19550                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19551
19552                     var r = (c.more.length) ? 1 : 0;
19553                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19554                     cg.setWidth(ebox.right - sbox.x -2);
19555
19556                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19557                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19558                     cg.on('click', _this.onEventClick, _this, ev);
19559
19560                     ev.els.push(cg);
19561                     
19562                 }
19563                 
19564             }
19565             
19566             
19567             if(c.more.length){
19568                 var  cfg = {
19569                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19570                     style : 'position: absolute',
19571                     unselectable : "on",
19572                     cn : [
19573                         {
19574                             cls: 'fc-event-inner',
19575                             cn : [
19576                                 {
19577                                   tag:'span',
19578                                   cls: 'fc-event-title',
19579                                   html : 'More'
19580                                 }
19581
19582
19583                             ]
19584                         },
19585                         {
19586                             cls: 'ui-resizable-handle ui-resizable-e',
19587                             html : '&nbsp;&nbsp;&nbsp'
19588                         }
19589
19590                     ]
19591                 };
19592
19593                 var ctr = _this.el.select('.fc-event-container',true).first();
19594                 var cg = ctr.createChild(cfg);
19595
19596                 var sbox = c.select('.fc-day-content',true).first().getBox();
19597                 var ebox = c.select('.fc-day-content',true).first().getBox();
19598                 //Roo.log(cg);
19599                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19600                 cg.setWidth(ebox.right - sbox.x -2);
19601
19602                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19603                 
19604             }
19605             
19606         });
19607         
19608         
19609         
19610     },
19611     
19612     onEventEnter: function (e, el,event,d) {
19613         this.fireEvent('evententer', this, el, event);
19614     },
19615     
19616     onEventLeave: function (e, el,event,d) {
19617         this.fireEvent('eventleave', this, el, event);
19618     },
19619     
19620     onEventClick: function (e, el,event,d) {
19621         this.fireEvent('eventclick', this, el, event);
19622     },
19623     
19624     onMonthChange: function () {
19625         this.store.load();
19626     },
19627     
19628     onMoreEventClick: function(e, el, more)
19629     {
19630         var _this = this;
19631         
19632         this.calpopover.placement = 'right';
19633         this.calpopover.setTitle('More');
19634         
19635         this.calpopover.setContent('');
19636         
19637         var ctr = this.calpopover.el.select('.popover-content', true).first();
19638         
19639         Roo.each(more, function(m){
19640             var cfg = {
19641                 cls : 'fc-event-hori fc-event-draggable',
19642                 html : m.title
19643             };
19644             var cg = ctr.createChild(cfg);
19645             
19646             cg.on('click', _this.onEventClick, _this, m);
19647         });
19648         
19649         this.calpopover.show(el);
19650         
19651         
19652     },
19653     
19654     onLoad: function () 
19655     {   
19656         this.calevents = [];
19657         var cal = this;
19658         
19659         if(this.store.getCount() > 0){
19660             this.store.data.each(function(d){
19661                cal.addItem({
19662                     id : d.data.id,
19663                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19664                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19665                     time : d.data.start_time,
19666                     title : d.data.title,
19667                     description : d.data.description,
19668                     venue : d.data.venue
19669                 });
19670             });
19671         }
19672         
19673         this.renderEvents();
19674         
19675         if(this.calevents.length && this.loadMask){
19676             this.maskEl.hide();
19677         }
19678     },
19679     
19680     onBeforeLoad: function()
19681     {
19682         this.clearEvents();
19683         if(this.loadMask){
19684             this.maskEl.show();
19685         }
19686     }
19687 });
19688
19689  
19690  /*
19691  * - LGPL
19692  *
19693  * element
19694  * 
19695  */
19696
19697 /**
19698  * @class Roo.bootstrap.Popover
19699  * @extends Roo.bootstrap.Component
19700  * Bootstrap Popover class
19701  * @cfg {String} html contents of the popover   (or false to use children..)
19702  * @cfg {String} title of popover (or false to hide)
19703  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19704  * @cfg {String} trigger click || hover (or false to trigger manually)
19705  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19706  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19707  *      - if false and it has a 'parent' then it will be automatically added to that element
19708  *      - if string - Roo.get  will be called 
19709  * @cfg {Number} delay - delay before showing
19710  
19711  * @constructor
19712  * Create a new Popover
19713  * @param {Object} config The config object
19714  */
19715
19716 Roo.bootstrap.Popover = function(config){
19717     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19718     
19719     this.addEvents({
19720         // raw events
19721          /**
19722          * @event show
19723          * After the popover show
19724          * 
19725          * @param {Roo.bootstrap.Popover} this
19726          */
19727         "show" : true,
19728         /**
19729          * @event hide
19730          * After the popover hide
19731          * 
19732          * @param {Roo.bootstrap.Popover} this
19733          */
19734         "hide" : true
19735     });
19736 };
19737
19738 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19739     
19740     title: false,
19741     html: false,
19742     
19743     placement : 'right',
19744     trigger : 'hover', // hover
19745     modal : false,
19746     delay : 0,
19747     
19748     over: false,
19749     
19750     can_build_overlaid : false,
19751     
19752     maskEl : false, // the mask element
19753     headerEl : false,
19754     contentEl : false,
19755     alignEl : false, // when show is called with an element - this get's stored.
19756     
19757     getChildContainer : function()
19758     {
19759         return this.contentEl;
19760         
19761     },
19762     getPopoverHeader : function()
19763     {
19764         this.title = true; // flag not to hide it..
19765         this.headerEl.addClass('p-0');
19766         return this.headerEl
19767     },
19768     
19769     
19770     getAutoCreate : function(){
19771          
19772         var cfg = {
19773            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19774            style: 'display:block',
19775            cn : [
19776                 {
19777                     cls : 'arrow'
19778                 },
19779                 {
19780                     cls : 'popover-inner ',
19781                     cn : [
19782                         {
19783                             tag: 'h3',
19784                             cls: 'popover-title popover-header',
19785                             html : this.title === false ? '' : this.title
19786                         },
19787                         {
19788                             cls : 'popover-content popover-body '  + (this.cls || ''),
19789                             html : this.html || ''
19790                         }
19791                     ]
19792                     
19793                 }
19794            ]
19795         };
19796         
19797         return cfg;
19798     },
19799     /**
19800      * @param {string} the title
19801      */
19802     setTitle: function(str)
19803     {
19804         this.title = str;
19805         if (this.el) {
19806             this.headerEl.dom.innerHTML = str;
19807         }
19808         
19809     },
19810     /**
19811      * @param {string} the body content
19812      */
19813     setContent: function(str)
19814     {
19815         this.html = str;
19816         if (this.contentEl) {
19817             this.contentEl.dom.innerHTML = str;
19818         }
19819         
19820     },
19821     // as it get's added to the bottom of the page.
19822     onRender : function(ct, position)
19823     {
19824         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19825         
19826         
19827         
19828         if(!this.el){
19829             var cfg = Roo.apply({},  this.getAutoCreate());
19830             cfg.id = Roo.id();
19831             
19832             if (this.cls) {
19833                 cfg.cls += ' ' + this.cls;
19834             }
19835             if (this.style) {
19836                 cfg.style = this.style;
19837             }
19838             //Roo.log("adding to ");
19839             this.el = Roo.get(document.body).createChild(cfg, position);
19840 //            Roo.log(this.el);
19841         }
19842         
19843         this.contentEl = this.el.select('.popover-content',true).first();
19844         this.headerEl =  this.el.select('.popover-title',true).first();
19845         
19846         var nitems = [];
19847         if(typeof(this.items) != 'undefined'){
19848             var items = this.items;
19849             delete this.items;
19850
19851             for(var i =0;i < items.length;i++) {
19852                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19853             }
19854         }
19855
19856         this.items = nitems;
19857         
19858         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19859         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19860         
19861         
19862         
19863         this.initEvents();
19864     },
19865     
19866     resizeMask : function()
19867     {
19868         this.maskEl.setSize(
19869             Roo.lib.Dom.getViewWidth(true),
19870             Roo.lib.Dom.getViewHeight(true)
19871         );
19872     },
19873     
19874     initEvents : function()
19875     {
19876         
19877         if (!this.modal) { 
19878             Roo.bootstrap.Popover.register(this);
19879         }
19880          
19881         this.arrowEl = this.el.select('.arrow',true).first();
19882         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19883         this.el.enableDisplayMode('block');
19884         this.el.hide();
19885  
19886         
19887         if (this.over === false && !this.parent()) {
19888             return; 
19889         }
19890         if (this.triggers === false) {
19891             return;
19892         }
19893          
19894         // support parent
19895         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19896         var triggers = this.trigger ? this.trigger.split(' ') : [];
19897         Roo.each(triggers, function(trigger) {
19898         
19899             if (trigger == 'click') {
19900                 on_el.on('click', this.toggle, this);
19901             } else if (trigger != 'manual') {
19902                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19903                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19904       
19905                 on_el.on(eventIn  ,this.enter, this);
19906                 on_el.on(eventOut, this.leave, this);
19907             }
19908         }, this);
19909     },
19910     
19911     
19912     // private
19913     timeout : null,
19914     hoverState : null,
19915     
19916     toggle : function () {
19917         this.hoverState == 'in' ? this.leave() : this.enter();
19918     },
19919     
19920     enter : function () {
19921         
19922         clearTimeout(this.timeout);
19923     
19924         this.hoverState = 'in';
19925     
19926         if (!this.delay || !this.delay.show) {
19927             this.show();
19928             return;
19929         }
19930         var _t = this;
19931         this.timeout = setTimeout(function () {
19932             if (_t.hoverState == 'in') {
19933                 _t.show();
19934             }
19935         }, this.delay.show)
19936     },
19937     
19938     leave : function() {
19939         clearTimeout(this.timeout);
19940     
19941         this.hoverState = 'out';
19942     
19943         if (!this.delay || !this.delay.hide) {
19944             this.hide();
19945             return;
19946         }
19947         var _t = this;
19948         this.timeout = setTimeout(function () {
19949             if (_t.hoverState == 'out') {
19950                 _t.hide();
19951             }
19952         }, this.delay.hide)
19953     },
19954     /**
19955      * Show the popover
19956      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19957      * @param {string} (left|right|top|bottom) position
19958      */
19959     show : function (on_el, placement)
19960     {
19961         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19962         on_el = on_el || false; // default to false
19963          
19964         if (!on_el) {
19965             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19966                 on_el = this.parent().el;
19967             } else if (this.over) {
19968                 Roo.get(this.over);
19969             }
19970             
19971         }
19972         
19973         this.alignEl = Roo.get( on_el );
19974
19975         if (!this.el) {
19976             this.render(document.body);
19977         }
19978         
19979         
19980          
19981         
19982         if (this.title === false) {
19983             this.headerEl.hide();
19984         }
19985         
19986        
19987         this.el.show();
19988         this.el.dom.style.display = 'block';
19989          
19990  
19991         if (this.alignEl) {
19992             this.updatePosition(this.placement, true);
19993              
19994         } else {
19995             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19996             var es = this.el.getSize();
19997             var x = Roo.lib.Dom.getViewWidth()/2;
19998             var y = Roo.lib.Dom.getViewHeight()/2;
19999             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20000             
20001         }
20002
20003         
20004         //var arrow = this.el.select('.arrow',true).first();
20005         //arrow.set(align[2], 
20006         
20007         this.el.addClass('in');
20008         
20009          
20010         
20011         this.hoverState = 'in';
20012         
20013         if (this.modal) {
20014             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20015             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20016             this.maskEl.dom.style.display = 'block';
20017             this.maskEl.addClass('show');
20018         }
20019         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20020  
20021         this.fireEvent('show', this);
20022         
20023     },
20024     /**
20025      * fire this manually after loading a grid in the table for example
20026      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20027      * @param {Boolean} try and move it if we cant get right position.
20028      */
20029     updatePosition : function(placement, try_move)
20030     {
20031         // allow for calling with no parameters
20032         placement = placement   ? placement :  this.placement;
20033         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20034         
20035         this.el.removeClass([
20036             'fade','top','bottom', 'left', 'right','in',
20037             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20038         ]);
20039         this.el.addClass(placement + ' bs-popover-' + placement);
20040         
20041         if (!this.alignEl ) {
20042             return false;
20043         }
20044         
20045         switch (placement) {
20046             case 'right':
20047                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20048                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20049                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20050                     //normal display... or moved up/down.
20051                     this.el.setXY(offset);
20052                     var xy = this.alignEl.getAnchorXY('tr', false);
20053                     xy[0]+=2;xy[1]+=5;
20054                     this.arrowEl.setXY(xy);
20055                     return true;
20056                 }
20057                 // continue through...
20058                 return this.updatePosition('left', false);
20059                 
20060             
20061             case 'left':
20062                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20063                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20064                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20065                     //normal display... or moved up/down.
20066                     this.el.setXY(offset);
20067                     var xy = this.alignEl.getAnchorXY('tl', false);
20068                     xy[0]-=10;xy[1]+=5; // << fix me
20069                     this.arrowEl.setXY(xy);
20070                     return true;
20071                 }
20072                 // call self...
20073                 return this.updatePosition('right', false);
20074             
20075             case 'top':
20076                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20077                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20078                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20079                     //normal display... or moved up/down.
20080                     this.el.setXY(offset);
20081                     var xy = this.alignEl.getAnchorXY('t', false);
20082                     xy[1]-=10; // << fix me
20083                     this.arrowEl.setXY(xy);
20084                     return true;
20085                 }
20086                 // fall through
20087                return this.updatePosition('bottom', false);
20088             
20089             case 'bottom':
20090                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20091                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20092                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20093                     //normal display... or moved up/down.
20094                     this.el.setXY(offset);
20095                     var xy = this.alignEl.getAnchorXY('b', false);
20096                      xy[1]+=2; // << fix me
20097                     this.arrowEl.setXY(xy);
20098                     return true;
20099                 }
20100                 // fall through
20101                 return this.updatePosition('top', false);
20102                 
20103             
20104         }
20105         
20106         
20107         return false;
20108     },
20109     
20110     hide : function()
20111     {
20112         this.el.setXY([0,0]);
20113         this.el.removeClass('in');
20114         this.el.hide();
20115         this.hoverState = null;
20116         this.maskEl.hide(); // always..
20117         this.fireEvent('hide', this);
20118     }
20119     
20120 });
20121
20122
20123 Roo.apply(Roo.bootstrap.Popover, {
20124
20125     alignment : {
20126         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20127         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20128         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20129         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20130     },
20131     
20132     zIndex : 20001,
20133
20134     clickHander : false,
20135     
20136
20137     onMouseDown : function(e)
20138     {
20139         if (!e.getTarget(".roo-popover")) {
20140             this.hideAll();
20141         }
20142          
20143     },
20144     
20145     popups : [],
20146     
20147     register : function(popup)
20148     {
20149         if (!Roo.bootstrap.Popover.clickHandler) {
20150             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20151         }
20152         // hide other popups.
20153         this.hideAll();
20154         this.popups.push(popup);
20155     },
20156     hideAll : function()
20157     {
20158         this.popups.forEach(function(p) {
20159             p.hide();
20160         });
20161     }
20162
20163 });/*
20164  * - LGPL
20165  *
20166  * Card header - holder for the card header elements.
20167  * 
20168  */
20169
20170 /**
20171  * @class Roo.bootstrap.PopoverNav
20172  * @extends Roo.bootstrap.NavGroup
20173  * Bootstrap Popover header navigation class
20174  * @constructor
20175  * Create a new Popover Header Navigation 
20176  * @param {Object} config The config object
20177  */
20178
20179 Roo.bootstrap.PopoverNav = function(config){
20180     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20181 };
20182
20183 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20184     
20185     
20186     container_method : 'getPopoverHeader' 
20187     
20188      
20189     
20190     
20191    
20192 });
20193
20194  
20195
20196  /*
20197  * - LGPL
20198  *
20199  * Progress
20200  * 
20201  */
20202
20203 /**
20204  * @class Roo.bootstrap.Progress
20205  * @extends Roo.bootstrap.Component
20206  * Bootstrap Progress class
20207  * @cfg {Boolean} striped striped of the progress bar
20208  * @cfg {Boolean} active animated of the progress bar
20209  * 
20210  * 
20211  * @constructor
20212  * Create a new Progress
20213  * @param {Object} config The config object
20214  */
20215
20216 Roo.bootstrap.Progress = function(config){
20217     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20218 };
20219
20220 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20221     
20222     striped : false,
20223     active: false,
20224     
20225     getAutoCreate : function(){
20226         var cfg = {
20227             tag: 'div',
20228             cls: 'progress'
20229         };
20230         
20231         
20232         if(this.striped){
20233             cfg.cls += ' progress-striped';
20234         }
20235       
20236         if(this.active){
20237             cfg.cls += ' active';
20238         }
20239         
20240         
20241         return cfg;
20242     }
20243    
20244 });
20245
20246  
20247
20248  /*
20249  * - LGPL
20250  *
20251  * ProgressBar
20252  * 
20253  */
20254
20255 /**
20256  * @class Roo.bootstrap.ProgressBar
20257  * @extends Roo.bootstrap.Component
20258  * Bootstrap ProgressBar class
20259  * @cfg {Number} aria_valuenow aria-value now
20260  * @cfg {Number} aria_valuemin aria-value min
20261  * @cfg {Number} aria_valuemax aria-value max
20262  * @cfg {String} label label for the progress bar
20263  * @cfg {String} panel (success | info | warning | danger )
20264  * @cfg {String} role role of the progress bar
20265  * @cfg {String} sr_only text
20266  * 
20267  * 
20268  * @constructor
20269  * Create a new ProgressBar
20270  * @param {Object} config The config object
20271  */
20272
20273 Roo.bootstrap.ProgressBar = function(config){
20274     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20275 };
20276
20277 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20278     
20279     aria_valuenow : 0,
20280     aria_valuemin : 0,
20281     aria_valuemax : 100,
20282     label : false,
20283     panel : false,
20284     role : false,
20285     sr_only: false,
20286     
20287     getAutoCreate : function()
20288     {
20289         
20290         var cfg = {
20291             tag: 'div',
20292             cls: 'progress-bar',
20293             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20294         };
20295         
20296         if(this.sr_only){
20297             cfg.cn = {
20298                 tag: 'span',
20299                 cls: 'sr-only',
20300                 html: this.sr_only
20301             }
20302         }
20303         
20304         if(this.role){
20305             cfg.role = this.role;
20306         }
20307         
20308         if(this.aria_valuenow){
20309             cfg['aria-valuenow'] = this.aria_valuenow;
20310         }
20311         
20312         if(this.aria_valuemin){
20313             cfg['aria-valuemin'] = this.aria_valuemin;
20314         }
20315         
20316         if(this.aria_valuemax){
20317             cfg['aria-valuemax'] = this.aria_valuemax;
20318         }
20319         
20320         if(this.label && !this.sr_only){
20321             cfg.html = this.label;
20322         }
20323         
20324         if(this.panel){
20325             cfg.cls += ' progress-bar-' + this.panel;
20326         }
20327         
20328         return cfg;
20329     },
20330     
20331     update : function(aria_valuenow)
20332     {
20333         this.aria_valuenow = aria_valuenow;
20334         
20335         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20336     }
20337    
20338 });
20339
20340  
20341
20342  /*
20343  * - LGPL
20344  *
20345  * column
20346  * 
20347  */
20348
20349 /**
20350  * @class Roo.bootstrap.TabGroup
20351  * @extends Roo.bootstrap.Column
20352  * Bootstrap Column class
20353  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20354  * @cfg {Boolean} carousel true to make the group behave like a carousel
20355  * @cfg {Boolean} bullets show bullets for the panels
20356  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20357  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20358  * @cfg {Boolean} showarrow (true|false) show arrow default true
20359  * 
20360  * @constructor
20361  * Create a new TabGroup
20362  * @param {Object} config The config object
20363  */
20364
20365 Roo.bootstrap.TabGroup = function(config){
20366     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20367     if (!this.navId) {
20368         this.navId = Roo.id();
20369     }
20370     this.tabs = [];
20371     Roo.bootstrap.TabGroup.register(this);
20372     
20373 };
20374
20375 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20376     
20377     carousel : false,
20378     transition : false,
20379     bullets : 0,
20380     timer : 0,
20381     autoslide : false,
20382     slideFn : false,
20383     slideOnTouch : false,
20384     showarrow : true,
20385     
20386     getAutoCreate : function()
20387     {
20388         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20389         
20390         cfg.cls += ' tab-content';
20391         
20392         if (this.carousel) {
20393             cfg.cls += ' carousel slide';
20394             
20395             cfg.cn = [{
20396                cls : 'carousel-inner',
20397                cn : []
20398             }];
20399         
20400             if(this.bullets  && !Roo.isTouch){
20401                 
20402                 var bullets = {
20403                     cls : 'carousel-bullets',
20404                     cn : []
20405                 };
20406                
20407                 if(this.bullets_cls){
20408                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20409                 }
20410                 
20411                 bullets.cn.push({
20412                     cls : 'clear'
20413                 });
20414                 
20415                 cfg.cn[0].cn.push(bullets);
20416             }
20417             
20418             if(this.showarrow){
20419                 cfg.cn[0].cn.push({
20420                     tag : 'div',
20421                     class : 'carousel-arrow',
20422                     cn : [
20423                         {
20424                             tag : 'div',
20425                             class : 'carousel-prev',
20426                             cn : [
20427                                 {
20428                                     tag : 'i',
20429                                     class : 'fa fa-chevron-left'
20430                                 }
20431                             ]
20432                         },
20433                         {
20434                             tag : 'div',
20435                             class : 'carousel-next',
20436                             cn : [
20437                                 {
20438                                     tag : 'i',
20439                                     class : 'fa fa-chevron-right'
20440                                 }
20441                             ]
20442                         }
20443                     ]
20444                 });
20445             }
20446             
20447         }
20448         
20449         return cfg;
20450     },
20451     
20452     initEvents:  function()
20453     {
20454 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20455 //            this.el.on("touchstart", this.onTouchStart, this);
20456 //        }
20457         
20458         if(this.autoslide){
20459             var _this = this;
20460             
20461             this.slideFn = window.setInterval(function() {
20462                 _this.showPanelNext();
20463             }, this.timer);
20464         }
20465         
20466         if(this.showarrow){
20467             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20468             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20469         }
20470         
20471         
20472     },
20473     
20474 //    onTouchStart : function(e, el, o)
20475 //    {
20476 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20477 //            return;
20478 //        }
20479 //        
20480 //        this.showPanelNext();
20481 //    },
20482     
20483     
20484     getChildContainer : function()
20485     {
20486         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20487     },
20488     
20489     /**
20490     * register a Navigation item
20491     * @param {Roo.bootstrap.NavItem} the navitem to add
20492     */
20493     register : function(item)
20494     {
20495         this.tabs.push( item);
20496         item.navId = this.navId; // not really needed..
20497         this.addBullet();
20498     
20499     },
20500     
20501     getActivePanel : function()
20502     {
20503         var r = false;
20504         Roo.each(this.tabs, function(t) {
20505             if (t.active) {
20506                 r = t;
20507                 return false;
20508             }
20509             return null;
20510         });
20511         return r;
20512         
20513     },
20514     getPanelByName : function(n)
20515     {
20516         var r = false;
20517         Roo.each(this.tabs, function(t) {
20518             if (t.tabId == n) {
20519                 r = t;
20520                 return false;
20521             }
20522             return null;
20523         });
20524         return r;
20525     },
20526     indexOfPanel : function(p)
20527     {
20528         var r = false;
20529         Roo.each(this.tabs, function(t,i) {
20530             if (t.tabId == p.tabId) {
20531                 r = i;
20532                 return false;
20533             }
20534             return null;
20535         });
20536         return r;
20537     },
20538     /**
20539      * show a specific panel
20540      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20541      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20542      */
20543     showPanel : function (pan)
20544     {
20545         if(this.transition || typeof(pan) == 'undefined'){
20546             Roo.log("waiting for the transitionend");
20547             return false;
20548         }
20549         
20550         if (typeof(pan) == 'number') {
20551             pan = this.tabs[pan];
20552         }
20553         
20554         if (typeof(pan) == 'string') {
20555             pan = this.getPanelByName(pan);
20556         }
20557         
20558         var cur = this.getActivePanel();
20559         
20560         if(!pan || !cur){
20561             Roo.log('pan or acitve pan is undefined');
20562             return false;
20563         }
20564         
20565         if (pan.tabId == this.getActivePanel().tabId) {
20566             return true;
20567         }
20568         
20569         if (false === cur.fireEvent('beforedeactivate')) {
20570             return false;
20571         }
20572         
20573         if(this.bullets > 0 && !Roo.isTouch){
20574             this.setActiveBullet(this.indexOfPanel(pan));
20575         }
20576         
20577         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20578             
20579             //class="carousel-item carousel-item-next carousel-item-left"
20580             
20581             this.transition = true;
20582             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20583             var lr = dir == 'next' ? 'left' : 'right';
20584             pan.el.addClass(dir); // or prev
20585             pan.el.addClass('carousel-item-' + dir); // or prev
20586             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20587             cur.el.addClass(lr); // or right
20588             pan.el.addClass(lr);
20589             cur.el.addClass('carousel-item-' +lr); // or right
20590             pan.el.addClass('carousel-item-' +lr);
20591             
20592             
20593             var _this = this;
20594             cur.el.on('transitionend', function() {
20595                 Roo.log("trans end?");
20596                 
20597                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20598                 pan.setActive(true);
20599                 
20600                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20601                 cur.setActive(false);
20602                 
20603                 _this.transition = false;
20604                 
20605             }, this, { single:  true } );
20606             
20607             return true;
20608         }
20609         
20610         cur.setActive(false);
20611         pan.setActive(true);
20612         
20613         return true;
20614         
20615     },
20616     showPanelNext : function()
20617     {
20618         var i = this.indexOfPanel(this.getActivePanel());
20619         
20620         if (i >= this.tabs.length - 1 && !this.autoslide) {
20621             return;
20622         }
20623         
20624         if (i >= this.tabs.length - 1 && this.autoslide) {
20625             i = -1;
20626         }
20627         
20628         this.showPanel(this.tabs[i+1]);
20629     },
20630     
20631     showPanelPrev : function()
20632     {
20633         var i = this.indexOfPanel(this.getActivePanel());
20634         
20635         if (i  < 1 && !this.autoslide) {
20636             return;
20637         }
20638         
20639         if (i < 1 && this.autoslide) {
20640             i = this.tabs.length;
20641         }
20642         
20643         this.showPanel(this.tabs[i-1]);
20644     },
20645     
20646     
20647     addBullet: function()
20648     {
20649         if(!this.bullets || Roo.isTouch){
20650             return;
20651         }
20652         var ctr = this.el.select('.carousel-bullets',true).first();
20653         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20654         var bullet = ctr.createChild({
20655             cls : 'bullet bullet-' + i
20656         },ctr.dom.lastChild);
20657         
20658         
20659         var _this = this;
20660         
20661         bullet.on('click', (function(e, el, o, ii, t){
20662
20663             e.preventDefault();
20664
20665             this.showPanel(ii);
20666
20667             if(this.autoslide && this.slideFn){
20668                 clearInterval(this.slideFn);
20669                 this.slideFn = window.setInterval(function() {
20670                     _this.showPanelNext();
20671                 }, this.timer);
20672             }
20673
20674         }).createDelegate(this, [i, bullet], true));
20675                 
20676         
20677     },
20678      
20679     setActiveBullet : function(i)
20680     {
20681         if(Roo.isTouch){
20682             return;
20683         }
20684         
20685         Roo.each(this.el.select('.bullet', true).elements, function(el){
20686             el.removeClass('selected');
20687         });
20688
20689         var bullet = this.el.select('.bullet-' + i, true).first();
20690         
20691         if(!bullet){
20692             return;
20693         }
20694         
20695         bullet.addClass('selected');
20696     }
20697     
20698     
20699   
20700 });
20701
20702  
20703
20704  
20705  
20706 Roo.apply(Roo.bootstrap.TabGroup, {
20707     
20708     groups: {},
20709      /**
20710     * register a Navigation Group
20711     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20712     */
20713     register : function(navgrp)
20714     {
20715         this.groups[navgrp.navId] = navgrp;
20716         
20717     },
20718     /**
20719     * fetch a Navigation Group based on the navigation ID
20720     * if one does not exist , it will get created.
20721     * @param {string} the navgroup to add
20722     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20723     */
20724     get: function(navId) {
20725         if (typeof(this.groups[navId]) == 'undefined') {
20726             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20727         }
20728         return this.groups[navId] ;
20729     }
20730     
20731     
20732     
20733 });
20734
20735  /*
20736  * - LGPL
20737  *
20738  * TabPanel
20739  * 
20740  */
20741
20742 /**
20743  * @class Roo.bootstrap.TabPanel
20744  * @extends Roo.bootstrap.Component
20745  * Bootstrap TabPanel class
20746  * @cfg {Boolean} active panel active
20747  * @cfg {String} html panel content
20748  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20749  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20750  * @cfg {String} href click to link..
20751  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20752  * 
20753  * 
20754  * @constructor
20755  * Create a new TabPanel
20756  * @param {Object} config The config object
20757  */
20758
20759 Roo.bootstrap.TabPanel = function(config){
20760     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20761     this.addEvents({
20762         /**
20763              * @event changed
20764              * Fires when the active status changes
20765              * @param {Roo.bootstrap.TabPanel} this
20766              * @param {Boolean} state the new state
20767             
20768          */
20769         'changed': true,
20770         /**
20771              * @event beforedeactivate
20772              * Fires before a tab is de-activated - can be used to do validation on a form.
20773              * @param {Roo.bootstrap.TabPanel} this
20774              * @return {Boolean} false if there is an error
20775             
20776          */
20777         'beforedeactivate': true
20778      });
20779     
20780     this.tabId = this.tabId || Roo.id();
20781   
20782 };
20783
20784 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20785     
20786     active: false,
20787     html: false,
20788     tabId: false,
20789     navId : false,
20790     href : '',
20791     touchSlide : false,
20792     getAutoCreate : function(){
20793         
20794         
20795         var cfg = {
20796             tag: 'div',
20797             // item is needed for carousel - not sure if it has any effect otherwise
20798             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20799             html: this.html || ''
20800         };
20801         
20802         if(this.active){
20803             cfg.cls += ' active';
20804         }
20805         
20806         if(this.tabId){
20807             cfg.tabId = this.tabId;
20808         }
20809         
20810         
20811         
20812         return cfg;
20813     },
20814     
20815     initEvents:  function()
20816     {
20817         var p = this.parent();
20818         
20819         this.navId = this.navId || p.navId;
20820         
20821         if (typeof(this.navId) != 'undefined') {
20822             // not really needed.. but just in case.. parent should be a NavGroup.
20823             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20824             
20825             tg.register(this);
20826             
20827             var i = tg.tabs.length - 1;
20828             
20829             if(this.active && tg.bullets > 0 && i < tg.bullets){
20830                 tg.setActiveBullet(i);
20831             }
20832         }
20833         
20834         this.el.on('click', this.onClick, this);
20835         
20836         if(Roo.isTouch && this.touchSlide){
20837             this.el.on("touchstart", this.onTouchStart, this);
20838             this.el.on("touchmove", this.onTouchMove, this);
20839             this.el.on("touchend", this.onTouchEnd, this);
20840         }
20841         
20842     },
20843     
20844     onRender : function(ct, position)
20845     {
20846         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20847     },
20848     
20849     setActive : function(state)
20850     {
20851         Roo.log("panel - set active " + this.tabId + "=" + state);
20852         
20853         this.active = state;
20854         if (!state) {
20855             this.el.removeClass('active');
20856             
20857         } else  if (!this.el.hasClass('active')) {
20858             this.el.addClass('active');
20859         }
20860         
20861         this.fireEvent('changed', this, state);
20862     },
20863     
20864     onClick : function(e)
20865     {
20866         e.preventDefault();
20867         
20868         if(!this.href.length){
20869             return;
20870         }
20871         
20872         window.location.href = this.href;
20873     },
20874     
20875     startX : 0,
20876     startY : 0,
20877     endX : 0,
20878     endY : 0,
20879     swiping : false,
20880     
20881     onTouchStart : function(e)
20882     {
20883         this.swiping = false;
20884         
20885         this.startX = e.browserEvent.touches[0].clientX;
20886         this.startY = e.browserEvent.touches[0].clientY;
20887     },
20888     
20889     onTouchMove : function(e)
20890     {
20891         this.swiping = true;
20892         
20893         this.endX = e.browserEvent.touches[0].clientX;
20894         this.endY = e.browserEvent.touches[0].clientY;
20895     },
20896     
20897     onTouchEnd : function(e)
20898     {
20899         if(!this.swiping){
20900             this.onClick(e);
20901             return;
20902         }
20903         
20904         var tabGroup = this.parent();
20905         
20906         if(this.endX > this.startX){ // swiping right
20907             tabGroup.showPanelPrev();
20908             return;
20909         }
20910         
20911         if(this.startX > this.endX){ // swiping left
20912             tabGroup.showPanelNext();
20913             return;
20914         }
20915     }
20916     
20917     
20918 });
20919  
20920
20921  
20922
20923  /*
20924  * - LGPL
20925  *
20926  * DateField
20927  * 
20928  */
20929
20930 /**
20931  * @class Roo.bootstrap.DateField
20932  * @extends Roo.bootstrap.Input
20933  * Bootstrap DateField class
20934  * @cfg {Number} weekStart default 0
20935  * @cfg {String} viewMode default empty, (months|years)
20936  * @cfg {String} minViewMode default empty, (months|years)
20937  * @cfg {Number} startDate default -Infinity
20938  * @cfg {Number} endDate default Infinity
20939  * @cfg {Boolean} todayHighlight default false
20940  * @cfg {Boolean} todayBtn default false
20941  * @cfg {Boolean} calendarWeeks default false
20942  * @cfg {Object} daysOfWeekDisabled default empty
20943  * @cfg {Boolean} singleMode default false (true | false)
20944  * 
20945  * @cfg {Boolean} keyboardNavigation default true
20946  * @cfg {String} language default en
20947  * 
20948  * @constructor
20949  * Create a new DateField
20950  * @param {Object} config The config object
20951  */
20952
20953 Roo.bootstrap.DateField = function(config){
20954     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20955      this.addEvents({
20956             /**
20957              * @event show
20958              * Fires when this field show.
20959              * @param {Roo.bootstrap.DateField} this
20960              * @param {Mixed} date The date value
20961              */
20962             show : true,
20963             /**
20964              * @event show
20965              * Fires when this field hide.
20966              * @param {Roo.bootstrap.DateField} this
20967              * @param {Mixed} date The date value
20968              */
20969             hide : true,
20970             /**
20971              * @event select
20972              * Fires when select a date.
20973              * @param {Roo.bootstrap.DateField} this
20974              * @param {Mixed} date The date value
20975              */
20976             select : true,
20977             /**
20978              * @event beforeselect
20979              * Fires when before select a date.
20980              * @param {Roo.bootstrap.DateField} this
20981              * @param {Mixed} date The date value
20982              */
20983             beforeselect : true
20984         });
20985 };
20986
20987 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20988     
20989     /**
20990      * @cfg {String} format
20991      * The default date format string which can be overriden for localization support.  The format must be
20992      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20993      */
20994     format : "m/d/y",
20995     /**
20996      * @cfg {String} altFormats
20997      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20998      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20999      */
21000     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21001     
21002     weekStart : 0,
21003     
21004     viewMode : '',
21005     
21006     minViewMode : '',
21007     
21008     todayHighlight : false,
21009     
21010     todayBtn: false,
21011     
21012     language: 'en',
21013     
21014     keyboardNavigation: true,
21015     
21016     calendarWeeks: false,
21017     
21018     startDate: -Infinity,
21019     
21020     endDate: Infinity,
21021     
21022     daysOfWeekDisabled: [],
21023     
21024     _events: [],
21025     
21026     singleMode : false,
21027     
21028     UTCDate: function()
21029     {
21030         return new Date(Date.UTC.apply(Date, arguments));
21031     },
21032     
21033     UTCToday: function()
21034     {
21035         var today = new Date();
21036         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21037     },
21038     
21039     getDate: function() {
21040             var d = this.getUTCDate();
21041             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21042     },
21043     
21044     getUTCDate: function() {
21045             return this.date;
21046     },
21047     
21048     setDate: function(d) {
21049             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21050     },
21051     
21052     setUTCDate: function(d) {
21053             this.date = d;
21054             this.setValue(this.formatDate(this.date));
21055     },
21056         
21057     onRender: function(ct, position)
21058     {
21059         
21060         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21061         
21062         this.language = this.language || 'en';
21063         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21064         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21065         
21066         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21067         this.format = this.format || 'm/d/y';
21068         this.isInline = false;
21069         this.isInput = true;
21070         this.component = this.el.select('.add-on', true).first() || false;
21071         this.component = (this.component && this.component.length === 0) ? false : this.component;
21072         this.hasInput = this.component && this.inputEl().length;
21073         
21074         if (typeof(this.minViewMode === 'string')) {
21075             switch (this.minViewMode) {
21076                 case 'months':
21077                     this.minViewMode = 1;
21078                     break;
21079                 case 'years':
21080                     this.minViewMode = 2;
21081                     break;
21082                 default:
21083                     this.minViewMode = 0;
21084                     break;
21085             }
21086         }
21087         
21088         if (typeof(this.viewMode === 'string')) {
21089             switch (this.viewMode) {
21090                 case 'months':
21091                     this.viewMode = 1;
21092                     break;
21093                 case 'years':
21094                     this.viewMode = 2;
21095                     break;
21096                 default:
21097                     this.viewMode = 0;
21098                     break;
21099             }
21100         }
21101                 
21102         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21103         
21104 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21105         
21106         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21107         
21108         this.picker().on('mousedown', this.onMousedown, this);
21109         this.picker().on('click', this.onClick, this);
21110         
21111         this.picker().addClass('datepicker-dropdown');
21112         
21113         this.startViewMode = this.viewMode;
21114         
21115         if(this.singleMode){
21116             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21117                 v.setVisibilityMode(Roo.Element.DISPLAY);
21118                 v.hide();
21119             });
21120             
21121             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21122                 v.setStyle('width', '189px');
21123             });
21124         }
21125         
21126         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21127             if(!this.calendarWeeks){
21128                 v.remove();
21129                 return;
21130             }
21131             
21132             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21133             v.attr('colspan', function(i, val){
21134                 return parseInt(val) + 1;
21135             });
21136         });
21137                         
21138         
21139         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21140         
21141         this.setStartDate(this.startDate);
21142         this.setEndDate(this.endDate);
21143         
21144         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21145         
21146         this.fillDow();
21147         this.fillMonths();
21148         this.update();
21149         this.showMode();
21150         
21151         if(this.isInline) {
21152             this.showPopup();
21153         }
21154     },
21155     
21156     picker : function()
21157     {
21158         return this.pickerEl;
21159 //        return this.el.select('.datepicker', true).first();
21160     },
21161     
21162     fillDow: function()
21163     {
21164         var dowCnt = this.weekStart;
21165         
21166         var dow = {
21167             tag: 'tr',
21168             cn: [
21169                 
21170             ]
21171         };
21172         
21173         if(this.calendarWeeks){
21174             dow.cn.push({
21175                 tag: 'th',
21176                 cls: 'cw',
21177                 html: '&nbsp;'
21178             })
21179         }
21180         
21181         while (dowCnt < this.weekStart + 7) {
21182             dow.cn.push({
21183                 tag: 'th',
21184                 cls: 'dow',
21185                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21186             });
21187         }
21188         
21189         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21190     },
21191     
21192     fillMonths: function()
21193     {    
21194         var i = 0;
21195         var months = this.picker().select('>.datepicker-months td', true).first();
21196         
21197         months.dom.innerHTML = '';
21198         
21199         while (i < 12) {
21200             var month = {
21201                 tag: 'span',
21202                 cls: 'month',
21203                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21204             };
21205             
21206             months.createChild(month);
21207         }
21208         
21209     },
21210     
21211     update: function()
21212     {
21213         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;
21214         
21215         if (this.date < this.startDate) {
21216             this.viewDate = new Date(this.startDate);
21217         } else if (this.date > this.endDate) {
21218             this.viewDate = new Date(this.endDate);
21219         } else {
21220             this.viewDate = new Date(this.date);
21221         }
21222         
21223         this.fill();
21224     },
21225     
21226     fill: function() 
21227     {
21228         var d = new Date(this.viewDate),
21229                 year = d.getUTCFullYear(),
21230                 month = d.getUTCMonth(),
21231                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21232                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21233                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21234                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21235                 currentDate = this.date && this.date.valueOf(),
21236                 today = this.UTCToday();
21237         
21238         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21239         
21240 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21241         
21242 //        this.picker.select('>tfoot th.today').
21243 //                                              .text(dates[this.language].today)
21244 //                                              .toggle(this.todayBtn !== false);
21245     
21246         this.updateNavArrows();
21247         this.fillMonths();
21248                                                 
21249         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21250         
21251         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21252          
21253         prevMonth.setUTCDate(day);
21254         
21255         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21256         
21257         var nextMonth = new Date(prevMonth);
21258         
21259         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21260         
21261         nextMonth = nextMonth.valueOf();
21262         
21263         var fillMonths = false;
21264         
21265         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21266         
21267         while(prevMonth.valueOf() <= nextMonth) {
21268             var clsName = '';
21269             
21270             if (prevMonth.getUTCDay() === this.weekStart) {
21271                 if(fillMonths){
21272                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21273                 }
21274                     
21275                 fillMonths = {
21276                     tag: 'tr',
21277                     cn: []
21278                 };
21279                 
21280                 if(this.calendarWeeks){
21281                     // ISO 8601: First week contains first thursday.
21282                     // ISO also states week starts on Monday, but we can be more abstract here.
21283                     var
21284                     // Start of current week: based on weekstart/current date
21285                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21286                     // Thursday of this week
21287                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21288                     // First Thursday of year, year from thursday
21289                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21290                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21291                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21292                     
21293                     fillMonths.cn.push({
21294                         tag: 'td',
21295                         cls: 'cw',
21296                         html: calWeek
21297                     });
21298                 }
21299             }
21300             
21301             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21302                 clsName += ' old';
21303             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21304                 clsName += ' new';
21305             }
21306             if (this.todayHighlight &&
21307                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21308                 prevMonth.getUTCMonth() == today.getMonth() &&
21309                 prevMonth.getUTCDate() == today.getDate()) {
21310                 clsName += ' today';
21311             }
21312             
21313             if (currentDate && prevMonth.valueOf() === currentDate) {
21314                 clsName += ' active';
21315             }
21316             
21317             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21318                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21319                     clsName += ' disabled';
21320             }
21321             
21322             fillMonths.cn.push({
21323                 tag: 'td',
21324                 cls: 'day ' + clsName,
21325                 html: prevMonth.getDate()
21326             });
21327             
21328             prevMonth.setDate(prevMonth.getDate()+1);
21329         }
21330           
21331         var currentYear = this.date && this.date.getUTCFullYear();
21332         var currentMonth = this.date && this.date.getUTCMonth();
21333         
21334         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21335         
21336         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21337             v.removeClass('active');
21338             
21339             if(currentYear === year && k === currentMonth){
21340                 v.addClass('active');
21341             }
21342             
21343             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21344                 v.addClass('disabled');
21345             }
21346             
21347         });
21348         
21349         
21350         year = parseInt(year/10, 10) * 10;
21351         
21352         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21353         
21354         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21355         
21356         year -= 1;
21357         for (var i = -1; i < 11; i++) {
21358             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21359                 tag: 'span',
21360                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21361                 html: year
21362             });
21363             
21364             year += 1;
21365         }
21366     },
21367     
21368     showMode: function(dir) 
21369     {
21370         if (dir) {
21371             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21372         }
21373         
21374         Roo.each(this.picker().select('>div',true).elements, function(v){
21375             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21376             v.hide();
21377         });
21378         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21379     },
21380     
21381     place: function()
21382     {
21383         if(this.isInline) {
21384             return;
21385         }
21386         
21387         this.picker().removeClass(['bottom', 'top']);
21388         
21389         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21390             /*
21391              * place to the top of element!
21392              *
21393              */
21394             
21395             this.picker().addClass('top');
21396             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21397             
21398             return;
21399         }
21400         
21401         this.picker().addClass('bottom');
21402         
21403         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21404     },
21405     
21406     parseDate : function(value)
21407     {
21408         if(!value || value instanceof Date){
21409             return value;
21410         }
21411         var v = Date.parseDate(value, this.format);
21412         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21413             v = Date.parseDate(value, 'Y-m-d');
21414         }
21415         if(!v && this.altFormats){
21416             if(!this.altFormatsArray){
21417                 this.altFormatsArray = this.altFormats.split("|");
21418             }
21419             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21420                 v = Date.parseDate(value, this.altFormatsArray[i]);
21421             }
21422         }
21423         return v;
21424     },
21425     
21426     formatDate : function(date, fmt)
21427     {   
21428         return (!date || !(date instanceof Date)) ?
21429         date : date.dateFormat(fmt || this.format);
21430     },
21431     
21432     onFocus : function()
21433     {
21434         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21435         this.showPopup();
21436     },
21437     
21438     onBlur : function()
21439     {
21440         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21441         
21442         var d = this.inputEl().getValue();
21443         
21444         this.setValue(d);
21445                 
21446         this.hidePopup();
21447     },
21448     
21449     showPopup : function()
21450     {
21451         this.picker().show();
21452         this.update();
21453         this.place();
21454         
21455         this.fireEvent('showpopup', this, this.date);
21456     },
21457     
21458     hidePopup : function()
21459     {
21460         if(this.isInline) {
21461             return;
21462         }
21463         this.picker().hide();
21464         this.viewMode = this.startViewMode;
21465         this.showMode();
21466         
21467         this.fireEvent('hidepopup', this, this.date);
21468         
21469     },
21470     
21471     onMousedown: function(e)
21472     {
21473         e.stopPropagation();
21474         e.preventDefault();
21475     },
21476     
21477     keyup: function(e)
21478     {
21479         Roo.bootstrap.DateField.superclass.keyup.call(this);
21480         this.update();
21481     },
21482
21483     setValue: function(v)
21484     {
21485         if(this.fireEvent('beforeselect', this, v) !== false){
21486             var d = new Date(this.parseDate(v) ).clearTime();
21487         
21488             if(isNaN(d.getTime())){
21489                 this.date = this.viewDate = '';
21490                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21491                 return;
21492             }
21493
21494             v = this.formatDate(d);
21495
21496             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21497
21498             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21499
21500             this.update();
21501
21502             this.fireEvent('select', this, this.date);
21503         }
21504     },
21505     
21506     getValue: function()
21507     {
21508         return this.formatDate(this.date);
21509     },
21510     
21511     fireKey: function(e)
21512     {
21513         if (!this.picker().isVisible()){
21514             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21515                 this.showPopup();
21516             }
21517             return;
21518         }
21519         
21520         var dateChanged = false,
21521         dir, day, month,
21522         newDate, newViewDate;
21523         
21524         switch(e.keyCode){
21525             case 27: // escape
21526                 this.hidePopup();
21527                 e.preventDefault();
21528                 break;
21529             case 37: // left
21530             case 39: // right
21531                 if (!this.keyboardNavigation) {
21532                     break;
21533                 }
21534                 dir = e.keyCode == 37 ? -1 : 1;
21535                 
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);
21545                     newViewDate = new Date(this.viewDate);
21546                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
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 38: // up
21558             case 40: // down
21559                 if (!this.keyboardNavigation) {
21560                     break;
21561                 }
21562                 dir = e.keyCode == 38 ? -1 : 1;
21563                 if (e.ctrlKey){
21564                     newDate = this.moveYear(this.date, dir);
21565                     newViewDate = this.moveYear(this.viewDate, dir);
21566                 } else if (e.shiftKey){
21567                     newDate = this.moveMonth(this.date, dir);
21568                     newViewDate = this.moveMonth(this.viewDate, dir);
21569                 } else {
21570                     newDate = new Date(this.date);
21571                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21572                     newViewDate = new Date(this.viewDate);
21573                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21574                 }
21575                 if (this.dateWithinRange(newDate)){
21576                     this.date = newDate;
21577                     this.viewDate = newViewDate;
21578                     this.setValue(this.formatDate(this.date));
21579 //                    this.update();
21580                     e.preventDefault();
21581                     dateChanged = true;
21582                 }
21583                 break;
21584             case 13: // enter
21585                 this.setValue(this.formatDate(this.date));
21586                 this.hidePopup();
21587                 e.preventDefault();
21588                 break;
21589             case 9: // tab
21590                 this.setValue(this.formatDate(this.date));
21591                 this.hidePopup();
21592                 break;
21593             case 16: // shift
21594             case 17: // ctrl
21595             case 18: // alt
21596                 break;
21597             default :
21598                 this.hidePopup();
21599                 
21600         }
21601     },
21602     
21603     
21604     onClick: function(e) 
21605     {
21606         e.stopPropagation();
21607         e.preventDefault();
21608         
21609         var target = e.getTarget();
21610         
21611         if(target.nodeName.toLowerCase() === 'i'){
21612             target = Roo.get(target).dom.parentNode;
21613         }
21614         
21615         var nodeName = target.nodeName;
21616         var className = target.className;
21617         var html = target.innerHTML;
21618         //Roo.log(nodeName);
21619         
21620         switch(nodeName.toLowerCase()) {
21621             case 'th':
21622                 switch(className) {
21623                     case 'switch':
21624                         this.showMode(1);
21625                         break;
21626                     case 'prev':
21627                     case 'next':
21628                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21629                         switch(this.viewMode){
21630                                 case 0:
21631                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21632                                         break;
21633                                 case 1:
21634                                 case 2:
21635                                         this.viewDate = this.moveYear(this.viewDate, dir);
21636                                         break;
21637                         }
21638                         this.fill();
21639                         break;
21640                     case 'today':
21641                         var date = new Date();
21642                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21643 //                        this.fill()
21644                         this.setValue(this.formatDate(this.date));
21645                         
21646                         this.hidePopup();
21647                         break;
21648                 }
21649                 break;
21650             case 'span':
21651                 if (className.indexOf('disabled') < 0) {
21652                     this.viewDate.setUTCDate(1);
21653                     if (className.indexOf('month') > -1) {
21654                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21655                     } else {
21656                         var year = parseInt(html, 10) || 0;
21657                         this.viewDate.setUTCFullYear(year);
21658                         
21659                     }
21660                     
21661                     if(this.singleMode){
21662                         this.setValue(this.formatDate(this.viewDate));
21663                         this.hidePopup();
21664                         return;
21665                     }
21666                     
21667                     this.showMode(-1);
21668                     this.fill();
21669                 }
21670                 break;
21671                 
21672             case 'td':
21673                 //Roo.log(className);
21674                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21675                     var day = parseInt(html, 10) || 1;
21676                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21677                         month = (this.viewDate || new Date()).getUTCMonth();
21678
21679                     if (className.indexOf('old') > -1) {
21680                         if(month === 0 ){
21681                             month = 11;
21682                             year -= 1;
21683                         }else{
21684                             month -= 1;
21685                         }
21686                     } else if (className.indexOf('new') > -1) {
21687                         if (month == 11) {
21688                             month = 0;
21689                             year += 1;
21690                         } else {
21691                             month += 1;
21692                         }
21693                     }
21694                     //Roo.log([year,month,day]);
21695                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21696                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21697 //                    this.fill();
21698                     //Roo.log(this.formatDate(this.date));
21699                     this.setValue(this.formatDate(this.date));
21700                     this.hidePopup();
21701                 }
21702                 break;
21703         }
21704     },
21705     
21706     setStartDate: function(startDate)
21707     {
21708         this.startDate = startDate || -Infinity;
21709         if (this.startDate !== -Infinity) {
21710             this.startDate = this.parseDate(this.startDate);
21711         }
21712         this.update();
21713         this.updateNavArrows();
21714     },
21715
21716     setEndDate: function(endDate)
21717     {
21718         this.endDate = endDate || Infinity;
21719         if (this.endDate !== Infinity) {
21720             this.endDate = this.parseDate(this.endDate);
21721         }
21722         this.update();
21723         this.updateNavArrows();
21724     },
21725     
21726     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21727     {
21728         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21729         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21730             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21731         }
21732         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21733             return parseInt(d, 10);
21734         });
21735         this.update();
21736         this.updateNavArrows();
21737     },
21738     
21739     updateNavArrows: function() 
21740     {
21741         if(this.singleMode){
21742             return;
21743         }
21744         
21745         var d = new Date(this.viewDate),
21746         year = d.getUTCFullYear(),
21747         month = d.getUTCMonth();
21748         
21749         Roo.each(this.picker().select('.prev', true).elements, function(v){
21750             v.show();
21751             switch (this.viewMode) {
21752                 case 0:
21753
21754                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21755                         v.hide();
21756                     }
21757                     break;
21758                 case 1:
21759                 case 2:
21760                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21761                         v.hide();
21762                     }
21763                     break;
21764             }
21765         });
21766         
21767         Roo.each(this.picker().select('.next', true).elements, function(v){
21768             v.show();
21769             switch (this.viewMode) {
21770                 case 0:
21771
21772                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21773                         v.hide();
21774                     }
21775                     break;
21776                 case 1:
21777                 case 2:
21778                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21779                         v.hide();
21780                     }
21781                     break;
21782             }
21783         })
21784     },
21785     
21786     moveMonth: function(date, dir)
21787     {
21788         if (!dir) {
21789             return date;
21790         }
21791         var new_date = new Date(date.valueOf()),
21792         day = new_date.getUTCDate(),
21793         month = new_date.getUTCMonth(),
21794         mag = Math.abs(dir),
21795         new_month, test;
21796         dir = dir > 0 ? 1 : -1;
21797         if (mag == 1){
21798             test = dir == -1
21799             // If going back one month, make sure month is not current month
21800             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21801             ? function(){
21802                 return new_date.getUTCMonth() == month;
21803             }
21804             // If going forward one month, make sure month is as expected
21805             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21806             : function(){
21807                 return new_date.getUTCMonth() != new_month;
21808             };
21809             new_month = month + dir;
21810             new_date.setUTCMonth(new_month);
21811             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21812             if (new_month < 0 || new_month > 11) {
21813                 new_month = (new_month + 12) % 12;
21814             }
21815         } else {
21816             // For magnitudes >1, move one month at a time...
21817             for (var i=0; i<mag; i++) {
21818                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21819                 new_date = this.moveMonth(new_date, dir);
21820             }
21821             // ...then reset the day, keeping it in the new month
21822             new_month = new_date.getUTCMonth();
21823             new_date.setUTCDate(day);
21824             test = function(){
21825                 return new_month != new_date.getUTCMonth();
21826             };
21827         }
21828         // Common date-resetting loop -- if date is beyond end of month, make it
21829         // end of month
21830         while (test()){
21831             new_date.setUTCDate(--day);
21832             new_date.setUTCMonth(new_month);
21833         }
21834         return new_date;
21835     },
21836
21837     moveYear: function(date, dir)
21838     {
21839         return this.moveMonth(date, dir*12);
21840     },
21841
21842     dateWithinRange: function(date)
21843     {
21844         return date >= this.startDate && date <= this.endDate;
21845     },
21846
21847     
21848     remove: function() 
21849     {
21850         this.picker().remove();
21851     },
21852     
21853     validateValue : function(value)
21854     {
21855         if(this.getVisibilityEl().hasClass('hidden')){
21856             return true;
21857         }
21858         
21859         if(value.length < 1)  {
21860             if(this.allowBlank){
21861                 return true;
21862             }
21863             return false;
21864         }
21865         
21866         if(value.length < this.minLength){
21867             return false;
21868         }
21869         if(value.length > this.maxLength){
21870             return false;
21871         }
21872         if(this.vtype){
21873             var vt = Roo.form.VTypes;
21874             if(!vt[this.vtype](value, this)){
21875                 return false;
21876             }
21877         }
21878         if(typeof this.validator == "function"){
21879             var msg = this.validator(value);
21880             if(msg !== true){
21881                 return false;
21882             }
21883         }
21884         
21885         if(this.regex && !this.regex.test(value)){
21886             return false;
21887         }
21888         
21889         if(typeof(this.parseDate(value)) == 'undefined'){
21890             return false;
21891         }
21892         
21893         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21894             return false;
21895         }      
21896         
21897         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21898             return false;
21899         } 
21900         
21901         
21902         return true;
21903     },
21904     
21905     reset : function()
21906     {
21907         this.date = this.viewDate = '';
21908         
21909         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21910     }
21911    
21912 });
21913
21914 Roo.apply(Roo.bootstrap.DateField,  {
21915     
21916     head : {
21917         tag: 'thead',
21918         cn: [
21919         {
21920             tag: 'tr',
21921             cn: [
21922             {
21923                 tag: 'th',
21924                 cls: 'prev',
21925                 html: '<i class="fa fa-arrow-left"/>'
21926             },
21927             {
21928                 tag: 'th',
21929                 cls: 'switch',
21930                 colspan: '5'
21931             },
21932             {
21933                 tag: 'th',
21934                 cls: 'next',
21935                 html: '<i class="fa fa-arrow-right"/>'
21936             }
21937
21938             ]
21939         }
21940         ]
21941     },
21942     
21943     content : {
21944         tag: 'tbody',
21945         cn: [
21946         {
21947             tag: 'tr',
21948             cn: [
21949             {
21950                 tag: 'td',
21951                 colspan: '7'
21952             }
21953             ]
21954         }
21955         ]
21956     },
21957     
21958     footer : {
21959         tag: 'tfoot',
21960         cn: [
21961         {
21962             tag: 'tr',
21963             cn: [
21964             {
21965                 tag: 'th',
21966                 colspan: '7',
21967                 cls: 'today'
21968             }
21969                     
21970             ]
21971         }
21972         ]
21973     },
21974     
21975     dates:{
21976         en: {
21977             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21978             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21979             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21980             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21981             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21982             today: "Today"
21983         }
21984     },
21985     
21986     modes: [
21987     {
21988         clsName: 'days',
21989         navFnc: 'Month',
21990         navStep: 1
21991     },
21992     {
21993         clsName: 'months',
21994         navFnc: 'FullYear',
21995         navStep: 1
21996     },
21997     {
21998         clsName: 'years',
21999         navFnc: 'FullYear',
22000         navStep: 10
22001     }]
22002 });
22003
22004 Roo.apply(Roo.bootstrap.DateField,  {
22005   
22006     template : {
22007         tag: 'div',
22008         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22009         cn: [
22010         {
22011             tag: 'div',
22012             cls: 'datepicker-days',
22013             cn: [
22014             {
22015                 tag: 'table',
22016                 cls: 'table-condensed',
22017                 cn:[
22018                 Roo.bootstrap.DateField.head,
22019                 {
22020                     tag: 'tbody'
22021                 },
22022                 Roo.bootstrap.DateField.footer
22023                 ]
22024             }
22025             ]
22026         },
22027         {
22028             tag: 'div',
22029             cls: 'datepicker-months',
22030             cn: [
22031             {
22032                 tag: 'table',
22033                 cls: 'table-condensed',
22034                 cn:[
22035                 Roo.bootstrap.DateField.head,
22036                 Roo.bootstrap.DateField.content,
22037                 Roo.bootstrap.DateField.footer
22038                 ]
22039             }
22040             ]
22041         },
22042         {
22043             tag: 'div',
22044             cls: 'datepicker-years',
22045             cn: [
22046             {
22047                 tag: 'table',
22048                 cls: 'table-condensed',
22049                 cn:[
22050                 Roo.bootstrap.DateField.head,
22051                 Roo.bootstrap.DateField.content,
22052                 Roo.bootstrap.DateField.footer
22053                 ]
22054             }
22055             ]
22056         }
22057         ]
22058     }
22059 });
22060
22061  
22062
22063  /*
22064  * - LGPL
22065  *
22066  * TimeField
22067  * 
22068  */
22069
22070 /**
22071  * @class Roo.bootstrap.TimeField
22072  * @extends Roo.bootstrap.Input
22073  * Bootstrap DateField class
22074  * 
22075  * 
22076  * @constructor
22077  * Create a new TimeField
22078  * @param {Object} config The config object
22079  */
22080
22081 Roo.bootstrap.TimeField = function(config){
22082     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22083     this.addEvents({
22084             /**
22085              * @event show
22086              * Fires when this field show.
22087              * @param {Roo.bootstrap.DateField} thisthis
22088              * @param {Mixed} date The date value
22089              */
22090             show : true,
22091             /**
22092              * @event show
22093              * Fires when this field hide.
22094              * @param {Roo.bootstrap.DateField} this
22095              * @param {Mixed} date The date value
22096              */
22097             hide : true,
22098             /**
22099              * @event select
22100              * Fires when select a date.
22101              * @param {Roo.bootstrap.DateField} this
22102              * @param {Mixed} date The date value
22103              */
22104             select : true
22105         });
22106 };
22107
22108 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22109     
22110     /**
22111      * @cfg {String} format
22112      * The default time format string which can be overriden for localization support.  The format must be
22113      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22114      */
22115     format : "H:i",
22116
22117     getAutoCreate : function()
22118     {
22119         this.after = '<i class="fa far fa-clock"></i>';
22120         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22121         
22122          
22123     },
22124     onRender: function(ct, position)
22125     {
22126         
22127         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22128                 
22129         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22130         
22131         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22132         
22133         this.pop = this.picker().select('>.datepicker-time',true).first();
22134         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22135         
22136         this.picker().on('mousedown', this.onMousedown, this);
22137         this.picker().on('click', this.onClick, this);
22138         
22139         this.picker().addClass('datepicker-dropdown');
22140     
22141         this.fillTime();
22142         this.update();
22143             
22144         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22145         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22146         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22147         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22148         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22149         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22150
22151     },
22152     
22153     fireKey: function(e){
22154         if (!this.picker().isVisible()){
22155             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22156                 this.show();
22157             }
22158             return;
22159         }
22160
22161         e.preventDefault();
22162         
22163         switch(e.keyCode){
22164             case 27: // escape
22165                 this.hide();
22166                 break;
22167             case 37: // left
22168             case 39: // right
22169                 this.onTogglePeriod();
22170                 break;
22171             case 38: // up
22172                 this.onIncrementMinutes();
22173                 break;
22174             case 40: // down
22175                 this.onDecrementMinutes();
22176                 break;
22177             case 13: // enter
22178             case 9: // tab
22179                 this.setTime();
22180                 break;
22181         }
22182     },
22183     
22184     onClick: function(e) {
22185         e.stopPropagation();
22186         e.preventDefault();
22187     },
22188     
22189     picker : function()
22190     {
22191         return this.pickerEl;
22192     },
22193     
22194     fillTime: function()
22195     {    
22196         var time = this.pop.select('tbody', true).first();
22197         
22198         time.dom.innerHTML = '';
22199         
22200         time.createChild({
22201             tag: 'tr',
22202             cn: [
22203                 {
22204                     tag: 'td',
22205                     cn: [
22206                         {
22207                             tag: 'a',
22208                             href: '#',
22209                             cls: 'btn',
22210                             cn: [
22211                                 {
22212                                     tag: 'i',
22213                                     cls: 'hours-up fa fas fa-chevron-up'
22214                                 }
22215                             ]
22216                         } 
22217                     ]
22218                 },
22219                 {
22220                     tag: 'td',
22221                     cls: 'separator'
22222                 },
22223                 {
22224                     tag: 'td',
22225                     cn: [
22226                         {
22227                             tag: 'a',
22228                             href: '#',
22229                             cls: 'btn',
22230                             cn: [
22231                                 {
22232                                     tag: 'i',
22233                                     cls: 'minutes-up fa fas fa-chevron-up'
22234                                 }
22235                             ]
22236                         }
22237                     ]
22238                 },
22239                 {
22240                     tag: 'td',
22241                     cls: 'separator'
22242                 }
22243             ]
22244         });
22245         
22246         time.createChild({
22247             tag: 'tr',
22248             cn: [
22249                 {
22250                     tag: 'td',
22251                     cn: [
22252                         {
22253                             tag: 'span',
22254                             cls: 'timepicker-hour',
22255                             html: '00'
22256                         }  
22257                     ]
22258                 },
22259                 {
22260                     tag: 'td',
22261                     cls: 'separator',
22262                     html: ':'
22263                 },
22264                 {
22265                     tag: 'td',
22266                     cn: [
22267                         {
22268                             tag: 'span',
22269                             cls: 'timepicker-minute',
22270                             html: '00'
22271                         }  
22272                     ]
22273                 },
22274                 {
22275                     tag: 'td',
22276                     cls: 'separator'
22277                 },
22278                 {
22279                     tag: 'td',
22280                     cn: [
22281                         {
22282                             tag: 'button',
22283                             type: 'button',
22284                             cls: 'btn btn-primary period',
22285                             html: 'AM'
22286                             
22287                         }
22288                     ]
22289                 }
22290             ]
22291         });
22292         
22293         time.createChild({
22294             tag: 'tr',
22295             cn: [
22296                 {
22297                     tag: 'td',
22298                     cn: [
22299                         {
22300                             tag: 'a',
22301                             href: '#',
22302                             cls: 'btn',
22303                             cn: [
22304                                 {
22305                                     tag: 'span',
22306                                     cls: 'hours-down fa fas fa-chevron-down'
22307                                 }
22308                             ]
22309                         }
22310                     ]
22311                 },
22312                 {
22313                     tag: 'td',
22314                     cls: 'separator'
22315                 },
22316                 {
22317                     tag: 'td',
22318                     cn: [
22319                         {
22320                             tag: 'a',
22321                             href: '#',
22322                             cls: 'btn',
22323                             cn: [
22324                                 {
22325                                     tag: 'span',
22326                                     cls: 'minutes-down fa fas fa-chevron-down'
22327                                 }
22328                             ]
22329                         }
22330                     ]
22331                 },
22332                 {
22333                     tag: 'td',
22334                     cls: 'separator'
22335                 }
22336             ]
22337         });
22338         
22339     },
22340     
22341     update: function()
22342     {
22343         
22344         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22345         
22346         this.fill();
22347     },
22348     
22349     fill: function() 
22350     {
22351         var hours = this.time.getHours();
22352         var minutes = this.time.getMinutes();
22353         var period = 'AM';
22354         
22355         if(hours > 11){
22356             period = 'PM';
22357         }
22358         
22359         if(hours == 0){
22360             hours = 12;
22361         }
22362         
22363         
22364         if(hours > 12){
22365             hours = hours - 12;
22366         }
22367         
22368         if(hours < 10){
22369             hours = '0' + hours;
22370         }
22371         
22372         if(minutes < 10){
22373             minutes = '0' + minutes;
22374         }
22375         
22376         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22377         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22378         this.pop.select('button', true).first().dom.innerHTML = period;
22379         
22380     },
22381     
22382     place: function()
22383     {   
22384         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22385         
22386         var cls = ['bottom'];
22387         
22388         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22389             cls.pop();
22390             cls.push('top');
22391         }
22392         
22393         cls.push('right');
22394         
22395         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22396             cls.pop();
22397             cls.push('left');
22398         }
22399         //this.picker().setXY(20000,20000);
22400         this.picker().addClass(cls.join('-'));
22401         
22402         var _this = this;
22403         
22404         Roo.each(cls, function(c){
22405             if(c == 'bottom'){
22406                 (function() {
22407                  //  
22408                 }).defer(200);
22409                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22410                 //_this.picker().setTop(_this.inputEl().getHeight());
22411                 return;
22412             }
22413             if(c == 'top'){
22414                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22415                 
22416                 //_this.picker().setTop(0 - _this.picker().getHeight());
22417                 return;
22418             }
22419             /*
22420             if(c == 'left'){
22421                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22422                 return;
22423             }
22424             if(c == 'right'){
22425                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22426                 return;
22427             }
22428             */
22429         });
22430         
22431     },
22432   
22433     onFocus : function()
22434     {
22435         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22436         this.show();
22437     },
22438     
22439     onBlur : function()
22440     {
22441         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22442         this.hide();
22443     },
22444     
22445     show : function()
22446     {
22447         this.picker().show();
22448         this.pop.show();
22449         this.update();
22450         this.place();
22451         
22452         this.fireEvent('show', this, this.date);
22453     },
22454     
22455     hide : function()
22456     {
22457         this.picker().hide();
22458         this.pop.hide();
22459         
22460         this.fireEvent('hide', this, this.date);
22461     },
22462     
22463     setTime : function()
22464     {
22465         this.hide();
22466         this.setValue(this.time.format(this.format));
22467         
22468         this.fireEvent('select', this, this.date);
22469         
22470         
22471     },
22472     
22473     onMousedown: function(e){
22474         e.stopPropagation();
22475         e.preventDefault();
22476     },
22477     
22478     onIncrementHours: function()
22479     {
22480         Roo.log('onIncrementHours');
22481         this.time = this.time.add(Date.HOUR, 1);
22482         this.update();
22483         
22484     },
22485     
22486     onDecrementHours: function()
22487     {
22488         Roo.log('onDecrementHours');
22489         this.time = this.time.add(Date.HOUR, -1);
22490         this.update();
22491     },
22492     
22493     onIncrementMinutes: function()
22494     {
22495         Roo.log('onIncrementMinutes');
22496         this.time = this.time.add(Date.MINUTE, 1);
22497         this.update();
22498     },
22499     
22500     onDecrementMinutes: function()
22501     {
22502         Roo.log('onDecrementMinutes');
22503         this.time = this.time.add(Date.MINUTE, -1);
22504         this.update();
22505     },
22506     
22507     onTogglePeriod: function()
22508     {
22509         Roo.log('onTogglePeriod');
22510         this.time = this.time.add(Date.HOUR, 12);
22511         this.update();
22512     }
22513     
22514    
22515 });
22516  
22517
22518 Roo.apply(Roo.bootstrap.TimeField,  {
22519   
22520     template : {
22521         tag: 'div',
22522         cls: 'datepicker dropdown-menu',
22523         cn: [
22524             {
22525                 tag: 'div',
22526                 cls: 'datepicker-time',
22527                 cn: [
22528                 {
22529                     tag: 'table',
22530                     cls: 'table-condensed',
22531                     cn:[
22532                         {
22533                             tag: 'tbody',
22534                             cn: [
22535                                 {
22536                                     tag: 'tr',
22537                                     cn: [
22538                                     {
22539                                         tag: 'td',
22540                                         colspan: '7'
22541                                     }
22542                                     ]
22543                                 }
22544                             ]
22545                         },
22546                         {
22547                             tag: 'tfoot',
22548                             cn: [
22549                                 {
22550                                     tag: 'tr',
22551                                     cn: [
22552                                     {
22553                                         tag: 'th',
22554                                         colspan: '7',
22555                                         cls: '',
22556                                         cn: [
22557                                             {
22558                                                 tag: 'button',
22559                                                 cls: 'btn btn-info ok',
22560                                                 html: 'OK'
22561                                             }
22562                                         ]
22563                                     }
22564                     
22565                                     ]
22566                                 }
22567                             ]
22568                         }
22569                     ]
22570                 }
22571                 ]
22572             }
22573         ]
22574     }
22575 });
22576
22577  
22578
22579  /*
22580  * - LGPL
22581  *
22582  * MonthField
22583  * 
22584  */
22585
22586 /**
22587  * @class Roo.bootstrap.MonthField
22588  * @extends Roo.bootstrap.Input
22589  * Bootstrap MonthField class
22590  * 
22591  * @cfg {String} language default en
22592  * 
22593  * @constructor
22594  * Create a new MonthField
22595  * @param {Object} config The config object
22596  */
22597
22598 Roo.bootstrap.MonthField = function(config){
22599     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22600     
22601     this.addEvents({
22602         /**
22603          * @event show
22604          * Fires when this field show.
22605          * @param {Roo.bootstrap.MonthField} this
22606          * @param {Mixed} date The date value
22607          */
22608         show : true,
22609         /**
22610          * @event show
22611          * Fires when this field hide.
22612          * @param {Roo.bootstrap.MonthField} this
22613          * @param {Mixed} date The date value
22614          */
22615         hide : true,
22616         /**
22617          * @event select
22618          * Fires when select a date.
22619          * @param {Roo.bootstrap.MonthField} this
22620          * @param {String} oldvalue The old value
22621          * @param {String} newvalue The new value
22622          */
22623         select : true
22624     });
22625 };
22626
22627 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22628     
22629     onRender: function(ct, position)
22630     {
22631         
22632         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22633         
22634         this.language = this.language || 'en';
22635         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22636         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22637         
22638         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22639         this.isInline = false;
22640         this.isInput = true;
22641         this.component = this.el.select('.add-on', true).first() || false;
22642         this.component = (this.component && this.component.length === 0) ? false : this.component;
22643         this.hasInput = this.component && this.inputEL().length;
22644         
22645         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22646         
22647         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22648         
22649         this.picker().on('mousedown', this.onMousedown, this);
22650         this.picker().on('click', this.onClick, this);
22651         
22652         this.picker().addClass('datepicker-dropdown');
22653         
22654         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22655             v.setStyle('width', '189px');
22656         });
22657         
22658         this.fillMonths();
22659         
22660         this.update();
22661         
22662         if(this.isInline) {
22663             this.show();
22664         }
22665         
22666     },
22667     
22668     setValue: function(v, suppressEvent)
22669     {   
22670         var o = this.getValue();
22671         
22672         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22673         
22674         this.update();
22675
22676         if(suppressEvent !== true){
22677             this.fireEvent('select', this, o, v);
22678         }
22679         
22680     },
22681     
22682     getValue: function()
22683     {
22684         return this.value;
22685     },
22686     
22687     onClick: function(e) 
22688     {
22689         e.stopPropagation();
22690         e.preventDefault();
22691         
22692         var target = e.getTarget();
22693         
22694         if(target.nodeName.toLowerCase() === 'i'){
22695             target = Roo.get(target).dom.parentNode;
22696         }
22697         
22698         var nodeName = target.nodeName;
22699         var className = target.className;
22700         var html = target.innerHTML;
22701         
22702         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22703             return;
22704         }
22705         
22706         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22707         
22708         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22709         
22710         this.hide();
22711                         
22712     },
22713     
22714     picker : function()
22715     {
22716         return this.pickerEl;
22717     },
22718     
22719     fillMonths: function()
22720     {    
22721         var i = 0;
22722         var months = this.picker().select('>.datepicker-months td', true).first();
22723         
22724         months.dom.innerHTML = '';
22725         
22726         while (i < 12) {
22727             var month = {
22728                 tag: 'span',
22729                 cls: 'month',
22730                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22731             };
22732             
22733             months.createChild(month);
22734         }
22735         
22736     },
22737     
22738     update: function()
22739     {
22740         var _this = this;
22741         
22742         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22743             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22744         }
22745         
22746         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22747             e.removeClass('active');
22748             
22749             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22750                 e.addClass('active');
22751             }
22752         })
22753     },
22754     
22755     place: function()
22756     {
22757         if(this.isInline) {
22758             return;
22759         }
22760         
22761         this.picker().removeClass(['bottom', 'top']);
22762         
22763         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22764             /*
22765              * place to the top of element!
22766              *
22767              */
22768             
22769             this.picker().addClass('top');
22770             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22771             
22772             return;
22773         }
22774         
22775         this.picker().addClass('bottom');
22776         
22777         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22778     },
22779     
22780     onFocus : function()
22781     {
22782         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22783         this.show();
22784     },
22785     
22786     onBlur : function()
22787     {
22788         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22789         
22790         var d = this.inputEl().getValue();
22791         
22792         this.setValue(d);
22793                 
22794         this.hide();
22795     },
22796     
22797     show : function()
22798     {
22799         this.picker().show();
22800         this.picker().select('>.datepicker-months', true).first().show();
22801         this.update();
22802         this.place();
22803         
22804         this.fireEvent('show', this, this.date);
22805     },
22806     
22807     hide : function()
22808     {
22809         if(this.isInline) {
22810             return;
22811         }
22812         this.picker().hide();
22813         this.fireEvent('hide', this, this.date);
22814         
22815     },
22816     
22817     onMousedown: function(e)
22818     {
22819         e.stopPropagation();
22820         e.preventDefault();
22821     },
22822     
22823     keyup: function(e)
22824     {
22825         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22826         this.update();
22827     },
22828
22829     fireKey: function(e)
22830     {
22831         if (!this.picker().isVisible()){
22832             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22833                 this.show();
22834             }
22835             return;
22836         }
22837         
22838         var dir;
22839         
22840         switch(e.keyCode){
22841             case 27: // escape
22842                 this.hide();
22843                 e.preventDefault();
22844                 break;
22845             case 37: // left
22846             case 39: // right
22847                 dir = e.keyCode == 37 ? -1 : 1;
22848                 
22849                 this.vIndex = this.vIndex + dir;
22850                 
22851                 if(this.vIndex < 0){
22852                     this.vIndex = 0;
22853                 }
22854                 
22855                 if(this.vIndex > 11){
22856                     this.vIndex = 11;
22857                 }
22858                 
22859                 if(isNaN(this.vIndex)){
22860                     this.vIndex = 0;
22861                 }
22862                 
22863                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22864                 
22865                 break;
22866             case 38: // up
22867             case 40: // down
22868                 
22869                 dir = e.keyCode == 38 ? -1 : 1;
22870                 
22871                 this.vIndex = this.vIndex + dir * 4;
22872                 
22873                 if(this.vIndex < 0){
22874                     this.vIndex = 0;
22875                 }
22876                 
22877                 if(this.vIndex > 11){
22878                     this.vIndex = 11;
22879                 }
22880                 
22881                 if(isNaN(this.vIndex)){
22882                     this.vIndex = 0;
22883                 }
22884                 
22885                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22886                 break;
22887                 
22888             case 13: // enter
22889                 
22890                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22891                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22892                 }
22893                 
22894                 this.hide();
22895                 e.preventDefault();
22896                 break;
22897             case 9: // tab
22898                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22899                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22900                 }
22901                 this.hide();
22902                 break;
22903             case 16: // shift
22904             case 17: // ctrl
22905             case 18: // alt
22906                 break;
22907             default :
22908                 this.hide();
22909                 
22910         }
22911     },
22912     
22913     remove: function() 
22914     {
22915         this.picker().remove();
22916     }
22917    
22918 });
22919
22920 Roo.apply(Roo.bootstrap.MonthField,  {
22921     
22922     content : {
22923         tag: 'tbody',
22924         cn: [
22925         {
22926             tag: 'tr',
22927             cn: [
22928             {
22929                 tag: 'td',
22930                 colspan: '7'
22931             }
22932             ]
22933         }
22934         ]
22935     },
22936     
22937     dates:{
22938         en: {
22939             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22940             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22941         }
22942     }
22943 });
22944
22945 Roo.apply(Roo.bootstrap.MonthField,  {
22946   
22947     template : {
22948         tag: 'div',
22949         cls: 'datepicker dropdown-menu roo-dynamic',
22950         cn: [
22951             {
22952                 tag: 'div',
22953                 cls: 'datepicker-months',
22954                 cn: [
22955                 {
22956                     tag: 'table',
22957                     cls: 'table-condensed',
22958                     cn:[
22959                         Roo.bootstrap.DateField.content
22960                     ]
22961                 }
22962                 ]
22963             }
22964         ]
22965     }
22966 });
22967
22968  
22969
22970  
22971  /*
22972  * - LGPL
22973  *
22974  * CheckBox
22975  * 
22976  */
22977
22978 /**
22979  * @class Roo.bootstrap.CheckBox
22980  * @extends Roo.bootstrap.Input
22981  * Bootstrap CheckBox class
22982  * 
22983  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22984  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22985  * @cfg {String} boxLabel The text that appears beside the checkbox
22986  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22987  * @cfg {Boolean} checked initnal the element
22988  * @cfg {Boolean} inline inline the element (default false)
22989  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22990  * @cfg {String} tooltip label tooltip
22991  * 
22992  * @constructor
22993  * Create a new CheckBox
22994  * @param {Object} config The config object
22995  */
22996
22997 Roo.bootstrap.CheckBox = function(config){
22998     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22999    
23000     this.addEvents({
23001         /**
23002         * @event check
23003         * Fires when the element is checked or unchecked.
23004         * @param {Roo.bootstrap.CheckBox} this This input
23005         * @param {Boolean} checked The new checked value
23006         */
23007        check : true,
23008        /**
23009         * @event click
23010         * Fires when the element is click.
23011         * @param {Roo.bootstrap.CheckBox} this This input
23012         */
23013        click : true
23014     });
23015     
23016 };
23017
23018 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23019   
23020     inputType: 'checkbox',
23021     inputValue: 1,
23022     valueOff: 0,
23023     boxLabel: false,
23024     checked: false,
23025     weight : false,
23026     inline: false,
23027     tooltip : '',
23028     
23029     // checkbox success does not make any sense really.. 
23030     invalidClass : "",
23031     validClass : "",
23032     
23033     
23034     getAutoCreate : function()
23035     {
23036         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23037         
23038         var id = Roo.id();
23039         
23040         var cfg = {};
23041         
23042         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23043         
23044         if(this.inline){
23045             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23046         }
23047         
23048         var input =  {
23049             tag: 'input',
23050             id : id,
23051             type : this.inputType,
23052             value : this.inputValue,
23053             cls : 'roo-' + this.inputType, //'form-box',
23054             placeholder : this.placeholder || ''
23055             
23056         };
23057         
23058         if(this.inputType != 'radio'){
23059             var hidden =  {
23060                 tag: 'input',
23061                 type : 'hidden',
23062                 cls : 'roo-hidden-value',
23063                 value : this.checked ? this.inputValue : this.valueOff
23064             };
23065         }
23066         
23067             
23068         if (this.weight) { // Validity check?
23069             cfg.cls += " " + this.inputType + "-" + this.weight;
23070         }
23071         
23072         if (this.disabled) {
23073             input.disabled=true;
23074         }
23075         
23076         if(this.checked){
23077             input.checked = this.checked;
23078         }
23079         
23080         if (this.name) {
23081             
23082             input.name = this.name;
23083             
23084             if(this.inputType != 'radio'){
23085                 hidden.name = this.name;
23086                 input.name = '_hidden_' + this.name;
23087             }
23088         }
23089         
23090         if (this.size) {
23091             input.cls += ' input-' + this.size;
23092         }
23093         
23094         var settings=this;
23095         
23096         ['xs','sm','md','lg'].map(function(size){
23097             if (settings[size]) {
23098                 cfg.cls += ' col-' + size + '-' + settings[size];
23099             }
23100         });
23101         
23102         var inputblock = input;
23103          
23104         if (this.before || this.after) {
23105             
23106             inputblock = {
23107                 cls : 'input-group',
23108                 cn :  [] 
23109             };
23110             
23111             if (this.before) {
23112                 inputblock.cn.push({
23113                     tag :'span',
23114                     cls : 'input-group-addon',
23115                     html : this.before
23116                 });
23117             }
23118             
23119             inputblock.cn.push(input);
23120             
23121             if(this.inputType != 'radio'){
23122                 inputblock.cn.push(hidden);
23123             }
23124             
23125             if (this.after) {
23126                 inputblock.cn.push({
23127                     tag :'span',
23128                     cls : 'input-group-addon',
23129                     html : this.after
23130                 });
23131             }
23132             
23133         }
23134         var boxLabelCfg = false;
23135         
23136         if(this.boxLabel){
23137            
23138             boxLabelCfg = {
23139                 tag: 'label',
23140                 //'for': id, // box label is handled by onclick - so no for...
23141                 cls: 'box-label',
23142                 html: this.boxLabel
23143             };
23144             if(this.tooltip){
23145                 boxLabelCfg.tooltip = this.tooltip;
23146             }
23147              
23148         }
23149         
23150         
23151         if (align ==='left' && this.fieldLabel.length) {
23152 //                Roo.log("left and has label");
23153             cfg.cn = [
23154                 {
23155                     tag: 'label',
23156                     'for' :  id,
23157                     cls : 'control-label',
23158                     html : this.fieldLabel
23159                 },
23160                 {
23161                     cls : "", 
23162                     cn: [
23163                         inputblock
23164                     ]
23165                 }
23166             ];
23167             
23168             if (boxLabelCfg) {
23169                 cfg.cn[1].cn.push(boxLabelCfg);
23170             }
23171             
23172             if(this.labelWidth > 12){
23173                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23174             }
23175             
23176             if(this.labelWidth < 13 && this.labelmd == 0){
23177                 this.labelmd = this.labelWidth;
23178             }
23179             
23180             if(this.labellg > 0){
23181                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23182                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23183             }
23184             
23185             if(this.labelmd > 0){
23186                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23187                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23188             }
23189             
23190             if(this.labelsm > 0){
23191                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23192                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23193             }
23194             
23195             if(this.labelxs > 0){
23196                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23197                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23198             }
23199             
23200         } else if ( this.fieldLabel.length) {
23201 //                Roo.log(" label");
23202                 cfg.cn = [
23203                    
23204                     {
23205                         tag: this.boxLabel ? 'span' : 'label',
23206                         'for': id,
23207                         cls: 'control-label box-input-label',
23208                         //cls : 'input-group-addon',
23209                         html : this.fieldLabel
23210                     },
23211                     
23212                     inputblock
23213                     
23214                 ];
23215                 if (boxLabelCfg) {
23216                     cfg.cn.push(boxLabelCfg);
23217                 }
23218
23219         } else {
23220             
23221 //                Roo.log(" no label && no align");
23222                 cfg.cn = [  inputblock ] ;
23223                 if (boxLabelCfg) {
23224                     cfg.cn.push(boxLabelCfg);
23225                 }
23226
23227                 
23228         }
23229         
23230        
23231         
23232         if(this.inputType != 'radio'){
23233             cfg.cn.push(hidden);
23234         }
23235         
23236         return cfg;
23237         
23238     },
23239     
23240     /**
23241      * return the real input element.
23242      */
23243     inputEl: function ()
23244     {
23245         return this.el.select('input.roo-' + this.inputType,true).first();
23246     },
23247     hiddenEl: function ()
23248     {
23249         return this.el.select('input.roo-hidden-value',true).first();
23250     },
23251     
23252     labelEl: function()
23253     {
23254         return this.el.select('label.control-label',true).first();
23255     },
23256     /* depricated... */
23257     
23258     label: function()
23259     {
23260         return this.labelEl();
23261     },
23262     
23263     boxLabelEl: function()
23264     {
23265         return this.el.select('label.box-label',true).first();
23266     },
23267     
23268     initEvents : function()
23269     {
23270 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23271         
23272         this.inputEl().on('click', this.onClick,  this);
23273         
23274         if (this.boxLabel) { 
23275             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23276         }
23277         
23278         this.startValue = this.getValue();
23279         
23280         if(this.groupId){
23281             Roo.bootstrap.CheckBox.register(this);
23282         }
23283     },
23284     
23285     onClick : function(e)
23286     {   
23287         if(this.fireEvent('click', this, e) !== false){
23288             this.setChecked(!this.checked);
23289         }
23290         
23291     },
23292     
23293     setChecked : function(state,suppressEvent)
23294     {
23295         this.startValue = this.getValue();
23296
23297         if(this.inputType == 'radio'){
23298             
23299             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23300                 e.dom.checked = false;
23301             });
23302             
23303             this.inputEl().dom.checked = true;
23304             
23305             this.inputEl().dom.value = this.inputValue;
23306             
23307             if(suppressEvent !== true){
23308                 this.fireEvent('check', this, true);
23309             }
23310             
23311             this.validate();
23312             
23313             return;
23314         }
23315         
23316         this.checked = state;
23317         
23318         this.inputEl().dom.checked = state;
23319         
23320         
23321         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23322         
23323         if(suppressEvent !== true){
23324             this.fireEvent('check', this, state);
23325         }
23326         
23327         this.validate();
23328     },
23329     
23330     getValue : function()
23331     {
23332         if(this.inputType == 'radio'){
23333             return this.getGroupValue();
23334         }
23335         
23336         return this.hiddenEl().dom.value;
23337         
23338     },
23339     
23340     getGroupValue : function()
23341     {
23342         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23343             return '';
23344         }
23345         
23346         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23347     },
23348     
23349     setValue : function(v,suppressEvent)
23350     {
23351         if(this.inputType == 'radio'){
23352             this.setGroupValue(v, suppressEvent);
23353             return;
23354         }
23355         
23356         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23357         
23358         this.validate();
23359     },
23360     
23361     setGroupValue : function(v, suppressEvent)
23362     {
23363         this.startValue = this.getValue();
23364         
23365         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23366             e.dom.checked = false;
23367             
23368             if(e.dom.value == v){
23369                 e.dom.checked = true;
23370             }
23371         });
23372         
23373         if(suppressEvent !== true){
23374             this.fireEvent('check', this, true);
23375         }
23376
23377         this.validate();
23378         
23379         return;
23380     },
23381     
23382     validate : function()
23383     {
23384         if(this.getVisibilityEl().hasClass('hidden')){
23385             return true;
23386         }
23387         
23388         if(
23389                 this.disabled || 
23390                 (this.inputType == 'radio' && this.validateRadio()) ||
23391                 (this.inputType == 'checkbox' && this.validateCheckbox())
23392         ){
23393             this.markValid();
23394             return true;
23395         }
23396         
23397         this.markInvalid();
23398         return false;
23399     },
23400     
23401     validateRadio : function()
23402     {
23403         if(this.getVisibilityEl().hasClass('hidden')){
23404             return true;
23405         }
23406         
23407         if(this.allowBlank){
23408             return true;
23409         }
23410         
23411         var valid = false;
23412         
23413         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23414             if(!e.dom.checked){
23415                 return;
23416             }
23417             
23418             valid = true;
23419             
23420             return false;
23421         });
23422         
23423         return valid;
23424     },
23425     
23426     validateCheckbox : function()
23427     {
23428         if(!this.groupId){
23429             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23430             //return (this.getValue() == this.inputValue) ? true : false;
23431         }
23432         
23433         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23434         
23435         if(!group){
23436             return false;
23437         }
23438         
23439         var r = false;
23440         
23441         for(var i in group){
23442             if(group[i].el.isVisible(true)){
23443                 r = false;
23444                 break;
23445             }
23446             
23447             r = true;
23448         }
23449         
23450         for(var i in group){
23451             if(r){
23452                 break;
23453             }
23454             
23455             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23456         }
23457         
23458         return r;
23459     },
23460     
23461     /**
23462      * Mark this field as valid
23463      */
23464     markValid : function()
23465     {
23466         var _this = this;
23467         
23468         this.fireEvent('valid', this);
23469         
23470         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23471         
23472         if(this.groupId){
23473             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23474         }
23475         
23476         if(label){
23477             label.markValid();
23478         }
23479
23480         if(this.inputType == 'radio'){
23481             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23482                 var fg = e.findParent('.form-group', false, true);
23483                 if (Roo.bootstrap.version == 3) {
23484                     fg.removeClass([_this.invalidClass, _this.validClass]);
23485                     fg.addClass(_this.validClass);
23486                 } else {
23487                     fg.removeClass(['is-valid', 'is-invalid']);
23488                     fg.addClass('is-valid');
23489                 }
23490             });
23491             
23492             return;
23493         }
23494
23495         if(!this.groupId){
23496             var fg = this.el.findParent('.form-group', false, true);
23497             if (Roo.bootstrap.version == 3) {
23498                 fg.removeClass([this.invalidClass, this.validClass]);
23499                 fg.addClass(this.validClass);
23500             } else {
23501                 fg.removeClass(['is-valid', 'is-invalid']);
23502                 fg.addClass('is-valid');
23503             }
23504             return;
23505         }
23506         
23507         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23508         
23509         if(!group){
23510             return;
23511         }
23512         
23513         for(var i in group){
23514             var fg = group[i].el.findParent('.form-group', false, true);
23515             if (Roo.bootstrap.version == 3) {
23516                 fg.removeClass([this.invalidClass, this.validClass]);
23517                 fg.addClass(this.validClass);
23518             } else {
23519                 fg.removeClass(['is-valid', 'is-invalid']);
23520                 fg.addClass('is-valid');
23521             }
23522         }
23523     },
23524     
23525      /**
23526      * Mark this field as invalid
23527      * @param {String} msg The validation message
23528      */
23529     markInvalid : function(msg)
23530     {
23531         if(this.allowBlank){
23532             return;
23533         }
23534         
23535         var _this = this;
23536         
23537         this.fireEvent('invalid', this, msg);
23538         
23539         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23540         
23541         if(this.groupId){
23542             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23543         }
23544         
23545         if(label){
23546             label.markInvalid();
23547         }
23548             
23549         if(this.inputType == 'radio'){
23550             
23551             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23552                 var fg = e.findParent('.form-group', false, true);
23553                 if (Roo.bootstrap.version == 3) {
23554                     fg.removeClass([_this.invalidClass, _this.validClass]);
23555                     fg.addClass(_this.invalidClass);
23556                 } else {
23557                     fg.removeClass(['is-invalid', 'is-valid']);
23558                     fg.addClass('is-invalid');
23559                 }
23560             });
23561             
23562             return;
23563         }
23564         
23565         if(!this.groupId){
23566             var fg = this.el.findParent('.form-group', false, true);
23567             if (Roo.bootstrap.version == 3) {
23568                 fg.removeClass([_this.invalidClass, _this.validClass]);
23569                 fg.addClass(_this.invalidClass);
23570             } else {
23571                 fg.removeClass(['is-invalid', 'is-valid']);
23572                 fg.addClass('is-invalid');
23573             }
23574             return;
23575         }
23576         
23577         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23578         
23579         if(!group){
23580             return;
23581         }
23582         
23583         for(var i in group){
23584             var fg = group[i].el.findParent('.form-group', false, true);
23585             if (Roo.bootstrap.version == 3) {
23586                 fg.removeClass([_this.invalidClass, _this.validClass]);
23587                 fg.addClass(_this.invalidClass);
23588             } else {
23589                 fg.removeClass(['is-invalid', 'is-valid']);
23590                 fg.addClass('is-invalid');
23591             }
23592         }
23593         
23594     },
23595     
23596     clearInvalid : function()
23597     {
23598         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23599         
23600         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23601         
23602         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23603         
23604         if (label && label.iconEl) {
23605             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23606             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23607         }
23608     },
23609     
23610     disable : function()
23611     {
23612         if(this.inputType != 'radio'){
23613             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23614             return;
23615         }
23616         
23617         var _this = this;
23618         
23619         if(this.rendered){
23620             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23621                 _this.getActionEl().addClass(this.disabledClass);
23622                 e.dom.disabled = true;
23623             });
23624         }
23625         
23626         this.disabled = true;
23627         this.fireEvent("disable", this);
23628         return this;
23629     },
23630
23631     enable : function()
23632     {
23633         if(this.inputType != 'radio'){
23634             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23635             return;
23636         }
23637         
23638         var _this = this;
23639         
23640         if(this.rendered){
23641             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23642                 _this.getActionEl().removeClass(this.disabledClass);
23643                 e.dom.disabled = false;
23644             });
23645         }
23646         
23647         this.disabled = false;
23648         this.fireEvent("enable", this);
23649         return this;
23650     },
23651     
23652     setBoxLabel : function(v)
23653     {
23654         this.boxLabel = v;
23655         
23656         if(this.rendered){
23657             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23658         }
23659     }
23660
23661 });
23662
23663 Roo.apply(Roo.bootstrap.CheckBox, {
23664     
23665     groups: {},
23666     
23667      /**
23668     * register a CheckBox Group
23669     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23670     */
23671     register : function(checkbox)
23672     {
23673         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23674             this.groups[checkbox.groupId] = {};
23675         }
23676         
23677         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23678             return;
23679         }
23680         
23681         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23682         
23683     },
23684     /**
23685     * fetch a CheckBox Group based on the group ID
23686     * @param {string} the group ID
23687     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23688     */
23689     get: function(groupId) {
23690         if (typeof(this.groups[groupId]) == 'undefined') {
23691             return false;
23692         }
23693         
23694         return this.groups[groupId] ;
23695     }
23696     
23697     
23698 });
23699 /*
23700  * - LGPL
23701  *
23702  * RadioItem
23703  * 
23704  */
23705
23706 /**
23707  * @class Roo.bootstrap.Radio
23708  * @extends Roo.bootstrap.Component
23709  * Bootstrap Radio class
23710  * @cfg {String} boxLabel - the label associated
23711  * @cfg {String} value - the value of radio
23712  * 
23713  * @constructor
23714  * Create a new Radio
23715  * @param {Object} config The config object
23716  */
23717 Roo.bootstrap.Radio = function(config){
23718     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23719     
23720 };
23721
23722 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23723     
23724     boxLabel : '',
23725     
23726     value : '',
23727     
23728     getAutoCreate : function()
23729     {
23730         var cfg = {
23731             tag : 'div',
23732             cls : 'form-group radio',
23733             cn : [
23734                 {
23735                     tag : 'label',
23736                     cls : 'box-label',
23737                     html : this.boxLabel
23738                 }
23739             ]
23740         };
23741         
23742         return cfg;
23743     },
23744     
23745     initEvents : function() 
23746     {
23747         this.parent().register(this);
23748         
23749         this.el.on('click', this.onClick, this);
23750         
23751     },
23752     
23753     onClick : function(e)
23754     {
23755         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23756             this.setChecked(true);
23757         }
23758     },
23759     
23760     setChecked : function(state, suppressEvent)
23761     {
23762         this.parent().setValue(this.value, suppressEvent);
23763         
23764     },
23765     
23766     setBoxLabel : function(v)
23767     {
23768         this.boxLabel = v;
23769         
23770         if(this.rendered){
23771             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23772         }
23773     }
23774     
23775 });
23776  
23777
23778  /*
23779  * - LGPL
23780  *
23781  * Input
23782  * 
23783  */
23784
23785 /**
23786  * @class Roo.bootstrap.SecurePass
23787  * @extends Roo.bootstrap.Input
23788  * Bootstrap SecurePass class
23789  *
23790  * 
23791  * @constructor
23792  * Create a new SecurePass
23793  * @param {Object} config The config object
23794  */
23795  
23796 Roo.bootstrap.SecurePass = function (config) {
23797     // these go here, so the translation tool can replace them..
23798     this.errors = {
23799         PwdEmpty: "Please type a password, and then retype it to confirm.",
23800         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23801         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23802         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23803         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23804         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23805         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23806         TooWeak: "Your password is Too Weak."
23807     },
23808     this.meterLabel = "Password strength:";
23809     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23810     this.meterClass = [
23811         "roo-password-meter-tooweak", 
23812         "roo-password-meter-weak", 
23813         "roo-password-meter-medium", 
23814         "roo-password-meter-strong", 
23815         "roo-password-meter-grey"
23816     ];
23817     
23818     this.errors = {};
23819     
23820     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23821 }
23822
23823 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23824     /**
23825      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23826      * {
23827      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23828      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23829      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23830      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23831      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23832      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23833      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23834      * })
23835      */
23836     // private
23837     
23838     meterWidth: 300,
23839     errorMsg :'',    
23840     errors: false,
23841     imageRoot: '/',
23842     /**
23843      * @cfg {String/Object} Label for the strength meter (defaults to
23844      * 'Password strength:')
23845      */
23846     // private
23847     meterLabel: '',
23848     /**
23849      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23850      * ['Weak', 'Medium', 'Strong'])
23851      */
23852     // private    
23853     pwdStrengths: false,    
23854     // private
23855     strength: 0,
23856     // private
23857     _lastPwd: null,
23858     // private
23859     kCapitalLetter: 0,
23860     kSmallLetter: 1,
23861     kDigit: 2,
23862     kPunctuation: 3,
23863     
23864     insecure: false,
23865     // private
23866     initEvents: function ()
23867     {
23868         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23869
23870         if (this.el.is('input[type=password]') && Roo.isSafari) {
23871             this.el.on('keydown', this.SafariOnKeyDown, this);
23872         }
23873
23874         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23875     },
23876     // private
23877     onRender: function (ct, position)
23878     {
23879         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23880         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23881         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23882
23883         this.trigger.createChild({
23884                    cn: [
23885                     {
23886                     //id: 'PwdMeter',
23887                     tag: 'div',
23888                     cls: 'roo-password-meter-grey col-xs-12',
23889                     style: {
23890                         //width: 0,
23891                         //width: this.meterWidth + 'px'                                                
23892                         }
23893                     },
23894                     {                            
23895                          cls: 'roo-password-meter-text'                          
23896                     }
23897                 ]            
23898         });
23899
23900          
23901         if (this.hideTrigger) {
23902             this.trigger.setDisplayed(false);
23903         }
23904         this.setSize(this.width || '', this.height || '');
23905     },
23906     // private
23907     onDestroy: function ()
23908     {
23909         if (this.trigger) {
23910             this.trigger.removeAllListeners();
23911             this.trigger.remove();
23912         }
23913         if (this.wrap) {
23914             this.wrap.remove();
23915         }
23916         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23917     },
23918     // private
23919     checkStrength: function ()
23920     {
23921         var pwd = this.inputEl().getValue();
23922         if (pwd == this._lastPwd) {
23923             return;
23924         }
23925
23926         var strength;
23927         if (this.ClientSideStrongPassword(pwd)) {
23928             strength = 3;
23929         } else if (this.ClientSideMediumPassword(pwd)) {
23930             strength = 2;
23931         } else if (this.ClientSideWeakPassword(pwd)) {
23932             strength = 1;
23933         } else {
23934             strength = 0;
23935         }
23936         
23937         Roo.log('strength1: ' + strength);
23938         
23939         //var pm = this.trigger.child('div/div/div').dom;
23940         var pm = this.trigger.child('div/div');
23941         pm.removeClass(this.meterClass);
23942         pm.addClass(this.meterClass[strength]);
23943                 
23944         
23945         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23946                 
23947         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23948         
23949         this._lastPwd = pwd;
23950     },
23951     reset: function ()
23952     {
23953         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23954         
23955         this._lastPwd = '';
23956         
23957         var pm = this.trigger.child('div/div');
23958         pm.removeClass(this.meterClass);
23959         pm.addClass('roo-password-meter-grey');        
23960         
23961         
23962         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23963         
23964         pt.innerHTML = '';
23965         this.inputEl().dom.type='password';
23966     },
23967     // private
23968     validateValue: function (value)
23969     {
23970         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23971             return false;
23972         }
23973         if (value.length == 0) {
23974             if (this.allowBlank) {
23975                 this.clearInvalid();
23976                 return true;
23977             }
23978
23979             this.markInvalid(this.errors.PwdEmpty);
23980             this.errorMsg = this.errors.PwdEmpty;
23981             return false;
23982         }
23983         
23984         if(this.insecure){
23985             return true;
23986         }
23987         
23988         if (!value.match(/[\x21-\x7e]+/)) {
23989             this.markInvalid(this.errors.PwdBadChar);
23990             this.errorMsg = this.errors.PwdBadChar;
23991             return false;
23992         }
23993         if (value.length < 6) {
23994             this.markInvalid(this.errors.PwdShort);
23995             this.errorMsg = this.errors.PwdShort;
23996             return false;
23997         }
23998         if (value.length > 16) {
23999             this.markInvalid(this.errors.PwdLong);
24000             this.errorMsg = this.errors.PwdLong;
24001             return false;
24002         }
24003         var strength;
24004         if (this.ClientSideStrongPassword(value)) {
24005             strength = 3;
24006         } else if (this.ClientSideMediumPassword(value)) {
24007             strength = 2;
24008         } else if (this.ClientSideWeakPassword(value)) {
24009             strength = 1;
24010         } else {
24011             strength = 0;
24012         }
24013
24014         
24015         if (strength < 2) {
24016             //this.markInvalid(this.errors.TooWeak);
24017             this.errorMsg = this.errors.TooWeak;
24018             //return false;
24019         }
24020         
24021         
24022         console.log('strength2: ' + strength);
24023         
24024         //var pm = this.trigger.child('div/div/div').dom;
24025         
24026         var pm = this.trigger.child('div/div');
24027         pm.removeClass(this.meterClass);
24028         pm.addClass(this.meterClass[strength]);
24029                 
24030         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24031                 
24032         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24033         
24034         this.errorMsg = ''; 
24035         return true;
24036     },
24037     // private
24038     CharacterSetChecks: function (type)
24039     {
24040         this.type = type;
24041         this.fResult = false;
24042     },
24043     // private
24044     isctype: function (character, type)
24045     {
24046         switch (type) {  
24047             case this.kCapitalLetter:
24048                 if (character >= 'A' && character <= 'Z') {
24049                     return true;
24050                 }
24051                 break;
24052             
24053             case this.kSmallLetter:
24054                 if (character >= 'a' && character <= 'z') {
24055                     return true;
24056                 }
24057                 break;
24058             
24059             case this.kDigit:
24060                 if (character >= '0' && character <= '9') {
24061                     return true;
24062                 }
24063                 break;
24064             
24065             case this.kPunctuation:
24066                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24067                     return true;
24068                 }
24069                 break;
24070             
24071             default:
24072                 return false;
24073         }
24074
24075     },
24076     // private
24077     IsLongEnough: function (pwd, size)
24078     {
24079         return !(pwd == null || isNaN(size) || pwd.length < size);
24080     },
24081     // private
24082     SpansEnoughCharacterSets: function (word, nb)
24083     {
24084         if (!this.IsLongEnough(word, nb))
24085         {
24086             return false;
24087         }
24088
24089         var characterSetChecks = new Array(
24090             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24091             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24092         );
24093         
24094         for (var index = 0; index < word.length; ++index) {
24095             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24096                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24097                     characterSetChecks[nCharSet].fResult = true;
24098                     break;
24099                 }
24100             }
24101         }
24102
24103         var nCharSets = 0;
24104         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24105             if (characterSetChecks[nCharSet].fResult) {
24106                 ++nCharSets;
24107             }
24108         }
24109
24110         if (nCharSets < nb) {
24111             return false;
24112         }
24113         return true;
24114     },
24115     // private
24116     ClientSideStrongPassword: function (pwd)
24117     {
24118         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24119     },
24120     // private
24121     ClientSideMediumPassword: function (pwd)
24122     {
24123         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24124     },
24125     // private
24126     ClientSideWeakPassword: function (pwd)
24127     {
24128         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24129     }
24130           
24131 })//<script type="text/javascript">
24132
24133 /*
24134  * Based  Ext JS Library 1.1.1
24135  * Copyright(c) 2006-2007, Ext JS, LLC.
24136  * LGPL
24137  *
24138  */
24139  
24140 /**
24141  * @class Roo.HtmlEditorCore
24142  * @extends Roo.Component
24143  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24144  *
24145  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24146  */
24147
24148 Roo.HtmlEditorCore = function(config){
24149     
24150     
24151     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24152     
24153     
24154     this.addEvents({
24155         /**
24156          * @event initialize
24157          * Fires when the editor is fully initialized (including the iframe)
24158          * @param {Roo.HtmlEditorCore} this
24159          */
24160         initialize: true,
24161         /**
24162          * @event activate
24163          * Fires when the editor is first receives the focus. Any insertion must wait
24164          * until after this event.
24165          * @param {Roo.HtmlEditorCore} this
24166          */
24167         activate: true,
24168          /**
24169          * @event beforesync
24170          * Fires before the textarea is updated with content from the editor iframe. Return false
24171          * to cancel the sync.
24172          * @param {Roo.HtmlEditorCore} this
24173          * @param {String} html
24174          */
24175         beforesync: true,
24176          /**
24177          * @event beforepush
24178          * Fires before the iframe editor is updated with content from the textarea. Return false
24179          * to cancel the push.
24180          * @param {Roo.HtmlEditorCore} this
24181          * @param {String} html
24182          */
24183         beforepush: true,
24184          /**
24185          * @event sync
24186          * Fires when the textarea is updated with content from the editor iframe.
24187          * @param {Roo.HtmlEditorCore} this
24188          * @param {String} html
24189          */
24190         sync: true,
24191          /**
24192          * @event push
24193          * Fires when the iframe editor is updated with content from the textarea.
24194          * @param {Roo.HtmlEditorCore} this
24195          * @param {String} html
24196          */
24197         push: true,
24198         
24199         /**
24200          * @event editorevent
24201          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24202          * @param {Roo.HtmlEditorCore} this
24203          */
24204         editorevent: true
24205         
24206     });
24207     
24208     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24209     
24210     // defaults : white / black...
24211     this.applyBlacklists();
24212     
24213     
24214     
24215 };
24216
24217
24218 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24219
24220
24221      /**
24222      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24223      */
24224     
24225     owner : false,
24226     
24227      /**
24228      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24229      *                        Roo.resizable.
24230      */
24231     resizable : false,
24232      /**
24233      * @cfg {Number} height (in pixels)
24234      */   
24235     height: 300,
24236    /**
24237      * @cfg {Number} width (in pixels)
24238      */   
24239     width: 500,
24240     
24241     /**
24242      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24243      * 
24244      */
24245     stylesheets: false,
24246     
24247     // id of frame..
24248     frameId: false,
24249     
24250     // private properties
24251     validationEvent : false,
24252     deferHeight: true,
24253     initialized : false,
24254     activated : false,
24255     sourceEditMode : false,
24256     onFocus : Roo.emptyFn,
24257     iframePad:3,
24258     hideMode:'offsets',
24259     
24260     clearUp: true,
24261     
24262     // blacklist + whitelisted elements..
24263     black: false,
24264     white: false,
24265      
24266     bodyCls : '',
24267
24268     /**
24269      * Protected method that will not generally be called directly. It
24270      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24271      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24272      */
24273     getDocMarkup : function(){
24274         // body styles..
24275         var st = '';
24276         
24277         // inherit styels from page...?? 
24278         if (this.stylesheets === false) {
24279             
24280             Roo.get(document.head).select('style').each(function(node) {
24281                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24282             });
24283             
24284             Roo.get(document.head).select('link').each(function(node) { 
24285                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24286             });
24287             
24288         } else if (!this.stylesheets.length) {
24289                 // simple..
24290                 st = '<style type="text/css">' +
24291                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24292                    '</style>';
24293         } else {
24294             for (var i in this.stylesheets) { 
24295                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24296             }
24297             
24298         }
24299         
24300         st +=  '<style type="text/css">' +
24301             'IMG { cursor: pointer } ' +
24302         '</style>';
24303
24304         var cls = 'roo-htmleditor-body';
24305         
24306         if(this.bodyCls.length){
24307             cls += ' ' + this.bodyCls;
24308         }
24309         
24310         return '<html><head>' + st  +
24311             //<style type="text/css">' +
24312             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24313             //'</style>' +
24314             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24315     },
24316
24317     // private
24318     onRender : function(ct, position)
24319     {
24320         var _t = this;
24321         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24322         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24323         
24324         
24325         this.el.dom.style.border = '0 none';
24326         this.el.dom.setAttribute('tabIndex', -1);
24327         this.el.addClass('x-hidden hide');
24328         
24329         
24330         
24331         if(Roo.isIE){ // fix IE 1px bogus margin
24332             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24333         }
24334        
24335         
24336         this.frameId = Roo.id();
24337         
24338          
24339         
24340         var iframe = this.owner.wrap.createChild({
24341             tag: 'iframe',
24342             cls: 'form-control', // bootstrap..
24343             id: this.frameId,
24344             name: this.frameId,
24345             frameBorder : 'no',
24346             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24347         }, this.el
24348         );
24349         
24350         
24351         this.iframe = iframe.dom;
24352
24353          this.assignDocWin();
24354         
24355         this.doc.designMode = 'on';
24356        
24357         this.doc.open();
24358         this.doc.write(this.getDocMarkup());
24359         this.doc.close();
24360
24361         
24362         var task = { // must defer to wait for browser to be ready
24363             run : function(){
24364                 //console.log("run task?" + this.doc.readyState);
24365                 this.assignDocWin();
24366                 if(this.doc.body || this.doc.readyState == 'complete'){
24367                     try {
24368                         this.doc.designMode="on";
24369                     } catch (e) {
24370                         return;
24371                     }
24372                     Roo.TaskMgr.stop(task);
24373                     this.initEditor.defer(10, this);
24374                 }
24375             },
24376             interval : 10,
24377             duration: 10000,
24378             scope: this
24379         };
24380         Roo.TaskMgr.start(task);
24381
24382     },
24383
24384     // private
24385     onResize : function(w, h)
24386     {
24387          Roo.log('resize: ' +w + ',' + h );
24388         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24389         if(!this.iframe){
24390             return;
24391         }
24392         if(typeof w == 'number'){
24393             
24394             this.iframe.style.width = w + 'px';
24395         }
24396         if(typeof h == 'number'){
24397             
24398             this.iframe.style.height = h + 'px';
24399             if(this.doc){
24400                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24401             }
24402         }
24403         
24404     },
24405
24406     /**
24407      * Toggles the editor between standard and source edit mode.
24408      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24409      */
24410     toggleSourceEdit : function(sourceEditMode){
24411         
24412         this.sourceEditMode = sourceEditMode === true;
24413         
24414         if(this.sourceEditMode){
24415  
24416             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24417             
24418         }else{
24419             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24420             //this.iframe.className = '';
24421             this.deferFocus();
24422         }
24423         //this.setSize(this.owner.wrap.getSize());
24424         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24425     },
24426
24427     
24428   
24429
24430     /**
24431      * Protected method that will not generally be called directly. If you need/want
24432      * custom HTML cleanup, this is the method you should override.
24433      * @param {String} html The HTML to be cleaned
24434      * return {String} The cleaned HTML
24435      */
24436     cleanHtml : function(html){
24437         html = String(html);
24438         if(html.length > 5){
24439             if(Roo.isSafari){ // strip safari nonsense
24440                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24441             }
24442         }
24443         if(html == '&nbsp;'){
24444             html = '';
24445         }
24446         return html;
24447     },
24448
24449     /**
24450      * HTML Editor -> Textarea
24451      * Protected method that will not generally be called directly. Syncs the contents
24452      * of the editor iframe with the textarea.
24453      */
24454     syncValue : function(){
24455         if(this.initialized){
24456             var bd = (this.doc.body || this.doc.documentElement);
24457             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24458             var html = bd.innerHTML;
24459             if(Roo.isSafari){
24460                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24461                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24462                 if(m && m[1]){
24463                     html = '<div style="'+m[0]+'">' + html + '</div>';
24464                 }
24465             }
24466             html = this.cleanHtml(html);
24467             // fix up the special chars.. normaly like back quotes in word...
24468             // however we do not want to do this with chinese..
24469             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24470                 
24471                 var cc = match.charCodeAt();
24472
24473                 // Get the character value, handling surrogate pairs
24474                 if (match.length == 2) {
24475                     // It's a surrogate pair, calculate the Unicode code point
24476                     var high = match.charCodeAt(0) - 0xD800;
24477                     var low  = match.charCodeAt(1) - 0xDC00;
24478                     cc = (high * 0x400) + low + 0x10000;
24479                 }  else if (
24480                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24481                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24482                     (cc >= 0xf900 && cc < 0xfb00 )
24483                 ) {
24484                         return match;
24485                 }  
24486          
24487                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24488                 return "&#" + cc + ";";
24489                 
24490                 
24491             });
24492             
24493             
24494              
24495             if(this.owner.fireEvent('beforesync', this, html) !== false){
24496                 this.el.dom.value = html;
24497                 this.owner.fireEvent('sync', this, html);
24498             }
24499         }
24500     },
24501
24502     /**
24503      * Protected method that will not generally be called directly. Pushes the value of the textarea
24504      * into the iframe editor.
24505      */
24506     pushValue : function(){
24507         if(this.initialized){
24508             var v = this.el.dom.value.trim();
24509             
24510 //            if(v.length < 1){
24511 //                v = '&#160;';
24512 //            }
24513             
24514             if(this.owner.fireEvent('beforepush', this, v) !== false){
24515                 var d = (this.doc.body || this.doc.documentElement);
24516                 d.innerHTML = v;
24517                 this.cleanUpPaste();
24518                 this.el.dom.value = d.innerHTML;
24519                 this.owner.fireEvent('push', this, v);
24520             }
24521         }
24522     },
24523
24524     // private
24525     deferFocus : function(){
24526         this.focus.defer(10, this);
24527     },
24528
24529     // doc'ed in Field
24530     focus : function(){
24531         if(this.win && !this.sourceEditMode){
24532             this.win.focus();
24533         }else{
24534             this.el.focus();
24535         }
24536     },
24537     
24538     assignDocWin: function()
24539     {
24540         var iframe = this.iframe;
24541         
24542          if(Roo.isIE){
24543             this.doc = iframe.contentWindow.document;
24544             this.win = iframe.contentWindow;
24545         } else {
24546 //            if (!Roo.get(this.frameId)) {
24547 //                return;
24548 //            }
24549 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24550 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24551             
24552             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24553                 return;
24554             }
24555             
24556             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24557             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24558         }
24559     },
24560     
24561     // private
24562     initEditor : function(){
24563         //console.log("INIT EDITOR");
24564         this.assignDocWin();
24565         
24566         
24567         
24568         this.doc.designMode="on";
24569         this.doc.open();
24570         this.doc.write(this.getDocMarkup());
24571         this.doc.close();
24572         
24573         var dbody = (this.doc.body || this.doc.documentElement);
24574         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24575         // this copies styles from the containing element into thsi one..
24576         // not sure why we need all of this..
24577         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24578         
24579         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24580         //ss['background-attachment'] = 'fixed'; // w3c
24581         dbody.bgProperties = 'fixed'; // ie
24582         //Roo.DomHelper.applyStyles(dbody, ss);
24583         Roo.EventManager.on(this.doc, {
24584             //'mousedown': this.onEditorEvent,
24585             'mouseup': this.onEditorEvent,
24586             'dblclick': this.onEditorEvent,
24587             'click': this.onEditorEvent,
24588             'keyup': this.onEditorEvent,
24589             buffer:100,
24590             scope: this
24591         });
24592         if(Roo.isGecko){
24593             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24594         }
24595         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24596             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24597         }
24598         this.initialized = true;
24599
24600         this.owner.fireEvent('initialize', this);
24601         this.pushValue();
24602     },
24603
24604     // private
24605     onDestroy : function(){
24606         
24607         
24608         
24609         if(this.rendered){
24610             
24611             //for (var i =0; i < this.toolbars.length;i++) {
24612             //    // fixme - ask toolbars for heights?
24613             //    this.toolbars[i].onDestroy();
24614            // }
24615             
24616             //this.wrap.dom.innerHTML = '';
24617             //this.wrap.remove();
24618         }
24619     },
24620
24621     // private
24622     onFirstFocus : function(){
24623         
24624         this.assignDocWin();
24625         
24626         
24627         this.activated = true;
24628          
24629     
24630         if(Roo.isGecko){ // prevent silly gecko errors
24631             this.win.focus();
24632             var s = this.win.getSelection();
24633             if(!s.focusNode || s.focusNode.nodeType != 3){
24634                 var r = s.getRangeAt(0);
24635                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24636                 r.collapse(true);
24637                 this.deferFocus();
24638             }
24639             try{
24640                 this.execCmd('useCSS', true);
24641                 this.execCmd('styleWithCSS', false);
24642             }catch(e){}
24643         }
24644         this.owner.fireEvent('activate', this);
24645     },
24646
24647     // private
24648     adjustFont: function(btn){
24649         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24650         //if(Roo.isSafari){ // safari
24651         //    adjust *= 2;
24652        // }
24653         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24654         if(Roo.isSafari){ // safari
24655             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24656             v =  (v < 10) ? 10 : v;
24657             v =  (v > 48) ? 48 : v;
24658             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24659             
24660         }
24661         
24662         
24663         v = Math.max(1, v+adjust);
24664         
24665         this.execCmd('FontSize', v  );
24666     },
24667
24668     onEditorEvent : function(e)
24669     {
24670         this.owner.fireEvent('editorevent', this, e);
24671       //  this.updateToolbar();
24672         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24673     },
24674
24675     insertTag : function(tg)
24676     {
24677         // could be a bit smarter... -> wrap the current selected tRoo..
24678         if (tg.toLowerCase() == 'span' ||
24679             tg.toLowerCase() == 'code' ||
24680             tg.toLowerCase() == 'sup' ||
24681             tg.toLowerCase() == 'sub' 
24682             ) {
24683             
24684             range = this.createRange(this.getSelection());
24685             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24686             wrappingNode.appendChild(range.extractContents());
24687             range.insertNode(wrappingNode);
24688
24689             return;
24690             
24691             
24692             
24693         }
24694         this.execCmd("formatblock",   tg);
24695         
24696     },
24697     
24698     insertText : function(txt)
24699     {
24700         
24701         
24702         var range = this.createRange();
24703         range.deleteContents();
24704                //alert(Sender.getAttribute('label'));
24705                
24706         range.insertNode(this.doc.createTextNode(txt));
24707     } ,
24708     
24709      
24710
24711     /**
24712      * Executes a Midas editor command on the editor document and performs necessary focus and
24713      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24714      * @param {String} cmd The Midas command
24715      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24716      */
24717     relayCmd : function(cmd, value){
24718         this.win.focus();
24719         this.execCmd(cmd, value);
24720         this.owner.fireEvent('editorevent', this);
24721         //this.updateToolbar();
24722         this.owner.deferFocus();
24723     },
24724
24725     /**
24726      * Executes a Midas editor command directly on the editor document.
24727      * For visual commands, you should use {@link #relayCmd} instead.
24728      * <b>This should only be called after the editor is initialized.</b>
24729      * @param {String} cmd The Midas command
24730      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24731      */
24732     execCmd : function(cmd, value){
24733         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24734         this.syncValue();
24735     },
24736  
24737  
24738    
24739     /**
24740      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24741      * to insert tRoo.
24742      * @param {String} text | dom node.. 
24743      */
24744     insertAtCursor : function(text)
24745     {
24746         
24747         if(!this.activated){
24748             return;
24749         }
24750         /*
24751         if(Roo.isIE){
24752             this.win.focus();
24753             var r = this.doc.selection.createRange();
24754             if(r){
24755                 r.collapse(true);
24756                 r.pasteHTML(text);
24757                 this.syncValue();
24758                 this.deferFocus();
24759             
24760             }
24761             return;
24762         }
24763         */
24764         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24765             this.win.focus();
24766             
24767             
24768             // from jquery ui (MIT licenced)
24769             var range, node;
24770             var win = this.win;
24771             
24772             if (win.getSelection && win.getSelection().getRangeAt) {
24773                 range = win.getSelection().getRangeAt(0);
24774                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24775                 range.insertNode(node);
24776             } else if (win.document.selection && win.document.selection.createRange) {
24777                 // no firefox support
24778                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24779                 win.document.selection.createRange().pasteHTML(txt);
24780             } else {
24781                 // no firefox support
24782                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24783                 this.execCmd('InsertHTML', txt);
24784             } 
24785             
24786             this.syncValue();
24787             
24788             this.deferFocus();
24789         }
24790     },
24791  // private
24792     mozKeyPress : function(e){
24793         if(e.ctrlKey){
24794             var c = e.getCharCode(), cmd;
24795           
24796             if(c > 0){
24797                 c = String.fromCharCode(c).toLowerCase();
24798                 switch(c){
24799                     case 'b':
24800                         cmd = 'bold';
24801                         break;
24802                     case 'i':
24803                         cmd = 'italic';
24804                         break;
24805                     
24806                     case 'u':
24807                         cmd = 'underline';
24808                         break;
24809                     
24810                     case 'v':
24811                         this.cleanUpPaste.defer(100, this);
24812                         return;
24813                         
24814                 }
24815                 if(cmd){
24816                     this.win.focus();
24817                     this.execCmd(cmd);
24818                     this.deferFocus();
24819                     e.preventDefault();
24820                 }
24821                 
24822             }
24823         }
24824     },
24825
24826     // private
24827     fixKeys : function(){ // load time branching for fastest keydown performance
24828         if(Roo.isIE){
24829             return function(e){
24830                 var k = e.getKey(), r;
24831                 if(k == e.TAB){
24832                     e.stopEvent();
24833                     r = this.doc.selection.createRange();
24834                     if(r){
24835                         r.collapse(true);
24836                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24837                         this.deferFocus();
24838                     }
24839                     return;
24840                 }
24841                 
24842                 if(k == e.ENTER){
24843                     r = this.doc.selection.createRange();
24844                     if(r){
24845                         var target = r.parentElement();
24846                         if(!target || target.tagName.toLowerCase() != 'li'){
24847                             e.stopEvent();
24848                             r.pasteHTML('<br />');
24849                             r.collapse(false);
24850                             r.select();
24851                         }
24852                     }
24853                 }
24854                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24855                     this.cleanUpPaste.defer(100, this);
24856                     return;
24857                 }
24858                 
24859                 
24860             };
24861         }else if(Roo.isOpera){
24862             return function(e){
24863                 var k = e.getKey();
24864                 if(k == e.TAB){
24865                     e.stopEvent();
24866                     this.win.focus();
24867                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24868                     this.deferFocus();
24869                 }
24870                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24871                     this.cleanUpPaste.defer(100, this);
24872                     return;
24873                 }
24874                 
24875             };
24876         }else if(Roo.isSafari){
24877             return function(e){
24878                 var k = e.getKey();
24879                 
24880                 if(k == e.TAB){
24881                     e.stopEvent();
24882                     this.execCmd('InsertText','\t');
24883                     this.deferFocus();
24884                     return;
24885                 }
24886                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24887                     this.cleanUpPaste.defer(100, this);
24888                     return;
24889                 }
24890                 
24891              };
24892         }
24893     }(),
24894     
24895     getAllAncestors: function()
24896     {
24897         var p = this.getSelectedNode();
24898         var a = [];
24899         if (!p) {
24900             a.push(p); // push blank onto stack..
24901             p = this.getParentElement();
24902         }
24903         
24904         
24905         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24906             a.push(p);
24907             p = p.parentNode;
24908         }
24909         a.push(this.doc.body);
24910         return a;
24911     },
24912     lastSel : false,
24913     lastSelNode : false,
24914     
24915     
24916     getSelection : function() 
24917     {
24918         this.assignDocWin();
24919         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24920     },
24921     
24922     getSelectedNode: function() 
24923     {
24924         // this may only work on Gecko!!!
24925         
24926         // should we cache this!!!!
24927         
24928         
24929         
24930          
24931         var range = this.createRange(this.getSelection()).cloneRange();
24932         
24933         if (Roo.isIE) {
24934             var parent = range.parentElement();
24935             while (true) {
24936                 var testRange = range.duplicate();
24937                 testRange.moveToElementText(parent);
24938                 if (testRange.inRange(range)) {
24939                     break;
24940                 }
24941                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24942                     break;
24943                 }
24944                 parent = parent.parentElement;
24945             }
24946             return parent;
24947         }
24948         
24949         // is ancestor a text element.
24950         var ac =  range.commonAncestorContainer;
24951         if (ac.nodeType == 3) {
24952             ac = ac.parentNode;
24953         }
24954         
24955         var ar = ac.childNodes;
24956          
24957         var nodes = [];
24958         var other_nodes = [];
24959         var has_other_nodes = false;
24960         for (var i=0;i<ar.length;i++) {
24961             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24962                 continue;
24963             }
24964             // fullly contained node.
24965             
24966             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24967                 nodes.push(ar[i]);
24968                 continue;
24969             }
24970             
24971             // probably selected..
24972             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24973                 other_nodes.push(ar[i]);
24974                 continue;
24975             }
24976             // outer..
24977             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24978                 continue;
24979             }
24980             
24981             
24982             has_other_nodes = true;
24983         }
24984         if (!nodes.length && other_nodes.length) {
24985             nodes= other_nodes;
24986         }
24987         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24988             return false;
24989         }
24990         
24991         return nodes[0];
24992     },
24993     createRange: function(sel)
24994     {
24995         // this has strange effects when using with 
24996         // top toolbar - not sure if it's a great idea.
24997         //this.editor.contentWindow.focus();
24998         if (typeof sel != "undefined") {
24999             try {
25000                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25001             } catch(e) {
25002                 return this.doc.createRange();
25003             }
25004         } else {
25005             return this.doc.createRange();
25006         }
25007     },
25008     getParentElement: function()
25009     {
25010         
25011         this.assignDocWin();
25012         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25013         
25014         var range = this.createRange(sel);
25015          
25016         try {
25017             var p = range.commonAncestorContainer;
25018             while (p.nodeType == 3) { // text node
25019                 p = p.parentNode;
25020             }
25021             return p;
25022         } catch (e) {
25023             return null;
25024         }
25025     
25026     },
25027     /***
25028      *
25029      * Range intersection.. the hard stuff...
25030      *  '-1' = before
25031      *  '0' = hits..
25032      *  '1' = after.
25033      *         [ -- selected range --- ]
25034      *   [fail]                        [fail]
25035      *
25036      *    basically..
25037      *      if end is before start or  hits it. fail.
25038      *      if start is after end or hits it fail.
25039      *
25040      *   if either hits (but other is outside. - then it's not 
25041      *   
25042      *    
25043      **/
25044     
25045     
25046     // @see http://www.thismuchiknow.co.uk/?p=64.
25047     rangeIntersectsNode : function(range, node)
25048     {
25049         var nodeRange = node.ownerDocument.createRange();
25050         try {
25051             nodeRange.selectNode(node);
25052         } catch (e) {
25053             nodeRange.selectNodeContents(node);
25054         }
25055     
25056         var rangeStartRange = range.cloneRange();
25057         rangeStartRange.collapse(true);
25058     
25059         var rangeEndRange = range.cloneRange();
25060         rangeEndRange.collapse(false);
25061     
25062         var nodeStartRange = nodeRange.cloneRange();
25063         nodeStartRange.collapse(true);
25064     
25065         var nodeEndRange = nodeRange.cloneRange();
25066         nodeEndRange.collapse(false);
25067     
25068         return rangeStartRange.compareBoundaryPoints(
25069                  Range.START_TO_START, nodeEndRange) == -1 &&
25070                rangeEndRange.compareBoundaryPoints(
25071                  Range.START_TO_START, nodeStartRange) == 1;
25072         
25073          
25074     },
25075     rangeCompareNode : function(range, node)
25076     {
25077         var nodeRange = node.ownerDocument.createRange();
25078         try {
25079             nodeRange.selectNode(node);
25080         } catch (e) {
25081             nodeRange.selectNodeContents(node);
25082         }
25083         
25084         
25085         range.collapse(true);
25086     
25087         nodeRange.collapse(true);
25088      
25089         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25090         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25091          
25092         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25093         
25094         var nodeIsBefore   =  ss == 1;
25095         var nodeIsAfter    = ee == -1;
25096         
25097         if (nodeIsBefore && nodeIsAfter) {
25098             return 0; // outer
25099         }
25100         if (!nodeIsBefore && nodeIsAfter) {
25101             return 1; //right trailed.
25102         }
25103         
25104         if (nodeIsBefore && !nodeIsAfter) {
25105             return 2;  // left trailed.
25106         }
25107         // fully contined.
25108         return 3;
25109     },
25110
25111     // private? - in a new class?
25112     cleanUpPaste :  function()
25113     {
25114         // cleans up the whole document..
25115         Roo.log('cleanuppaste');
25116         
25117         this.cleanUpChildren(this.doc.body);
25118         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25119         if (clean != this.doc.body.innerHTML) {
25120             this.doc.body.innerHTML = clean;
25121         }
25122         
25123     },
25124     
25125     cleanWordChars : function(input) {// change the chars to hex code
25126         var he = Roo.HtmlEditorCore;
25127         
25128         var output = input;
25129         Roo.each(he.swapCodes, function(sw) { 
25130             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25131             
25132             output = output.replace(swapper, sw[1]);
25133         });
25134         
25135         return output;
25136     },
25137     
25138     
25139     cleanUpChildren : function (n)
25140     {
25141         if (!n.childNodes.length) {
25142             return;
25143         }
25144         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25145            this.cleanUpChild(n.childNodes[i]);
25146         }
25147     },
25148     
25149     
25150         
25151     
25152     cleanUpChild : function (node)
25153     {
25154         var ed = this;
25155         //console.log(node);
25156         if (node.nodeName == "#text") {
25157             // clean up silly Windows -- stuff?
25158             return; 
25159         }
25160         if (node.nodeName == "#comment") {
25161             node.parentNode.removeChild(node);
25162             // clean up silly Windows -- stuff?
25163             return; 
25164         }
25165         var lcname = node.tagName.toLowerCase();
25166         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25167         // whitelist of tags..
25168         
25169         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25170             // remove node.
25171             node.parentNode.removeChild(node);
25172             return;
25173             
25174         }
25175         
25176         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25177         
25178         // spans with no attributes - just remove them..
25179         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25180             remove_keep_children = true;
25181         }
25182         
25183         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25184         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25185         
25186         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25187         //    remove_keep_children = true;
25188         //}
25189         
25190         if (remove_keep_children) {
25191             this.cleanUpChildren(node);
25192             // inserts everything just before this node...
25193             while (node.childNodes.length) {
25194                 var cn = node.childNodes[0];
25195                 node.removeChild(cn);
25196                 node.parentNode.insertBefore(cn, node);
25197             }
25198             node.parentNode.removeChild(node);
25199             return;
25200         }
25201         
25202         if (!node.attributes || !node.attributes.length) {
25203             
25204           
25205             
25206             
25207             this.cleanUpChildren(node);
25208             return;
25209         }
25210         
25211         function cleanAttr(n,v)
25212         {
25213             
25214             if (v.match(/^\./) || v.match(/^\//)) {
25215                 return;
25216             }
25217             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25218                 return;
25219             }
25220             if (v.match(/^#/)) {
25221                 return;
25222             }
25223             if (v.match(/^\{/)) { // allow template editing.
25224                 return;
25225             }
25226 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25227             node.removeAttribute(n);
25228             
25229         }
25230         
25231         var cwhite = this.cwhite;
25232         var cblack = this.cblack;
25233             
25234         function cleanStyle(n,v)
25235         {
25236             if (v.match(/expression/)) { //XSS?? should we even bother..
25237                 node.removeAttribute(n);
25238                 return;
25239             }
25240             
25241             var parts = v.split(/;/);
25242             var clean = [];
25243             
25244             Roo.each(parts, function(p) {
25245                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25246                 if (!p.length) {
25247                     return true;
25248                 }
25249                 var l = p.split(':').shift().replace(/\s+/g,'');
25250                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25251                 
25252                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25253 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25254                     //node.removeAttribute(n);
25255                     return true;
25256                 }
25257                 //Roo.log()
25258                 // only allow 'c whitelisted system attributes'
25259                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25260 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25261                     //node.removeAttribute(n);
25262                     return true;
25263                 }
25264                 
25265                 
25266                  
25267                 
25268                 clean.push(p);
25269                 return true;
25270             });
25271             if (clean.length) { 
25272                 node.setAttribute(n, clean.join(';'));
25273             } else {
25274                 node.removeAttribute(n);
25275             }
25276             
25277         }
25278         
25279         
25280         for (var i = node.attributes.length-1; i > -1 ; i--) {
25281             var a = node.attributes[i];
25282             //console.log(a);
25283             
25284             if (a.name.toLowerCase().substr(0,2)=='on')  {
25285                 node.removeAttribute(a.name);
25286                 continue;
25287             }
25288             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25289                 node.removeAttribute(a.name);
25290                 continue;
25291             }
25292             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25293                 cleanAttr(a.name,a.value); // fixme..
25294                 continue;
25295             }
25296             if (a.name == 'style') {
25297                 cleanStyle(a.name,a.value);
25298                 continue;
25299             }
25300             /// clean up MS crap..
25301             // tecnically this should be a list of valid class'es..
25302             
25303             
25304             if (a.name == 'class') {
25305                 if (a.value.match(/^Mso/)) {
25306                     node.removeAttribute('class');
25307                 }
25308                 
25309                 if (a.value.match(/^body$/)) {
25310                     node.removeAttribute('class');
25311                 }
25312                 continue;
25313             }
25314             
25315             // style cleanup!?
25316             // class cleanup?
25317             
25318         }
25319         
25320         
25321         this.cleanUpChildren(node);
25322         
25323         
25324     },
25325     
25326     /**
25327      * Clean up MS wordisms...
25328      */
25329     cleanWord : function(node)
25330     {
25331         if (!node) {
25332             this.cleanWord(this.doc.body);
25333             return;
25334         }
25335         
25336         if(
25337                 node.nodeName == 'SPAN' &&
25338                 !node.hasAttributes() &&
25339                 node.childNodes.length == 1 &&
25340                 node.firstChild.nodeName == "#text"  
25341         ) {
25342             var textNode = node.firstChild;
25343             node.removeChild(textNode);
25344             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25345                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25346             }
25347             node.parentNode.insertBefore(textNode, node);
25348             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25349                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25350             }
25351             node.parentNode.removeChild(node);
25352         }
25353         
25354         if (node.nodeName == "#text") {
25355             // clean up silly Windows -- stuff?
25356             return; 
25357         }
25358         if (node.nodeName == "#comment") {
25359             node.parentNode.removeChild(node);
25360             // clean up silly Windows -- stuff?
25361             return; 
25362         }
25363         
25364         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25365             node.parentNode.removeChild(node);
25366             return;
25367         }
25368         //Roo.log(node.tagName);
25369         // remove - but keep children..
25370         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25371             //Roo.log('-- removed');
25372             while (node.childNodes.length) {
25373                 var cn = node.childNodes[0];
25374                 node.removeChild(cn);
25375                 node.parentNode.insertBefore(cn, node);
25376                 // move node to parent - and clean it..
25377                 this.cleanWord(cn);
25378             }
25379             node.parentNode.removeChild(node);
25380             /// no need to iterate chidlren = it's got none..
25381             //this.iterateChildren(node, this.cleanWord);
25382             return;
25383         }
25384         // clean styles
25385         if (node.className.length) {
25386             
25387             var cn = node.className.split(/\W+/);
25388             var cna = [];
25389             Roo.each(cn, function(cls) {
25390                 if (cls.match(/Mso[a-zA-Z]+/)) {
25391                     return;
25392                 }
25393                 cna.push(cls);
25394             });
25395             node.className = cna.length ? cna.join(' ') : '';
25396             if (!cna.length) {
25397                 node.removeAttribute("class");
25398             }
25399         }
25400         
25401         if (node.hasAttribute("lang")) {
25402             node.removeAttribute("lang");
25403         }
25404         
25405         if (node.hasAttribute("style")) {
25406             
25407             var styles = node.getAttribute("style").split(";");
25408             var nstyle = [];
25409             Roo.each(styles, function(s) {
25410                 if (!s.match(/:/)) {
25411                     return;
25412                 }
25413                 var kv = s.split(":");
25414                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25415                     return;
25416                 }
25417                 // what ever is left... we allow.
25418                 nstyle.push(s);
25419             });
25420             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25421             if (!nstyle.length) {
25422                 node.removeAttribute('style');
25423             }
25424         }
25425         this.iterateChildren(node, this.cleanWord);
25426         
25427         
25428         
25429     },
25430     /**
25431      * iterateChildren of a Node, calling fn each time, using this as the scole..
25432      * @param {DomNode} node node to iterate children of.
25433      * @param {Function} fn method of this class to call on each item.
25434      */
25435     iterateChildren : function(node, fn)
25436     {
25437         if (!node.childNodes.length) {
25438                 return;
25439         }
25440         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25441            fn.call(this, node.childNodes[i])
25442         }
25443     },
25444     
25445     
25446     /**
25447      * cleanTableWidths.
25448      *
25449      * Quite often pasting from word etc.. results in tables with column and widths.
25450      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25451      *
25452      */
25453     cleanTableWidths : function(node)
25454     {
25455          
25456          
25457         if (!node) {
25458             this.cleanTableWidths(this.doc.body);
25459             return;
25460         }
25461         
25462         // ignore list...
25463         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25464             return; 
25465         }
25466         Roo.log(node.tagName);
25467         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25468             this.iterateChildren(node, this.cleanTableWidths);
25469             return;
25470         }
25471         if (node.hasAttribute('width')) {
25472             node.removeAttribute('width');
25473         }
25474         
25475          
25476         if (node.hasAttribute("style")) {
25477             // pretty basic...
25478             
25479             var styles = node.getAttribute("style").split(";");
25480             var nstyle = [];
25481             Roo.each(styles, function(s) {
25482                 if (!s.match(/:/)) {
25483                     return;
25484                 }
25485                 var kv = s.split(":");
25486                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25487                     return;
25488                 }
25489                 // what ever is left... we allow.
25490                 nstyle.push(s);
25491             });
25492             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25493             if (!nstyle.length) {
25494                 node.removeAttribute('style');
25495             }
25496         }
25497         
25498         this.iterateChildren(node, this.cleanTableWidths);
25499         
25500         
25501     },
25502     
25503     
25504     
25505     
25506     domToHTML : function(currentElement, depth, nopadtext) {
25507         
25508         depth = depth || 0;
25509         nopadtext = nopadtext || false;
25510     
25511         if (!currentElement) {
25512             return this.domToHTML(this.doc.body);
25513         }
25514         
25515         //Roo.log(currentElement);
25516         var j;
25517         var allText = false;
25518         var nodeName = currentElement.nodeName;
25519         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25520         
25521         if  (nodeName == '#text') {
25522             
25523             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25524         }
25525         
25526         
25527         var ret = '';
25528         if (nodeName != 'BODY') {
25529              
25530             var i = 0;
25531             // Prints the node tagName, such as <A>, <IMG>, etc
25532             if (tagName) {
25533                 var attr = [];
25534                 for(i = 0; i < currentElement.attributes.length;i++) {
25535                     // quoting?
25536                     var aname = currentElement.attributes.item(i).name;
25537                     if (!currentElement.attributes.item(i).value.length) {
25538                         continue;
25539                     }
25540                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25541                 }
25542                 
25543                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25544             } 
25545             else {
25546                 
25547                 // eack
25548             }
25549         } else {
25550             tagName = false;
25551         }
25552         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25553             return ret;
25554         }
25555         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25556             nopadtext = true;
25557         }
25558         
25559         
25560         // Traverse the tree
25561         i = 0;
25562         var currentElementChild = currentElement.childNodes.item(i);
25563         var allText = true;
25564         var innerHTML  = '';
25565         lastnode = '';
25566         while (currentElementChild) {
25567             // Formatting code (indent the tree so it looks nice on the screen)
25568             var nopad = nopadtext;
25569             if (lastnode == 'SPAN') {
25570                 nopad  = true;
25571             }
25572             // text
25573             if  (currentElementChild.nodeName == '#text') {
25574                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25575                 toadd = nopadtext ? toadd : toadd.trim();
25576                 if (!nopad && toadd.length > 80) {
25577                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25578                 }
25579                 innerHTML  += toadd;
25580                 
25581                 i++;
25582                 currentElementChild = currentElement.childNodes.item(i);
25583                 lastNode = '';
25584                 continue;
25585             }
25586             allText = false;
25587             
25588             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25589                 
25590             // Recursively traverse the tree structure of the child node
25591             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25592             lastnode = currentElementChild.nodeName;
25593             i++;
25594             currentElementChild=currentElement.childNodes.item(i);
25595         }
25596         
25597         ret += innerHTML;
25598         
25599         if (!allText) {
25600                 // The remaining code is mostly for formatting the tree
25601             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25602         }
25603         
25604         
25605         if (tagName) {
25606             ret+= "</"+tagName+">";
25607         }
25608         return ret;
25609         
25610     },
25611         
25612     applyBlacklists : function()
25613     {
25614         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25615         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25616         
25617         this.white = [];
25618         this.black = [];
25619         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25620             if (b.indexOf(tag) > -1) {
25621                 return;
25622             }
25623             this.white.push(tag);
25624             
25625         }, this);
25626         
25627         Roo.each(w, function(tag) {
25628             if (b.indexOf(tag) > -1) {
25629                 return;
25630             }
25631             if (this.white.indexOf(tag) > -1) {
25632                 return;
25633             }
25634             this.white.push(tag);
25635             
25636         }, this);
25637         
25638         
25639         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25640             if (w.indexOf(tag) > -1) {
25641                 return;
25642             }
25643             this.black.push(tag);
25644             
25645         }, this);
25646         
25647         Roo.each(b, function(tag) {
25648             if (w.indexOf(tag) > -1) {
25649                 return;
25650             }
25651             if (this.black.indexOf(tag) > -1) {
25652                 return;
25653             }
25654             this.black.push(tag);
25655             
25656         }, this);
25657         
25658         
25659         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25660         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25661         
25662         this.cwhite = [];
25663         this.cblack = [];
25664         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25665             if (b.indexOf(tag) > -1) {
25666                 return;
25667             }
25668             this.cwhite.push(tag);
25669             
25670         }, this);
25671         
25672         Roo.each(w, function(tag) {
25673             if (b.indexOf(tag) > -1) {
25674                 return;
25675             }
25676             if (this.cwhite.indexOf(tag) > -1) {
25677                 return;
25678             }
25679             this.cwhite.push(tag);
25680             
25681         }, this);
25682         
25683         
25684         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25685             if (w.indexOf(tag) > -1) {
25686                 return;
25687             }
25688             this.cblack.push(tag);
25689             
25690         }, this);
25691         
25692         Roo.each(b, function(tag) {
25693             if (w.indexOf(tag) > -1) {
25694                 return;
25695             }
25696             if (this.cblack.indexOf(tag) > -1) {
25697                 return;
25698             }
25699             this.cblack.push(tag);
25700             
25701         }, this);
25702     },
25703     
25704     setStylesheets : function(stylesheets)
25705     {
25706         if(typeof(stylesheets) == 'string'){
25707             Roo.get(this.iframe.contentDocument.head).createChild({
25708                 tag : 'link',
25709                 rel : 'stylesheet',
25710                 type : 'text/css',
25711                 href : stylesheets
25712             });
25713             
25714             return;
25715         }
25716         var _this = this;
25717      
25718         Roo.each(stylesheets, function(s) {
25719             if(!s.length){
25720                 return;
25721             }
25722             
25723             Roo.get(_this.iframe.contentDocument.head).createChild({
25724                 tag : 'link',
25725                 rel : 'stylesheet',
25726                 type : 'text/css',
25727                 href : s
25728             });
25729         });
25730
25731         
25732     },
25733     
25734     removeStylesheets : function()
25735     {
25736         var _this = this;
25737         
25738         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25739             s.remove();
25740         });
25741     },
25742     
25743     setStyle : function(style)
25744     {
25745         Roo.get(this.iframe.contentDocument.head).createChild({
25746             tag : 'style',
25747             type : 'text/css',
25748             html : style
25749         });
25750
25751         return;
25752     }
25753     
25754     // hide stuff that is not compatible
25755     /**
25756      * @event blur
25757      * @hide
25758      */
25759     /**
25760      * @event change
25761      * @hide
25762      */
25763     /**
25764      * @event focus
25765      * @hide
25766      */
25767     /**
25768      * @event specialkey
25769      * @hide
25770      */
25771     /**
25772      * @cfg {String} fieldClass @hide
25773      */
25774     /**
25775      * @cfg {String} focusClass @hide
25776      */
25777     /**
25778      * @cfg {String} autoCreate @hide
25779      */
25780     /**
25781      * @cfg {String} inputType @hide
25782      */
25783     /**
25784      * @cfg {String} invalidClass @hide
25785      */
25786     /**
25787      * @cfg {String} invalidText @hide
25788      */
25789     /**
25790      * @cfg {String} msgFx @hide
25791      */
25792     /**
25793      * @cfg {String} validateOnBlur @hide
25794      */
25795 });
25796
25797 Roo.HtmlEditorCore.white = [
25798         'area', 'br', 'img', 'input', 'hr', 'wbr',
25799         
25800        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25801        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25802        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25803        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25804        'table',   'ul',         'xmp', 
25805        
25806        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25807       'thead',   'tr', 
25808      
25809       'dir', 'menu', 'ol', 'ul', 'dl',
25810        
25811       'embed',  'object'
25812 ];
25813
25814
25815 Roo.HtmlEditorCore.black = [
25816     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25817         'applet', // 
25818         'base',   'basefont', 'bgsound', 'blink',  'body', 
25819         'frame',  'frameset', 'head',    'html',   'ilayer', 
25820         'iframe', 'layer',  'link',     'meta',    'object',   
25821         'script', 'style' ,'title',  'xml' // clean later..
25822 ];
25823 Roo.HtmlEditorCore.clean = [
25824     'script', 'style', 'title', 'xml'
25825 ];
25826 Roo.HtmlEditorCore.remove = [
25827     'font'
25828 ];
25829 // attributes..
25830
25831 Roo.HtmlEditorCore.ablack = [
25832     'on'
25833 ];
25834     
25835 Roo.HtmlEditorCore.aclean = [ 
25836     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25837 ];
25838
25839 // protocols..
25840 Roo.HtmlEditorCore.pwhite= [
25841         'http',  'https',  'mailto'
25842 ];
25843
25844 // white listed style attributes.
25845 Roo.HtmlEditorCore.cwhite= [
25846       //  'text-align', /// default is to allow most things..
25847       
25848          
25849 //        'font-size'//??
25850 ];
25851
25852 // black listed style attributes.
25853 Roo.HtmlEditorCore.cblack= [
25854       //  'font-size' -- this can be set by the project 
25855 ];
25856
25857
25858 Roo.HtmlEditorCore.swapCodes   =[ 
25859     [    8211, "&#8211;" ], 
25860     [    8212, "&#8212;" ], 
25861     [    8216,  "'" ],  
25862     [    8217, "'" ],  
25863     [    8220, '"' ],  
25864     [    8221, '"' ],  
25865     [    8226, "*" ],  
25866     [    8230, "..." ]
25867 ]; 
25868
25869     /*
25870  * - LGPL
25871  *
25872  * HtmlEditor
25873  * 
25874  */
25875
25876 /**
25877  * @class Roo.bootstrap.HtmlEditor
25878  * @extends Roo.bootstrap.TextArea
25879  * Bootstrap HtmlEditor class
25880
25881  * @constructor
25882  * Create a new HtmlEditor
25883  * @param {Object} config The config object
25884  */
25885
25886 Roo.bootstrap.HtmlEditor = function(config){
25887     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25888     if (!this.toolbars) {
25889         this.toolbars = [];
25890     }
25891     
25892     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25893     this.addEvents({
25894             /**
25895              * @event initialize
25896              * Fires when the editor is fully initialized (including the iframe)
25897              * @param {HtmlEditor} this
25898              */
25899             initialize: true,
25900             /**
25901              * @event activate
25902              * Fires when the editor is first receives the focus. Any insertion must wait
25903              * until after this event.
25904              * @param {HtmlEditor} this
25905              */
25906             activate: true,
25907              /**
25908              * @event beforesync
25909              * Fires before the textarea is updated with content from the editor iframe. Return false
25910              * to cancel the sync.
25911              * @param {HtmlEditor} this
25912              * @param {String} html
25913              */
25914             beforesync: true,
25915              /**
25916              * @event beforepush
25917              * Fires before the iframe editor is updated with content from the textarea. Return false
25918              * to cancel the push.
25919              * @param {HtmlEditor} this
25920              * @param {String} html
25921              */
25922             beforepush: true,
25923              /**
25924              * @event sync
25925              * Fires when the textarea is updated with content from the editor iframe.
25926              * @param {HtmlEditor} this
25927              * @param {String} html
25928              */
25929             sync: true,
25930              /**
25931              * @event push
25932              * Fires when the iframe editor is updated with content from the textarea.
25933              * @param {HtmlEditor} this
25934              * @param {String} html
25935              */
25936             push: true,
25937              /**
25938              * @event editmodechange
25939              * Fires when the editor switches edit modes
25940              * @param {HtmlEditor} this
25941              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25942              */
25943             editmodechange: true,
25944             /**
25945              * @event editorevent
25946              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25947              * @param {HtmlEditor} this
25948              */
25949             editorevent: true,
25950             /**
25951              * @event firstfocus
25952              * Fires when on first focus - needed by toolbars..
25953              * @param {HtmlEditor} this
25954              */
25955             firstfocus: true,
25956             /**
25957              * @event autosave
25958              * Auto save the htmlEditor value as a file into Events
25959              * @param {HtmlEditor} this
25960              */
25961             autosave: true,
25962             /**
25963              * @event savedpreview
25964              * preview the saved version of htmlEditor
25965              * @param {HtmlEditor} this
25966              */
25967             savedpreview: true
25968         });
25969 };
25970
25971
25972 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25973     
25974     
25975       /**
25976      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25977      */
25978     toolbars : false,
25979     
25980      /**
25981     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25982     */
25983     btns : [],
25984    
25985      /**
25986      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25987      *                        Roo.resizable.
25988      */
25989     resizable : false,
25990      /**
25991      * @cfg {Number} height (in pixels)
25992      */   
25993     height: 300,
25994    /**
25995      * @cfg {Number} width (in pixels)
25996      */   
25997     width: false,
25998     
25999     /**
26000      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26001      * 
26002      */
26003     stylesheets: false,
26004     
26005     // id of frame..
26006     frameId: false,
26007     
26008     // private properties
26009     validationEvent : false,
26010     deferHeight: true,
26011     initialized : false,
26012     activated : false,
26013     
26014     onFocus : Roo.emptyFn,
26015     iframePad:3,
26016     hideMode:'offsets',
26017     
26018     tbContainer : false,
26019     
26020     bodyCls : '',
26021     
26022     toolbarContainer :function() {
26023         return this.wrap.select('.x-html-editor-tb',true).first();
26024     },
26025
26026     /**
26027      * Protected method that will not generally be called directly. It
26028      * is called when the editor creates its toolbar. Override this method if you need to
26029      * add custom toolbar buttons.
26030      * @param {HtmlEditor} editor
26031      */
26032     createToolbar : function(){
26033         Roo.log('renewing');
26034         Roo.log("create toolbars");
26035         
26036         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26037         this.toolbars[0].render(this.toolbarContainer());
26038         
26039         return;
26040         
26041 //        if (!editor.toolbars || !editor.toolbars.length) {
26042 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26043 //        }
26044 //        
26045 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26046 //            editor.toolbars[i] = Roo.factory(
26047 //                    typeof(editor.toolbars[i]) == 'string' ?
26048 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26049 //                Roo.bootstrap.HtmlEditor);
26050 //            editor.toolbars[i].init(editor);
26051 //        }
26052     },
26053
26054      
26055     // private
26056     onRender : function(ct, position)
26057     {
26058        // Roo.log("Call onRender: " + this.xtype);
26059         var _t = this;
26060         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26061       
26062         this.wrap = this.inputEl().wrap({
26063             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26064         });
26065         
26066         this.editorcore.onRender(ct, position);
26067          
26068         if (this.resizable) {
26069             this.resizeEl = new Roo.Resizable(this.wrap, {
26070                 pinned : true,
26071                 wrap: true,
26072                 dynamic : true,
26073                 minHeight : this.height,
26074                 height: this.height,
26075                 handles : this.resizable,
26076                 width: this.width,
26077                 listeners : {
26078                     resize : function(r, w, h) {
26079                         _t.onResize(w,h); // -something
26080                     }
26081                 }
26082             });
26083             
26084         }
26085         this.createToolbar(this);
26086        
26087         
26088         if(!this.width && this.resizable){
26089             this.setSize(this.wrap.getSize());
26090         }
26091         if (this.resizeEl) {
26092             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26093             // should trigger onReize..
26094         }
26095         
26096     },
26097
26098     // private
26099     onResize : function(w, h)
26100     {
26101         Roo.log('resize: ' +w + ',' + h );
26102         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26103         var ew = false;
26104         var eh = false;
26105         
26106         if(this.inputEl() ){
26107             if(typeof w == 'number'){
26108                 var aw = w - this.wrap.getFrameWidth('lr');
26109                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26110                 ew = aw;
26111             }
26112             if(typeof h == 'number'){
26113                  var tbh = -11;  // fixme it needs to tool bar size!
26114                 for (var i =0; i < this.toolbars.length;i++) {
26115                     // fixme - ask toolbars for heights?
26116                     tbh += this.toolbars[i].el.getHeight();
26117                     //if (this.toolbars[i].footer) {
26118                     //    tbh += this.toolbars[i].footer.el.getHeight();
26119                     //}
26120                 }
26121               
26122                 
26123                 
26124                 
26125                 
26126                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26127                 ah -= 5; // knock a few pixes off for look..
26128                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26129                 var eh = ah;
26130             }
26131         }
26132         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26133         this.editorcore.onResize(ew,eh);
26134         
26135     },
26136
26137     /**
26138      * Toggles the editor between standard and source edit mode.
26139      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26140      */
26141     toggleSourceEdit : function(sourceEditMode)
26142     {
26143         this.editorcore.toggleSourceEdit(sourceEditMode);
26144         
26145         if(this.editorcore.sourceEditMode){
26146             Roo.log('editor - showing textarea');
26147             
26148 //            Roo.log('in');
26149 //            Roo.log(this.syncValue());
26150             this.syncValue();
26151             this.inputEl().removeClass(['hide', 'x-hidden']);
26152             this.inputEl().dom.removeAttribute('tabIndex');
26153             this.inputEl().focus();
26154         }else{
26155             Roo.log('editor - hiding textarea');
26156 //            Roo.log('out')
26157 //            Roo.log(this.pushValue()); 
26158             this.pushValue();
26159             
26160             this.inputEl().addClass(['hide', 'x-hidden']);
26161             this.inputEl().dom.setAttribute('tabIndex', -1);
26162             //this.deferFocus();
26163         }
26164          
26165         if(this.resizable){
26166             this.setSize(this.wrap.getSize());
26167         }
26168         
26169         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26170     },
26171  
26172     // private (for BoxComponent)
26173     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26174
26175     // private (for BoxComponent)
26176     getResizeEl : function(){
26177         return this.wrap;
26178     },
26179
26180     // private (for BoxComponent)
26181     getPositionEl : function(){
26182         return this.wrap;
26183     },
26184
26185     // private
26186     initEvents : function(){
26187         this.originalValue = this.getValue();
26188     },
26189
26190 //    /**
26191 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26192 //     * @method
26193 //     */
26194 //    markInvalid : Roo.emptyFn,
26195 //    /**
26196 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26197 //     * @method
26198 //     */
26199 //    clearInvalid : Roo.emptyFn,
26200
26201     setValue : function(v){
26202         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26203         this.editorcore.pushValue();
26204     },
26205
26206      
26207     // private
26208     deferFocus : function(){
26209         this.focus.defer(10, this);
26210     },
26211
26212     // doc'ed in Field
26213     focus : function(){
26214         this.editorcore.focus();
26215         
26216     },
26217       
26218
26219     // private
26220     onDestroy : function(){
26221         
26222         
26223         
26224         if(this.rendered){
26225             
26226             for (var i =0; i < this.toolbars.length;i++) {
26227                 // fixme - ask toolbars for heights?
26228                 this.toolbars[i].onDestroy();
26229             }
26230             
26231             this.wrap.dom.innerHTML = '';
26232             this.wrap.remove();
26233         }
26234     },
26235
26236     // private
26237     onFirstFocus : function(){
26238         //Roo.log("onFirstFocus");
26239         this.editorcore.onFirstFocus();
26240          for (var i =0; i < this.toolbars.length;i++) {
26241             this.toolbars[i].onFirstFocus();
26242         }
26243         
26244     },
26245     
26246     // private
26247     syncValue : function()
26248     {   
26249         this.editorcore.syncValue();
26250     },
26251     
26252     pushValue : function()
26253     {   
26254         this.editorcore.pushValue();
26255     }
26256      
26257     
26258     // hide stuff that is not compatible
26259     /**
26260      * @event blur
26261      * @hide
26262      */
26263     /**
26264      * @event change
26265      * @hide
26266      */
26267     /**
26268      * @event focus
26269      * @hide
26270      */
26271     /**
26272      * @event specialkey
26273      * @hide
26274      */
26275     /**
26276      * @cfg {String} fieldClass @hide
26277      */
26278     /**
26279      * @cfg {String} focusClass @hide
26280      */
26281     /**
26282      * @cfg {String} autoCreate @hide
26283      */
26284     /**
26285      * @cfg {String} inputType @hide
26286      */
26287      
26288     /**
26289      * @cfg {String} invalidText @hide
26290      */
26291     /**
26292      * @cfg {String} msgFx @hide
26293      */
26294     /**
26295      * @cfg {String} validateOnBlur @hide
26296      */
26297 });
26298  
26299     
26300    
26301    
26302    
26303       
26304 Roo.namespace('Roo.bootstrap.htmleditor');
26305 /**
26306  * @class Roo.bootstrap.HtmlEditorToolbar1
26307  * Basic Toolbar
26308  * 
26309  * @example
26310  * Usage:
26311  *
26312  new Roo.bootstrap.HtmlEditor({
26313     ....
26314     toolbars : [
26315         new Roo.bootstrap.HtmlEditorToolbar1({
26316             disable : { fonts: 1 , format: 1, ..., ... , ...],
26317             btns : [ .... ]
26318         })
26319     }
26320      
26321  * 
26322  * @cfg {Object} disable List of elements to disable..
26323  * @cfg {Array} btns List of additional buttons.
26324  * 
26325  * 
26326  * NEEDS Extra CSS? 
26327  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26328  */
26329  
26330 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26331 {
26332     
26333     Roo.apply(this, config);
26334     
26335     // default disabled, based on 'good practice'..
26336     this.disable = this.disable || {};
26337     Roo.applyIf(this.disable, {
26338         fontSize : true,
26339         colors : true,
26340         specialElements : true
26341     });
26342     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26343     
26344     this.editor = config.editor;
26345     this.editorcore = config.editor.editorcore;
26346     
26347     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26348     
26349     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26350     // dont call parent... till later.
26351 }
26352 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26353      
26354     bar : true,
26355     
26356     editor : false,
26357     editorcore : false,
26358     
26359     
26360     formats : [
26361         "p" ,  
26362         "h1","h2","h3","h4","h5","h6", 
26363         "pre", "code", 
26364         "abbr", "acronym", "address", "cite", "samp", "var",
26365         'div','span'
26366     ],
26367     
26368     onRender : function(ct, position)
26369     {
26370        // Roo.log("Call onRender: " + this.xtype);
26371         
26372        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26373        Roo.log(this.el);
26374        this.el.dom.style.marginBottom = '0';
26375        var _this = this;
26376        var editorcore = this.editorcore;
26377        var editor= this.editor;
26378        
26379        var children = [];
26380        var btn = function(id,cmd , toggle, handler, html){
26381        
26382             var  event = toggle ? 'toggle' : 'click';
26383        
26384             var a = {
26385                 size : 'sm',
26386                 xtype: 'Button',
26387                 xns: Roo.bootstrap,
26388                 //glyphicon : id,
26389                 fa: id,
26390                 cmd : id || cmd,
26391                 enableToggle:toggle !== false,
26392                 html : html || '',
26393                 pressed : toggle ? false : null,
26394                 listeners : {}
26395             };
26396             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26397                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26398             };
26399             children.push(a);
26400             return a;
26401        }
26402        
26403     //    var cb_box = function...
26404         
26405         var style = {
26406                 xtype: 'Button',
26407                 size : 'sm',
26408                 xns: Roo.bootstrap,
26409                 fa : 'font',
26410                 //html : 'submit'
26411                 menu : {
26412                     xtype: 'Menu',
26413                     xns: Roo.bootstrap,
26414                     items:  []
26415                 }
26416         };
26417         Roo.each(this.formats, function(f) {
26418             style.menu.items.push({
26419                 xtype :'MenuItem',
26420                 xns: Roo.bootstrap,
26421                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26422                 tagname : f,
26423                 listeners : {
26424                     click : function()
26425                     {
26426                         editorcore.insertTag(this.tagname);
26427                         editor.focus();
26428                     }
26429                 }
26430                 
26431             });
26432         });
26433         children.push(style);   
26434         
26435         btn('bold',false,true);
26436         btn('italic',false,true);
26437         btn('align-left', 'justifyleft',true);
26438         btn('align-center', 'justifycenter',true);
26439         btn('align-right' , 'justifyright',true);
26440         btn('link', false, false, function(btn) {
26441             //Roo.log("create link?");
26442             var url = prompt(this.createLinkText, this.defaultLinkValue);
26443             if(url && url != 'http:/'+'/'){
26444                 this.editorcore.relayCmd('createlink', url);
26445             }
26446         }),
26447         btn('list','insertunorderedlist',true);
26448         btn('pencil', false,true, function(btn){
26449                 Roo.log(this);
26450                 this.toggleSourceEdit(btn.pressed);
26451         });
26452         
26453         if (this.editor.btns.length > 0) {
26454             for (var i = 0; i<this.editor.btns.length; i++) {
26455                 children.push(this.editor.btns[i]);
26456             }
26457         }
26458         
26459         /*
26460         var cog = {
26461                 xtype: 'Button',
26462                 size : 'sm',
26463                 xns: Roo.bootstrap,
26464                 glyphicon : 'cog',
26465                 //html : 'submit'
26466                 menu : {
26467                     xtype: 'Menu',
26468                     xns: Roo.bootstrap,
26469                     items:  []
26470                 }
26471         };
26472         
26473         cog.menu.items.push({
26474             xtype :'MenuItem',
26475             xns: Roo.bootstrap,
26476             html : Clean styles,
26477             tagname : f,
26478             listeners : {
26479                 click : function()
26480                 {
26481                     editorcore.insertTag(this.tagname);
26482                     editor.focus();
26483                 }
26484             }
26485             
26486         });
26487        */
26488         
26489          
26490        this.xtype = 'NavSimplebar';
26491         
26492         for(var i=0;i< children.length;i++) {
26493             
26494             this.buttons.add(this.addxtypeChild(children[i]));
26495             
26496         }
26497         
26498         editor.on('editorevent', this.updateToolbar, this);
26499     },
26500     onBtnClick : function(id)
26501     {
26502        this.editorcore.relayCmd(id);
26503        this.editorcore.focus();
26504     },
26505     
26506     /**
26507      * Protected method that will not generally be called directly. It triggers
26508      * a toolbar update by reading the markup state of the current selection in the editor.
26509      */
26510     updateToolbar: function(){
26511
26512         if(!this.editorcore.activated){
26513             this.editor.onFirstFocus(); // is this neeed?
26514             return;
26515         }
26516
26517         var btns = this.buttons; 
26518         var doc = this.editorcore.doc;
26519         btns.get('bold').setActive(doc.queryCommandState('bold'));
26520         btns.get('italic').setActive(doc.queryCommandState('italic'));
26521         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26522         
26523         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26524         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26525         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26526         
26527         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26528         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26529          /*
26530         
26531         var ans = this.editorcore.getAllAncestors();
26532         if (this.formatCombo) {
26533             
26534             
26535             var store = this.formatCombo.store;
26536             this.formatCombo.setValue("");
26537             for (var i =0; i < ans.length;i++) {
26538                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26539                     // select it..
26540                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26541                     break;
26542                 }
26543             }
26544         }
26545         
26546         
26547         
26548         // hides menus... - so this cant be on a menu...
26549         Roo.bootstrap.MenuMgr.hideAll();
26550         */
26551         Roo.bootstrap.MenuMgr.hideAll();
26552         //this.editorsyncValue();
26553     },
26554     onFirstFocus: function() {
26555         this.buttons.each(function(item){
26556            item.enable();
26557         });
26558     },
26559     toggleSourceEdit : function(sourceEditMode){
26560         
26561           
26562         if(sourceEditMode){
26563             Roo.log("disabling buttons");
26564            this.buttons.each( function(item){
26565                 if(item.cmd != 'pencil'){
26566                     item.disable();
26567                 }
26568             });
26569           
26570         }else{
26571             Roo.log("enabling buttons");
26572             if(this.editorcore.initialized){
26573                 this.buttons.each( function(item){
26574                     item.enable();
26575                 });
26576             }
26577             
26578         }
26579         Roo.log("calling toggole on editor");
26580         // tell the editor that it's been pressed..
26581         this.editor.toggleSourceEdit(sourceEditMode);
26582        
26583     }
26584 });
26585
26586
26587
26588
26589  
26590 /*
26591  * - LGPL
26592  */
26593
26594 /**
26595  * @class Roo.bootstrap.Markdown
26596  * @extends Roo.bootstrap.TextArea
26597  * Bootstrap Showdown editable area
26598  * @cfg {string} content
26599  * 
26600  * @constructor
26601  * Create a new Showdown
26602  */
26603
26604 Roo.bootstrap.Markdown = function(config){
26605     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26606    
26607 };
26608
26609 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26610     
26611     editing :false,
26612     
26613     initEvents : function()
26614     {
26615         
26616         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26617         this.markdownEl = this.el.createChild({
26618             cls : 'roo-markdown-area'
26619         });
26620         this.inputEl().addClass('d-none');
26621         if (this.getValue() == '') {
26622             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26623             
26624         } else {
26625             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26626         }
26627         this.markdownEl.on('click', this.toggleTextEdit, this);
26628         this.on('blur', this.toggleTextEdit, this);
26629         this.on('specialkey', this.resizeTextArea, this);
26630     },
26631     
26632     toggleTextEdit : function()
26633     {
26634         var sh = this.markdownEl.getHeight();
26635         this.inputEl().addClass('d-none');
26636         this.markdownEl.addClass('d-none');
26637         if (!this.editing) {
26638             // show editor?
26639             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26640             this.inputEl().removeClass('d-none');
26641             this.inputEl().focus();
26642             this.editing = true;
26643             return;
26644         }
26645         // show showdown...
26646         this.updateMarkdown();
26647         this.markdownEl.removeClass('d-none');
26648         this.editing = false;
26649         return;
26650     },
26651     updateMarkdown : function()
26652     {
26653         if (this.getValue() == '') {
26654             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26655             return;
26656         }
26657  
26658         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26659     },
26660     
26661     resizeTextArea: function () {
26662         
26663         var sh = 100;
26664         Roo.log([sh, this.getValue().split("\n").length * 30]);
26665         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26666     },
26667     setValue : function(val)
26668     {
26669         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26670         if (!this.editing) {
26671             this.updateMarkdown();
26672         }
26673         
26674     },
26675     focus : function()
26676     {
26677         if (!this.editing) {
26678             this.toggleTextEdit();
26679         }
26680         
26681     }
26682
26683
26684 });
26685 /**
26686  * @class Roo.bootstrap.Table.AbstractSelectionModel
26687  * @extends Roo.util.Observable
26688  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26689  * implemented by descendant classes.  This class should not be directly instantiated.
26690  * @constructor
26691  */
26692 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26693     this.locked = false;
26694     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26695 };
26696
26697
26698 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26699     /** @ignore Called by the grid automatically. Do not call directly. */
26700     init : function(grid){
26701         this.grid = grid;
26702         this.initEvents();
26703     },
26704
26705     /**
26706      * Locks the selections.
26707      */
26708     lock : function(){
26709         this.locked = true;
26710     },
26711
26712     /**
26713      * Unlocks the selections.
26714      */
26715     unlock : function(){
26716         this.locked = false;
26717     },
26718
26719     /**
26720      * Returns true if the selections are locked.
26721      * @return {Boolean}
26722      */
26723     isLocked : function(){
26724         return this.locked;
26725     },
26726     
26727     
26728     initEvents : function ()
26729     {
26730         
26731     }
26732 });
26733 /**
26734  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26735  * @class Roo.bootstrap.Table.RowSelectionModel
26736  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26737  * It supports multiple selections and keyboard selection/navigation. 
26738  * @constructor
26739  * @param {Object} config
26740  */
26741
26742 Roo.bootstrap.Table.RowSelectionModel = function(config){
26743     Roo.apply(this, config);
26744     this.selections = new Roo.util.MixedCollection(false, function(o){
26745         return o.id;
26746     });
26747
26748     this.last = false;
26749     this.lastActive = false;
26750
26751     this.addEvents({
26752         /**
26753              * @event selectionchange
26754              * Fires when the selection changes
26755              * @param {SelectionModel} this
26756              */
26757             "selectionchange" : true,
26758         /**
26759              * @event afterselectionchange
26760              * Fires after the selection changes (eg. by key press or clicking)
26761              * @param {SelectionModel} this
26762              */
26763             "afterselectionchange" : true,
26764         /**
26765              * @event beforerowselect
26766              * Fires when a row is selected being selected, return false to cancel.
26767              * @param {SelectionModel} this
26768              * @param {Number} rowIndex The selected index
26769              * @param {Boolean} keepExisting False if other selections will be cleared
26770              */
26771             "beforerowselect" : true,
26772         /**
26773              * @event rowselect
26774              * Fires when a row is selected.
26775              * @param {SelectionModel} this
26776              * @param {Number} rowIndex The selected index
26777              * @param {Roo.data.Record} r The record
26778              */
26779             "rowselect" : true,
26780         /**
26781              * @event rowdeselect
26782              * Fires when a row is deselected.
26783              * @param {SelectionModel} this
26784              * @param {Number} rowIndex The selected index
26785              */
26786         "rowdeselect" : true
26787     });
26788     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26789     this.locked = false;
26790  };
26791
26792 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26793     /**
26794      * @cfg {Boolean} singleSelect
26795      * True to allow selection of only one row at a time (defaults to false)
26796      */
26797     singleSelect : false,
26798
26799     // private
26800     initEvents : function()
26801     {
26802
26803         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26804         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26805         //}else{ // allow click to work like normal
26806          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26807         //}
26808         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26809         this.grid.on("rowclick", this.handleMouseDown, this);
26810         
26811         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26812             "up" : function(e){
26813                 if(!e.shiftKey){
26814                     this.selectPrevious(e.shiftKey);
26815                 }else if(this.last !== false && this.lastActive !== false){
26816                     var last = this.last;
26817                     this.selectRange(this.last,  this.lastActive-1);
26818                     this.grid.getView().focusRow(this.lastActive);
26819                     if(last !== false){
26820                         this.last = last;
26821                     }
26822                 }else{
26823                     this.selectFirstRow();
26824                 }
26825                 this.fireEvent("afterselectionchange", this);
26826             },
26827             "down" : function(e){
26828                 if(!e.shiftKey){
26829                     this.selectNext(e.shiftKey);
26830                 }else if(this.last !== false && this.lastActive !== false){
26831                     var last = this.last;
26832                     this.selectRange(this.last,  this.lastActive+1);
26833                     this.grid.getView().focusRow(this.lastActive);
26834                     if(last !== false){
26835                         this.last = last;
26836                     }
26837                 }else{
26838                     this.selectFirstRow();
26839                 }
26840                 this.fireEvent("afterselectionchange", this);
26841             },
26842             scope: this
26843         });
26844         this.grid.store.on('load', function(){
26845             this.selections.clear();
26846         },this);
26847         /*
26848         var view = this.grid.view;
26849         view.on("refresh", this.onRefresh, this);
26850         view.on("rowupdated", this.onRowUpdated, this);
26851         view.on("rowremoved", this.onRemove, this);
26852         */
26853     },
26854
26855     // private
26856     onRefresh : function()
26857     {
26858         var ds = this.grid.store, i, v = this.grid.view;
26859         var s = this.selections;
26860         s.each(function(r){
26861             if((i = ds.indexOfId(r.id)) != -1){
26862                 v.onRowSelect(i);
26863             }else{
26864                 s.remove(r);
26865             }
26866         });
26867     },
26868
26869     // private
26870     onRemove : function(v, index, r){
26871         this.selections.remove(r);
26872     },
26873
26874     // private
26875     onRowUpdated : function(v, index, r){
26876         if(this.isSelected(r)){
26877             v.onRowSelect(index);
26878         }
26879     },
26880
26881     /**
26882      * Select records.
26883      * @param {Array} records The records to select
26884      * @param {Boolean} keepExisting (optional) True to keep existing selections
26885      */
26886     selectRecords : function(records, keepExisting)
26887     {
26888         if(!keepExisting){
26889             this.clearSelections();
26890         }
26891             var ds = this.grid.store;
26892         for(var i = 0, len = records.length; i < len; i++){
26893             this.selectRow(ds.indexOf(records[i]), true);
26894         }
26895     },
26896
26897     /**
26898      * Gets the number of selected rows.
26899      * @return {Number}
26900      */
26901     getCount : function(){
26902         return this.selections.length;
26903     },
26904
26905     /**
26906      * Selects the first row in the grid.
26907      */
26908     selectFirstRow : function(){
26909         this.selectRow(0);
26910     },
26911
26912     /**
26913      * Select the last row.
26914      * @param {Boolean} keepExisting (optional) True to keep existing selections
26915      */
26916     selectLastRow : function(keepExisting){
26917         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26918         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26919     },
26920
26921     /**
26922      * Selects the row immediately following the last selected row.
26923      * @param {Boolean} keepExisting (optional) True to keep existing selections
26924      */
26925     selectNext : function(keepExisting)
26926     {
26927             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26928             this.selectRow(this.last+1, keepExisting);
26929             this.grid.getView().focusRow(this.last);
26930         }
26931     },
26932
26933     /**
26934      * Selects the row that precedes the last selected row.
26935      * @param {Boolean} keepExisting (optional) True to keep existing selections
26936      */
26937     selectPrevious : function(keepExisting){
26938         if(this.last){
26939             this.selectRow(this.last-1, keepExisting);
26940             this.grid.getView().focusRow(this.last);
26941         }
26942     },
26943
26944     /**
26945      * Returns the selected records
26946      * @return {Array} Array of selected records
26947      */
26948     getSelections : function(){
26949         return [].concat(this.selections.items);
26950     },
26951
26952     /**
26953      * Returns the first selected record.
26954      * @return {Record}
26955      */
26956     getSelected : function(){
26957         return this.selections.itemAt(0);
26958     },
26959
26960
26961     /**
26962      * Clears all selections.
26963      */
26964     clearSelections : function(fast)
26965     {
26966         if(this.locked) {
26967             return;
26968         }
26969         if(fast !== true){
26970                 var ds = this.grid.store;
26971             var s = this.selections;
26972             s.each(function(r){
26973                 this.deselectRow(ds.indexOfId(r.id));
26974             }, this);
26975             s.clear();
26976         }else{
26977             this.selections.clear();
26978         }
26979         this.last = false;
26980     },
26981
26982
26983     /**
26984      * Selects all rows.
26985      */
26986     selectAll : function(){
26987         if(this.locked) {
26988             return;
26989         }
26990         this.selections.clear();
26991         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26992             this.selectRow(i, true);
26993         }
26994     },
26995
26996     /**
26997      * Returns True if there is a selection.
26998      * @return {Boolean}
26999      */
27000     hasSelection : function(){
27001         return this.selections.length > 0;
27002     },
27003
27004     /**
27005      * Returns True if the specified row is selected.
27006      * @param {Number/Record} record The record or index of the record to check
27007      * @return {Boolean}
27008      */
27009     isSelected : function(index){
27010             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27011         return (r && this.selections.key(r.id) ? true : false);
27012     },
27013
27014     /**
27015      * Returns True if the specified record id is selected.
27016      * @param {String} id The id of record to check
27017      * @return {Boolean}
27018      */
27019     isIdSelected : function(id){
27020         return (this.selections.key(id) ? true : false);
27021     },
27022
27023
27024     // private
27025     handleMouseDBClick : function(e, t){
27026         
27027     },
27028     // private
27029     handleMouseDown : function(e, t)
27030     {
27031             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27032         if(this.isLocked() || rowIndex < 0 ){
27033             return;
27034         };
27035         if(e.shiftKey && this.last !== false){
27036             var last = this.last;
27037             this.selectRange(last, rowIndex, e.ctrlKey);
27038             this.last = last; // reset the last
27039             t.focus();
27040     
27041         }else{
27042             var isSelected = this.isSelected(rowIndex);
27043             //Roo.log("select row:" + rowIndex);
27044             if(isSelected){
27045                 this.deselectRow(rowIndex);
27046             } else {
27047                         this.selectRow(rowIndex, true);
27048             }
27049     
27050             /*
27051                 if(e.button !== 0 && isSelected){
27052                 alert('rowIndex 2: ' + rowIndex);
27053                     view.focusRow(rowIndex);
27054                 }else if(e.ctrlKey && isSelected){
27055                     this.deselectRow(rowIndex);
27056                 }else if(!isSelected){
27057                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27058                     view.focusRow(rowIndex);
27059                 }
27060             */
27061         }
27062         this.fireEvent("afterselectionchange", this);
27063     },
27064     // private
27065     handleDragableRowClick :  function(grid, rowIndex, e) 
27066     {
27067         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27068             this.selectRow(rowIndex, false);
27069             grid.view.focusRow(rowIndex);
27070              this.fireEvent("afterselectionchange", this);
27071         }
27072     },
27073     
27074     /**
27075      * Selects multiple rows.
27076      * @param {Array} rows Array of the indexes of the row to select
27077      * @param {Boolean} keepExisting (optional) True to keep existing selections
27078      */
27079     selectRows : function(rows, keepExisting){
27080         if(!keepExisting){
27081             this.clearSelections();
27082         }
27083         for(var i = 0, len = rows.length; i < len; i++){
27084             this.selectRow(rows[i], true);
27085         }
27086     },
27087
27088     /**
27089      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27090      * @param {Number} startRow The index of the first row in the range
27091      * @param {Number} endRow The index of the last row in the range
27092      * @param {Boolean} keepExisting (optional) True to retain existing selections
27093      */
27094     selectRange : function(startRow, endRow, keepExisting){
27095         if(this.locked) {
27096             return;
27097         }
27098         if(!keepExisting){
27099             this.clearSelections();
27100         }
27101         if(startRow <= endRow){
27102             for(var i = startRow; i <= endRow; i++){
27103                 this.selectRow(i, true);
27104             }
27105         }else{
27106             for(var i = startRow; i >= endRow; i--){
27107                 this.selectRow(i, true);
27108             }
27109         }
27110     },
27111
27112     /**
27113      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27114      * @param {Number} startRow The index of the first row in the range
27115      * @param {Number} endRow The index of the last row in the range
27116      */
27117     deselectRange : function(startRow, endRow, preventViewNotify){
27118         if(this.locked) {
27119             return;
27120         }
27121         for(var i = startRow; i <= endRow; i++){
27122             this.deselectRow(i, preventViewNotify);
27123         }
27124     },
27125
27126     /**
27127      * Selects a row.
27128      * @param {Number} row The index of the row to select
27129      * @param {Boolean} keepExisting (optional) True to keep existing selections
27130      */
27131     selectRow : function(index, keepExisting, preventViewNotify)
27132     {
27133             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27134             return;
27135         }
27136         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27137             if(!keepExisting || this.singleSelect){
27138                 this.clearSelections();
27139             }
27140             
27141             var r = this.grid.store.getAt(index);
27142             //console.log('selectRow - record id :' + r.id);
27143             
27144             this.selections.add(r);
27145             this.last = this.lastActive = index;
27146             if(!preventViewNotify){
27147                 var proxy = new Roo.Element(
27148                                 this.grid.getRowDom(index)
27149                 );
27150                 proxy.addClass('bg-info info');
27151             }
27152             this.fireEvent("rowselect", this, index, r);
27153             this.fireEvent("selectionchange", this);
27154         }
27155     },
27156
27157     /**
27158      * Deselects a row.
27159      * @param {Number} row The index of the row to deselect
27160      */
27161     deselectRow : function(index, preventViewNotify)
27162     {
27163         if(this.locked) {
27164             return;
27165         }
27166         if(this.last == index){
27167             this.last = false;
27168         }
27169         if(this.lastActive == index){
27170             this.lastActive = false;
27171         }
27172         
27173         var r = this.grid.store.getAt(index);
27174         if (!r) {
27175             return;
27176         }
27177         
27178         this.selections.remove(r);
27179         //.console.log('deselectRow - record id :' + r.id);
27180         if(!preventViewNotify){
27181         
27182             var proxy = new Roo.Element(
27183                 this.grid.getRowDom(index)
27184             );
27185             proxy.removeClass('bg-info info');
27186         }
27187         this.fireEvent("rowdeselect", this, index);
27188         this.fireEvent("selectionchange", this);
27189     },
27190
27191     // private
27192     restoreLast : function(){
27193         if(this._last){
27194             this.last = this._last;
27195         }
27196     },
27197
27198     // private
27199     acceptsNav : function(row, col, cm){
27200         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27201     },
27202
27203     // private
27204     onEditorKey : function(field, e){
27205         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27206         if(k == e.TAB){
27207             e.stopEvent();
27208             ed.completeEdit();
27209             if(e.shiftKey){
27210                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27211             }else{
27212                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27213             }
27214         }else if(k == e.ENTER && !e.ctrlKey){
27215             e.stopEvent();
27216             ed.completeEdit();
27217             if(e.shiftKey){
27218                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27219             }else{
27220                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27221             }
27222         }else if(k == e.ESC){
27223             ed.cancelEdit();
27224         }
27225         if(newCell){
27226             g.startEditing(newCell[0], newCell[1]);
27227         }
27228     }
27229 });
27230 /*
27231  * Based on:
27232  * Ext JS Library 1.1.1
27233  * Copyright(c) 2006-2007, Ext JS, LLC.
27234  *
27235  * Originally Released Under LGPL - original licence link has changed is not relivant.
27236  *
27237  * Fork - LGPL
27238  * <script type="text/javascript">
27239  */
27240  
27241 /**
27242  * @class Roo.bootstrap.PagingToolbar
27243  * @extends Roo.bootstrap.NavSimplebar
27244  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27245  * @constructor
27246  * Create a new PagingToolbar
27247  * @param {Object} config The config object
27248  * @param {Roo.data.Store} store
27249  */
27250 Roo.bootstrap.PagingToolbar = function(config)
27251 {
27252     // old args format still supported... - xtype is prefered..
27253         // created from xtype...
27254     
27255     this.ds = config.dataSource;
27256     
27257     if (config.store && !this.ds) {
27258         this.store= Roo.factory(config.store, Roo.data);
27259         this.ds = this.store;
27260         this.ds.xmodule = this.xmodule || false;
27261     }
27262     
27263     this.toolbarItems = [];
27264     if (config.items) {
27265         this.toolbarItems = config.items;
27266     }
27267     
27268     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27269     
27270     this.cursor = 0;
27271     
27272     if (this.ds) { 
27273         this.bind(this.ds);
27274     }
27275     
27276     if (Roo.bootstrap.version == 4) {
27277         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27278     } else {
27279         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27280     }
27281     
27282 };
27283
27284 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27285     /**
27286      * @cfg {Roo.data.Store} dataSource
27287      * The underlying data store providing the paged data
27288      */
27289     /**
27290      * @cfg {String/HTMLElement/Element} container
27291      * container The id or element that will contain the toolbar
27292      */
27293     /**
27294      * @cfg {Boolean} displayInfo
27295      * True to display the displayMsg (defaults to false)
27296      */
27297     /**
27298      * @cfg {Number} pageSize
27299      * The number of records to display per page (defaults to 20)
27300      */
27301     pageSize: 20,
27302     /**
27303      * @cfg {String} displayMsg
27304      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27305      */
27306     displayMsg : 'Displaying {0} - {1} of {2}',
27307     /**
27308      * @cfg {String} emptyMsg
27309      * The message to display when no records are found (defaults to "No data to display")
27310      */
27311     emptyMsg : 'No data to display',
27312     /**
27313      * Customizable piece of the default paging text (defaults to "Page")
27314      * @type String
27315      */
27316     beforePageText : "Page",
27317     /**
27318      * Customizable piece of the default paging text (defaults to "of %0")
27319      * @type String
27320      */
27321     afterPageText : "of {0}",
27322     /**
27323      * Customizable piece of the default paging text (defaults to "First Page")
27324      * @type String
27325      */
27326     firstText : "First Page",
27327     /**
27328      * Customizable piece of the default paging text (defaults to "Previous Page")
27329      * @type String
27330      */
27331     prevText : "Previous Page",
27332     /**
27333      * Customizable piece of the default paging text (defaults to "Next Page")
27334      * @type String
27335      */
27336     nextText : "Next Page",
27337     /**
27338      * Customizable piece of the default paging text (defaults to "Last Page")
27339      * @type String
27340      */
27341     lastText : "Last Page",
27342     /**
27343      * Customizable piece of the default paging text (defaults to "Refresh")
27344      * @type String
27345      */
27346     refreshText : "Refresh",
27347
27348     buttons : false,
27349     // private
27350     onRender : function(ct, position) 
27351     {
27352         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27353         this.navgroup.parentId = this.id;
27354         this.navgroup.onRender(this.el, null);
27355         // add the buttons to the navgroup
27356         
27357         if(this.displayInfo){
27358             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27359             this.displayEl = this.el.select('.x-paging-info', true).first();
27360 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27361 //            this.displayEl = navel.el.select('span',true).first();
27362         }
27363         
27364         var _this = this;
27365         
27366         if(this.buttons){
27367             Roo.each(_this.buttons, function(e){ // this might need to use render????
27368                Roo.factory(e).render(_this.el);
27369             });
27370         }
27371             
27372         Roo.each(_this.toolbarItems, function(e) {
27373             _this.navgroup.addItem(e);
27374         });
27375         
27376         
27377         this.first = this.navgroup.addItem({
27378             tooltip: this.firstText,
27379             cls: "prev btn-outline-secondary",
27380             html : ' <i class="fa fa-step-backward"></i>',
27381             disabled: true,
27382             preventDefault: true,
27383             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27384         });
27385         
27386         this.prev =  this.navgroup.addItem({
27387             tooltip: this.prevText,
27388             cls: "prev btn-outline-secondary",
27389             html : ' <i class="fa fa-backward"></i>',
27390             disabled: true,
27391             preventDefault: true,
27392             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27393         });
27394     //this.addSeparator();
27395         
27396         
27397         var field = this.navgroup.addItem( {
27398             tagtype : 'span',
27399             cls : 'x-paging-position  btn-outline-secondary',
27400              disabled: true,
27401             html : this.beforePageText  +
27402                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27403                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27404          } ); //?? escaped?
27405         
27406         this.field = field.el.select('input', true).first();
27407         this.field.on("keydown", this.onPagingKeydown, this);
27408         this.field.on("focus", function(){this.dom.select();});
27409     
27410     
27411         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27412         //this.field.setHeight(18);
27413         //this.addSeparator();
27414         this.next = this.navgroup.addItem({
27415             tooltip: this.nextText,
27416             cls: "next btn-outline-secondary",
27417             html : ' <i class="fa fa-forward"></i>',
27418             disabled: true,
27419             preventDefault: true,
27420             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27421         });
27422         this.last = this.navgroup.addItem({
27423             tooltip: this.lastText,
27424             html : ' <i class="fa fa-step-forward"></i>',
27425             cls: "next btn-outline-secondary",
27426             disabled: true,
27427             preventDefault: true,
27428             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27429         });
27430     //this.addSeparator();
27431         this.loading = this.navgroup.addItem({
27432             tooltip: this.refreshText,
27433             cls: "btn-outline-secondary",
27434             html : ' <i class="fa fa-refresh"></i>',
27435             preventDefault: true,
27436             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27437         });
27438         
27439     },
27440
27441     // private
27442     updateInfo : function(){
27443         if(this.displayEl){
27444             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27445             var msg = count == 0 ?
27446                 this.emptyMsg :
27447                 String.format(
27448                     this.displayMsg,
27449                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27450                 );
27451             this.displayEl.update(msg);
27452         }
27453     },
27454
27455     // private
27456     onLoad : function(ds, r, o)
27457     {
27458         this.cursor = o.params && o.params.start ? o.params.start : 0;
27459         
27460         var d = this.getPageData(),
27461             ap = d.activePage,
27462             ps = d.pages;
27463         
27464         
27465         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27466         this.field.dom.value = ap;
27467         this.first.setDisabled(ap == 1);
27468         this.prev.setDisabled(ap == 1);
27469         this.next.setDisabled(ap == ps);
27470         this.last.setDisabled(ap == ps);
27471         this.loading.enable();
27472         this.updateInfo();
27473     },
27474
27475     // private
27476     getPageData : function(){
27477         var total = this.ds.getTotalCount();
27478         return {
27479             total : total,
27480             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27481             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27482         };
27483     },
27484
27485     // private
27486     onLoadError : function(){
27487         this.loading.enable();
27488     },
27489
27490     // private
27491     onPagingKeydown : function(e){
27492         var k = e.getKey();
27493         var d = this.getPageData();
27494         if(k == e.RETURN){
27495             var v = this.field.dom.value, pageNum;
27496             if(!v || isNaN(pageNum = parseInt(v, 10))){
27497                 this.field.dom.value = d.activePage;
27498                 return;
27499             }
27500             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27501             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27502             e.stopEvent();
27503         }
27504         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))
27505         {
27506           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27507           this.field.dom.value = pageNum;
27508           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27509           e.stopEvent();
27510         }
27511         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27512         {
27513           var v = this.field.dom.value, pageNum; 
27514           var increment = (e.shiftKey) ? 10 : 1;
27515           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27516                 increment *= -1;
27517           }
27518           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27519             this.field.dom.value = d.activePage;
27520             return;
27521           }
27522           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27523           {
27524             this.field.dom.value = parseInt(v, 10) + increment;
27525             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27526             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27527           }
27528           e.stopEvent();
27529         }
27530     },
27531
27532     // private
27533     beforeLoad : function(){
27534         if(this.loading){
27535             this.loading.disable();
27536         }
27537     },
27538
27539     // private
27540     onClick : function(which){
27541         
27542         var ds = this.ds;
27543         if (!ds) {
27544             return;
27545         }
27546         
27547         switch(which){
27548             case "first":
27549                 ds.load({params:{start: 0, limit: this.pageSize}});
27550             break;
27551             case "prev":
27552                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27553             break;
27554             case "next":
27555                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27556             break;
27557             case "last":
27558                 var total = ds.getTotalCount();
27559                 var extra = total % this.pageSize;
27560                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27561                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27562             break;
27563             case "refresh":
27564                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27565             break;
27566         }
27567     },
27568
27569     /**
27570      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27571      * @param {Roo.data.Store} store The data store to unbind
27572      */
27573     unbind : function(ds){
27574         ds.un("beforeload", this.beforeLoad, this);
27575         ds.un("load", this.onLoad, this);
27576         ds.un("loadexception", this.onLoadError, this);
27577         ds.un("remove", this.updateInfo, this);
27578         ds.un("add", this.updateInfo, this);
27579         this.ds = undefined;
27580     },
27581
27582     /**
27583      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27584      * @param {Roo.data.Store} store The data store to bind
27585      */
27586     bind : function(ds){
27587         ds.on("beforeload", this.beforeLoad, this);
27588         ds.on("load", this.onLoad, this);
27589         ds.on("loadexception", this.onLoadError, this);
27590         ds.on("remove", this.updateInfo, this);
27591         ds.on("add", this.updateInfo, this);
27592         this.ds = ds;
27593     }
27594 });/*
27595  * - LGPL
27596  *
27597  * element
27598  * 
27599  */
27600
27601 /**
27602  * @class Roo.bootstrap.MessageBar
27603  * @extends Roo.bootstrap.Component
27604  * Bootstrap MessageBar class
27605  * @cfg {String} html contents of the MessageBar
27606  * @cfg {String} weight (info | success | warning | danger) default info
27607  * @cfg {String} beforeClass insert the bar before the given class
27608  * @cfg {Boolean} closable (true | false) default false
27609  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27610  * 
27611  * @constructor
27612  * Create a new Element
27613  * @param {Object} config The config object
27614  */
27615
27616 Roo.bootstrap.MessageBar = function(config){
27617     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27618 };
27619
27620 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27621     
27622     html: '',
27623     weight: 'info',
27624     closable: false,
27625     fixed: false,
27626     beforeClass: 'bootstrap-sticky-wrap',
27627     
27628     getAutoCreate : function(){
27629         
27630         var cfg = {
27631             tag: 'div',
27632             cls: 'alert alert-dismissable alert-' + this.weight,
27633             cn: [
27634                 {
27635                     tag: 'span',
27636                     cls: 'message',
27637                     html: this.html || ''
27638                 }
27639             ]
27640         };
27641         
27642         if(this.fixed){
27643             cfg.cls += ' alert-messages-fixed';
27644         }
27645         
27646         if(this.closable){
27647             cfg.cn.push({
27648                 tag: 'button',
27649                 cls: 'close',
27650                 html: 'x'
27651             });
27652         }
27653         
27654         return cfg;
27655     },
27656     
27657     onRender : function(ct, position)
27658     {
27659         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27660         
27661         if(!this.el){
27662             var cfg = Roo.apply({},  this.getAutoCreate());
27663             cfg.id = Roo.id();
27664             
27665             if (this.cls) {
27666                 cfg.cls += ' ' + this.cls;
27667             }
27668             if (this.style) {
27669                 cfg.style = this.style;
27670             }
27671             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27672             
27673             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27674         }
27675         
27676         this.el.select('>button.close').on('click', this.hide, this);
27677         
27678     },
27679     
27680     show : function()
27681     {
27682         if (!this.rendered) {
27683             this.render();
27684         }
27685         
27686         this.el.show();
27687         
27688         this.fireEvent('show', this);
27689         
27690     },
27691     
27692     hide : function()
27693     {
27694         if (!this.rendered) {
27695             this.render();
27696         }
27697         
27698         this.el.hide();
27699         
27700         this.fireEvent('hide', this);
27701     },
27702     
27703     update : function()
27704     {
27705 //        var e = this.el.dom.firstChild;
27706 //        
27707 //        if(this.closable){
27708 //            e = e.nextSibling;
27709 //        }
27710 //        
27711 //        e.data = this.html || '';
27712
27713         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27714     }
27715    
27716 });
27717
27718  
27719
27720      /*
27721  * - LGPL
27722  *
27723  * Graph
27724  * 
27725  */
27726
27727
27728 /**
27729  * @class Roo.bootstrap.Graph
27730  * @extends Roo.bootstrap.Component
27731  * Bootstrap Graph class
27732 > Prameters
27733  -sm {number} sm 4
27734  -md {number} md 5
27735  @cfg {String} graphtype  bar | vbar | pie
27736  @cfg {number} g_x coodinator | centre x (pie)
27737  @cfg {number} g_y coodinator | centre y (pie)
27738  @cfg {number} g_r radius (pie)
27739  @cfg {number} g_height height of the chart (respected by all elements in the set)
27740  @cfg {number} g_width width of the chart (respected by all elements in the set)
27741  @cfg {Object} title The title of the chart
27742     
27743  -{Array}  values
27744  -opts (object) options for the chart 
27745      o {
27746      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27747      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27748      o vgutter (number)
27749      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.
27750      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27751      o to
27752      o stretch (boolean)
27753      o }
27754  -opts (object) options for the pie
27755      o{
27756      o cut
27757      o startAngle (number)
27758      o endAngle (number)
27759      } 
27760  *
27761  * @constructor
27762  * Create a new Input
27763  * @param {Object} config The config object
27764  */
27765
27766 Roo.bootstrap.Graph = function(config){
27767     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27768     
27769     this.addEvents({
27770         // img events
27771         /**
27772          * @event click
27773          * The img click event for the img.
27774          * @param {Roo.EventObject} e
27775          */
27776         "click" : true
27777     });
27778 };
27779
27780 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27781     
27782     sm: 4,
27783     md: 5,
27784     graphtype: 'bar',
27785     g_height: 250,
27786     g_width: 400,
27787     g_x: 50,
27788     g_y: 50,
27789     g_r: 30,
27790     opts:{
27791         //g_colors: this.colors,
27792         g_type: 'soft',
27793         g_gutter: '20%'
27794
27795     },
27796     title : false,
27797
27798     getAutoCreate : function(){
27799         
27800         var cfg = {
27801             tag: 'div',
27802             html : null
27803         };
27804         
27805         
27806         return  cfg;
27807     },
27808
27809     onRender : function(ct,position){
27810         
27811         
27812         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27813         
27814         if (typeof(Raphael) == 'undefined') {
27815             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27816             return;
27817         }
27818         
27819         this.raphael = Raphael(this.el.dom);
27820         
27821                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27822                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27823                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27824                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27825                 /*
27826                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27827                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27828                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27829                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27830                 
27831                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27832                 r.barchart(330, 10, 300, 220, data1);
27833                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27834                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27835                 */
27836                 
27837                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27838                 // r.barchart(30, 30, 560, 250,  xdata, {
27839                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27840                 //     axis : "0 0 1 1",
27841                 //     axisxlabels :  xdata
27842                 //     //yvalues : cols,
27843                    
27844                 // });
27845 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27846 //        
27847 //        this.load(null,xdata,{
27848 //                axis : "0 0 1 1",
27849 //                axisxlabels :  xdata
27850 //                });
27851
27852     },
27853
27854     load : function(graphtype,xdata,opts)
27855     {
27856         this.raphael.clear();
27857         if(!graphtype) {
27858             graphtype = this.graphtype;
27859         }
27860         if(!opts){
27861             opts = this.opts;
27862         }
27863         var r = this.raphael,
27864             fin = function () {
27865                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27866             },
27867             fout = function () {
27868                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27869             },
27870             pfin = function() {
27871                 this.sector.stop();
27872                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27873
27874                 if (this.label) {
27875                     this.label[0].stop();
27876                     this.label[0].attr({ r: 7.5 });
27877                     this.label[1].attr({ "font-weight": 800 });
27878                 }
27879             },
27880             pfout = function() {
27881                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27882
27883                 if (this.label) {
27884                     this.label[0].animate({ r: 5 }, 500, "bounce");
27885                     this.label[1].attr({ "font-weight": 400 });
27886                 }
27887             };
27888
27889         switch(graphtype){
27890             case 'bar':
27891                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27892                 break;
27893             case 'hbar':
27894                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27895                 break;
27896             case 'pie':
27897 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27898 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27899 //            
27900                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27901                 
27902                 break;
27903
27904         }
27905         
27906         if(this.title){
27907             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27908         }
27909         
27910     },
27911     
27912     setTitle: function(o)
27913     {
27914         this.title = o;
27915     },
27916     
27917     initEvents: function() {
27918         
27919         if(!this.href){
27920             this.el.on('click', this.onClick, this);
27921         }
27922     },
27923     
27924     onClick : function(e)
27925     {
27926         Roo.log('img onclick');
27927         this.fireEvent('click', this, e);
27928     }
27929    
27930 });
27931
27932  
27933 /*
27934  * - LGPL
27935  *
27936  * numberBox
27937  * 
27938  */
27939 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27940
27941 /**
27942  * @class Roo.bootstrap.dash.NumberBox
27943  * @extends Roo.bootstrap.Component
27944  * Bootstrap NumberBox class
27945  * @cfg {String} headline Box headline
27946  * @cfg {String} content Box content
27947  * @cfg {String} icon Box icon
27948  * @cfg {String} footer Footer text
27949  * @cfg {String} fhref Footer href
27950  * 
27951  * @constructor
27952  * Create a new NumberBox
27953  * @param {Object} config The config object
27954  */
27955
27956
27957 Roo.bootstrap.dash.NumberBox = function(config){
27958     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27959     
27960 };
27961
27962 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27963     
27964     headline : '',
27965     content : '',
27966     icon : '',
27967     footer : '',
27968     fhref : '',
27969     ficon : '',
27970     
27971     getAutoCreate : function(){
27972         
27973         var cfg = {
27974             tag : 'div',
27975             cls : 'small-box ',
27976             cn : [
27977                 {
27978                     tag : 'div',
27979                     cls : 'inner',
27980                     cn :[
27981                         {
27982                             tag : 'h3',
27983                             cls : 'roo-headline',
27984                             html : this.headline
27985                         },
27986                         {
27987                             tag : 'p',
27988                             cls : 'roo-content',
27989                             html : this.content
27990                         }
27991                     ]
27992                 }
27993             ]
27994         };
27995         
27996         if(this.icon){
27997             cfg.cn.push({
27998                 tag : 'div',
27999                 cls : 'icon',
28000                 cn :[
28001                     {
28002                         tag : 'i',
28003                         cls : 'ion ' + this.icon
28004                     }
28005                 ]
28006             });
28007         }
28008         
28009         if(this.footer){
28010             var footer = {
28011                 tag : 'a',
28012                 cls : 'small-box-footer',
28013                 href : this.fhref || '#',
28014                 html : this.footer
28015             };
28016             
28017             cfg.cn.push(footer);
28018             
28019         }
28020         
28021         return  cfg;
28022     },
28023
28024     onRender : function(ct,position){
28025         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28026
28027
28028        
28029                 
28030     },
28031
28032     setHeadline: function (value)
28033     {
28034         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28035     },
28036     
28037     setFooter: function (value, href)
28038     {
28039         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28040         
28041         if(href){
28042             this.el.select('a.small-box-footer',true).first().attr('href', href);
28043         }
28044         
28045     },
28046
28047     setContent: function (value)
28048     {
28049         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28050     },
28051
28052     initEvents: function() 
28053     {   
28054         
28055     }
28056     
28057 });
28058
28059  
28060 /*
28061  * - LGPL
28062  *
28063  * TabBox
28064  * 
28065  */
28066 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28067
28068 /**
28069  * @class Roo.bootstrap.dash.TabBox
28070  * @extends Roo.bootstrap.Component
28071  * Bootstrap TabBox class
28072  * @cfg {String} title Title of the TabBox
28073  * @cfg {String} icon Icon of the TabBox
28074  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28075  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28076  * 
28077  * @constructor
28078  * Create a new TabBox
28079  * @param {Object} config The config object
28080  */
28081
28082
28083 Roo.bootstrap.dash.TabBox = function(config){
28084     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28085     this.addEvents({
28086         // raw events
28087         /**
28088          * @event addpane
28089          * When a pane is added
28090          * @param {Roo.bootstrap.dash.TabPane} pane
28091          */
28092         "addpane" : true,
28093         /**
28094          * @event activatepane
28095          * When a pane is activated
28096          * @param {Roo.bootstrap.dash.TabPane} pane
28097          */
28098         "activatepane" : true
28099         
28100          
28101     });
28102     
28103     this.panes = [];
28104 };
28105
28106 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28107
28108     title : '',
28109     icon : false,
28110     showtabs : true,
28111     tabScrollable : false,
28112     
28113     getChildContainer : function()
28114     {
28115         return this.el.select('.tab-content', true).first();
28116     },
28117     
28118     getAutoCreate : function(){
28119         
28120         var header = {
28121             tag: 'li',
28122             cls: 'pull-left header',
28123             html: this.title,
28124             cn : []
28125         };
28126         
28127         if(this.icon){
28128             header.cn.push({
28129                 tag: 'i',
28130                 cls: 'fa ' + this.icon
28131             });
28132         }
28133         
28134         var h = {
28135             tag: 'ul',
28136             cls: 'nav nav-tabs pull-right',
28137             cn: [
28138                 header
28139             ]
28140         };
28141         
28142         if(this.tabScrollable){
28143             h = {
28144                 tag: 'div',
28145                 cls: 'tab-header',
28146                 cn: [
28147                     {
28148                         tag: 'ul',
28149                         cls: 'nav nav-tabs pull-right',
28150                         cn: [
28151                             header
28152                         ]
28153                     }
28154                 ]
28155             };
28156         }
28157         
28158         var cfg = {
28159             tag: 'div',
28160             cls: 'nav-tabs-custom',
28161             cn: [
28162                 h,
28163                 {
28164                     tag: 'div',
28165                     cls: 'tab-content no-padding',
28166                     cn: []
28167                 }
28168             ]
28169         };
28170
28171         return  cfg;
28172     },
28173     initEvents : function()
28174     {
28175         //Roo.log('add add pane handler');
28176         this.on('addpane', this.onAddPane, this);
28177     },
28178      /**
28179      * Updates the box title
28180      * @param {String} html to set the title to.
28181      */
28182     setTitle : function(value)
28183     {
28184         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28185     },
28186     onAddPane : function(pane)
28187     {
28188         this.panes.push(pane);
28189         //Roo.log('addpane');
28190         //Roo.log(pane);
28191         // tabs are rendere left to right..
28192         if(!this.showtabs){
28193             return;
28194         }
28195         
28196         var ctr = this.el.select('.nav-tabs', true).first();
28197          
28198          
28199         var existing = ctr.select('.nav-tab',true);
28200         var qty = existing.getCount();;
28201         
28202         
28203         var tab = ctr.createChild({
28204             tag : 'li',
28205             cls : 'nav-tab' + (qty ? '' : ' active'),
28206             cn : [
28207                 {
28208                     tag : 'a',
28209                     href:'#',
28210                     html : pane.title
28211                 }
28212             ]
28213         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28214         pane.tab = tab;
28215         
28216         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28217         if (!qty) {
28218             pane.el.addClass('active');
28219         }
28220         
28221                 
28222     },
28223     onTabClick : function(ev,un,ob,pane)
28224     {
28225         //Roo.log('tab - prev default');
28226         ev.preventDefault();
28227         
28228         
28229         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28230         pane.tab.addClass('active');
28231         //Roo.log(pane.title);
28232         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28233         // technically we should have a deactivate event.. but maybe add later.
28234         // and it should not de-activate the selected tab...
28235         this.fireEvent('activatepane', pane);
28236         pane.el.addClass('active');
28237         pane.fireEvent('activate');
28238         
28239         
28240     },
28241     
28242     getActivePane : function()
28243     {
28244         var r = false;
28245         Roo.each(this.panes, function(p) {
28246             if(p.el.hasClass('active')){
28247                 r = p;
28248                 return false;
28249             }
28250             
28251             return;
28252         });
28253         
28254         return r;
28255     }
28256     
28257     
28258 });
28259
28260  
28261 /*
28262  * - LGPL
28263  *
28264  * Tab pane
28265  * 
28266  */
28267 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28268 /**
28269  * @class Roo.bootstrap.TabPane
28270  * @extends Roo.bootstrap.Component
28271  * Bootstrap TabPane class
28272  * @cfg {Boolean} active (false | true) Default false
28273  * @cfg {String} title title of panel
28274
28275  * 
28276  * @constructor
28277  * Create a new TabPane
28278  * @param {Object} config The config object
28279  */
28280
28281 Roo.bootstrap.dash.TabPane = function(config){
28282     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28283     
28284     this.addEvents({
28285         // raw events
28286         /**
28287          * @event activate
28288          * When a pane is activated
28289          * @param {Roo.bootstrap.dash.TabPane} pane
28290          */
28291         "activate" : true
28292          
28293     });
28294 };
28295
28296 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28297     
28298     active : false,
28299     title : '',
28300     
28301     // the tabBox that this is attached to.
28302     tab : false,
28303      
28304     getAutoCreate : function() 
28305     {
28306         var cfg = {
28307             tag: 'div',
28308             cls: 'tab-pane'
28309         };
28310         
28311         if(this.active){
28312             cfg.cls += ' active';
28313         }
28314         
28315         return cfg;
28316     },
28317     initEvents  : function()
28318     {
28319         //Roo.log('trigger add pane handler');
28320         this.parent().fireEvent('addpane', this)
28321     },
28322     
28323      /**
28324      * Updates the tab title 
28325      * @param {String} html to set the title to.
28326      */
28327     setTitle: function(str)
28328     {
28329         if (!this.tab) {
28330             return;
28331         }
28332         this.title = str;
28333         this.tab.select('a', true).first().dom.innerHTML = str;
28334         
28335     }
28336     
28337     
28338     
28339 });
28340
28341  
28342
28343
28344  /*
28345  * - LGPL
28346  *
28347  * menu
28348  * 
28349  */
28350 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28351
28352 /**
28353  * @class Roo.bootstrap.menu.Menu
28354  * @extends Roo.bootstrap.Component
28355  * Bootstrap Menu class - container for Menu
28356  * @cfg {String} html Text of the menu
28357  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28358  * @cfg {String} icon Font awesome icon
28359  * @cfg {String} pos Menu align to (top | bottom) default bottom
28360  * 
28361  * 
28362  * @constructor
28363  * Create a new Menu
28364  * @param {Object} config The config object
28365  */
28366
28367
28368 Roo.bootstrap.menu.Menu = function(config){
28369     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28370     
28371     this.addEvents({
28372         /**
28373          * @event beforeshow
28374          * Fires before this menu is displayed
28375          * @param {Roo.bootstrap.menu.Menu} this
28376          */
28377         beforeshow : true,
28378         /**
28379          * @event beforehide
28380          * Fires before this menu is hidden
28381          * @param {Roo.bootstrap.menu.Menu} this
28382          */
28383         beforehide : true,
28384         /**
28385          * @event show
28386          * Fires after this menu is displayed
28387          * @param {Roo.bootstrap.menu.Menu} this
28388          */
28389         show : true,
28390         /**
28391          * @event hide
28392          * Fires after this menu is hidden
28393          * @param {Roo.bootstrap.menu.Menu} this
28394          */
28395         hide : true,
28396         /**
28397          * @event click
28398          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28399          * @param {Roo.bootstrap.menu.Menu} this
28400          * @param {Roo.EventObject} e
28401          */
28402         click : true
28403     });
28404     
28405 };
28406
28407 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28408     
28409     submenu : false,
28410     html : '',
28411     weight : 'default',
28412     icon : false,
28413     pos : 'bottom',
28414     
28415     
28416     getChildContainer : function() {
28417         if(this.isSubMenu){
28418             return this.el;
28419         }
28420         
28421         return this.el.select('ul.dropdown-menu', true).first();  
28422     },
28423     
28424     getAutoCreate : function()
28425     {
28426         var text = [
28427             {
28428                 tag : 'span',
28429                 cls : 'roo-menu-text',
28430                 html : this.html
28431             }
28432         ];
28433         
28434         if(this.icon){
28435             text.unshift({
28436                 tag : 'i',
28437                 cls : 'fa ' + this.icon
28438             })
28439         }
28440         
28441         
28442         var cfg = {
28443             tag : 'div',
28444             cls : 'btn-group',
28445             cn : [
28446                 {
28447                     tag : 'button',
28448                     cls : 'dropdown-button btn btn-' + this.weight,
28449                     cn : text
28450                 },
28451                 {
28452                     tag : 'button',
28453                     cls : 'dropdown-toggle btn btn-' + this.weight,
28454                     cn : [
28455                         {
28456                             tag : 'span',
28457                             cls : 'caret'
28458                         }
28459                     ]
28460                 },
28461                 {
28462                     tag : 'ul',
28463                     cls : 'dropdown-menu'
28464                 }
28465             ]
28466             
28467         };
28468         
28469         if(this.pos == 'top'){
28470             cfg.cls += ' dropup';
28471         }
28472         
28473         if(this.isSubMenu){
28474             cfg = {
28475                 tag : 'ul',
28476                 cls : 'dropdown-menu'
28477             }
28478         }
28479         
28480         return cfg;
28481     },
28482     
28483     onRender : function(ct, position)
28484     {
28485         this.isSubMenu = ct.hasClass('dropdown-submenu');
28486         
28487         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28488     },
28489     
28490     initEvents : function() 
28491     {
28492         if(this.isSubMenu){
28493             return;
28494         }
28495         
28496         this.hidden = true;
28497         
28498         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28499         this.triggerEl.on('click', this.onTriggerPress, this);
28500         
28501         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28502         this.buttonEl.on('click', this.onClick, this);
28503         
28504     },
28505     
28506     list : function()
28507     {
28508         if(this.isSubMenu){
28509             return this.el;
28510         }
28511         
28512         return this.el.select('ul.dropdown-menu', true).first();
28513     },
28514     
28515     onClick : function(e)
28516     {
28517         this.fireEvent("click", this, e);
28518     },
28519     
28520     onTriggerPress  : function(e)
28521     {   
28522         if (this.isVisible()) {
28523             this.hide();
28524         } else {
28525             this.show();
28526         }
28527     },
28528     
28529     isVisible : function(){
28530         return !this.hidden;
28531     },
28532     
28533     show : function()
28534     {
28535         this.fireEvent("beforeshow", this);
28536         
28537         this.hidden = false;
28538         this.el.addClass('open');
28539         
28540         Roo.get(document).on("mouseup", this.onMouseUp, this);
28541         
28542         this.fireEvent("show", this);
28543         
28544         
28545     },
28546     
28547     hide : function()
28548     {
28549         this.fireEvent("beforehide", this);
28550         
28551         this.hidden = true;
28552         this.el.removeClass('open');
28553         
28554         Roo.get(document).un("mouseup", this.onMouseUp);
28555         
28556         this.fireEvent("hide", this);
28557     },
28558     
28559     onMouseUp : function()
28560     {
28561         this.hide();
28562     }
28563     
28564 });
28565
28566  
28567  /*
28568  * - LGPL
28569  *
28570  * menu item
28571  * 
28572  */
28573 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28574
28575 /**
28576  * @class Roo.bootstrap.menu.Item
28577  * @extends Roo.bootstrap.Component
28578  * Bootstrap MenuItem class
28579  * @cfg {Boolean} submenu (true | false) default false
28580  * @cfg {String} html text of the item
28581  * @cfg {String} href the link
28582  * @cfg {Boolean} disable (true | false) default false
28583  * @cfg {Boolean} preventDefault (true | false) default true
28584  * @cfg {String} icon Font awesome icon
28585  * @cfg {String} pos Submenu align to (left | right) default right 
28586  * 
28587  * 
28588  * @constructor
28589  * Create a new Item
28590  * @param {Object} config The config object
28591  */
28592
28593
28594 Roo.bootstrap.menu.Item = function(config){
28595     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28596     this.addEvents({
28597         /**
28598          * @event mouseover
28599          * Fires when the mouse is hovering over this menu
28600          * @param {Roo.bootstrap.menu.Item} this
28601          * @param {Roo.EventObject} e
28602          */
28603         mouseover : true,
28604         /**
28605          * @event mouseout
28606          * Fires when the mouse exits this menu
28607          * @param {Roo.bootstrap.menu.Item} this
28608          * @param {Roo.EventObject} e
28609          */
28610         mouseout : true,
28611         // raw events
28612         /**
28613          * @event click
28614          * The raw click event for the entire grid.
28615          * @param {Roo.EventObject} e
28616          */
28617         click : true
28618     });
28619 };
28620
28621 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28622     
28623     submenu : false,
28624     href : '',
28625     html : '',
28626     preventDefault: true,
28627     disable : false,
28628     icon : false,
28629     pos : 'right',
28630     
28631     getAutoCreate : function()
28632     {
28633         var text = [
28634             {
28635                 tag : 'span',
28636                 cls : 'roo-menu-item-text',
28637                 html : this.html
28638             }
28639         ];
28640         
28641         if(this.icon){
28642             text.unshift({
28643                 tag : 'i',
28644                 cls : 'fa ' + this.icon
28645             })
28646         }
28647         
28648         var cfg = {
28649             tag : 'li',
28650             cn : [
28651                 {
28652                     tag : 'a',
28653                     href : this.href || '#',
28654                     cn : text
28655                 }
28656             ]
28657         };
28658         
28659         if(this.disable){
28660             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28661         }
28662         
28663         if(this.submenu){
28664             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28665             
28666             if(this.pos == 'left'){
28667                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28668             }
28669         }
28670         
28671         return cfg;
28672     },
28673     
28674     initEvents : function() 
28675     {
28676         this.el.on('mouseover', this.onMouseOver, this);
28677         this.el.on('mouseout', this.onMouseOut, this);
28678         
28679         this.el.select('a', true).first().on('click', this.onClick, this);
28680         
28681     },
28682     
28683     onClick : function(e)
28684     {
28685         if(this.preventDefault){
28686             e.preventDefault();
28687         }
28688         
28689         this.fireEvent("click", this, e);
28690     },
28691     
28692     onMouseOver : function(e)
28693     {
28694         if(this.submenu && this.pos == 'left'){
28695             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28696         }
28697         
28698         this.fireEvent("mouseover", this, e);
28699     },
28700     
28701     onMouseOut : function(e)
28702     {
28703         this.fireEvent("mouseout", this, e);
28704     }
28705 });
28706
28707  
28708
28709  /*
28710  * - LGPL
28711  *
28712  * menu separator
28713  * 
28714  */
28715 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28716
28717 /**
28718  * @class Roo.bootstrap.menu.Separator
28719  * @extends Roo.bootstrap.Component
28720  * Bootstrap Separator class
28721  * 
28722  * @constructor
28723  * Create a new Separator
28724  * @param {Object} config The config object
28725  */
28726
28727
28728 Roo.bootstrap.menu.Separator = function(config){
28729     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28730 };
28731
28732 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28733     
28734     getAutoCreate : function(){
28735         var cfg = {
28736             tag : 'li',
28737             cls: 'divider'
28738         };
28739         
28740         return cfg;
28741     }
28742    
28743 });
28744
28745  
28746
28747  /*
28748  * - LGPL
28749  *
28750  * Tooltip
28751  * 
28752  */
28753
28754 /**
28755  * @class Roo.bootstrap.Tooltip
28756  * Bootstrap Tooltip class
28757  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28758  * to determine which dom element triggers the tooltip.
28759  * 
28760  * It needs to add support for additional attributes like tooltip-position
28761  * 
28762  * @constructor
28763  * Create a new Toolti
28764  * @param {Object} config The config object
28765  */
28766
28767 Roo.bootstrap.Tooltip = function(config){
28768     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28769     
28770     this.alignment = Roo.bootstrap.Tooltip.alignment;
28771     
28772     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28773         this.alignment = config.alignment;
28774     }
28775     
28776 };
28777
28778 Roo.apply(Roo.bootstrap.Tooltip, {
28779     /**
28780      * @function init initialize tooltip monitoring.
28781      * @static
28782      */
28783     currentEl : false,
28784     currentTip : false,
28785     currentRegion : false,
28786     
28787     //  init : delay?
28788     
28789     init : function()
28790     {
28791         Roo.get(document).on('mouseover', this.enter ,this);
28792         Roo.get(document).on('mouseout', this.leave, this);
28793          
28794         
28795         this.currentTip = new Roo.bootstrap.Tooltip();
28796     },
28797     
28798     enter : function(ev)
28799     {
28800         var dom = ev.getTarget();
28801         
28802         //Roo.log(['enter',dom]);
28803         var el = Roo.fly(dom);
28804         if (this.currentEl) {
28805             //Roo.log(dom);
28806             //Roo.log(this.currentEl);
28807             //Roo.log(this.currentEl.contains(dom));
28808             if (this.currentEl == el) {
28809                 return;
28810             }
28811             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28812                 return;
28813             }
28814
28815         }
28816         
28817         if (this.currentTip.el) {
28818             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28819         }    
28820         //Roo.log(ev);
28821         
28822         if(!el || el.dom == document){
28823             return;
28824         }
28825         
28826         var bindEl = el;
28827         
28828         // you can not look for children, as if el is the body.. then everythign is the child..
28829         if (!el.attr('tooltip')) { //
28830             if (!el.select("[tooltip]").elements.length) {
28831                 return;
28832             }
28833             // is the mouse over this child...?
28834             bindEl = el.select("[tooltip]").first();
28835             var xy = ev.getXY();
28836             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28837                 //Roo.log("not in region.");
28838                 return;
28839             }
28840             //Roo.log("child element over..");
28841             
28842         }
28843         this.currentEl = bindEl;
28844         this.currentTip.bind(bindEl);
28845         this.currentRegion = Roo.lib.Region.getRegion(dom);
28846         this.currentTip.enter();
28847         
28848     },
28849     leave : function(ev)
28850     {
28851         var dom = ev.getTarget();
28852         //Roo.log(['leave',dom]);
28853         if (!this.currentEl) {
28854             return;
28855         }
28856         
28857         
28858         if (dom != this.currentEl.dom) {
28859             return;
28860         }
28861         var xy = ev.getXY();
28862         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28863             return;
28864         }
28865         // only activate leave if mouse cursor is outside... bounding box..
28866         
28867         
28868         
28869         
28870         if (this.currentTip) {
28871             this.currentTip.leave();
28872         }
28873         //Roo.log('clear currentEl');
28874         this.currentEl = false;
28875         
28876         
28877     },
28878     alignment : {
28879         'left' : ['r-l', [-2,0], 'right'],
28880         'right' : ['l-r', [2,0], 'left'],
28881         'bottom' : ['t-b', [0,2], 'top'],
28882         'top' : [ 'b-t', [0,-2], 'bottom']
28883     }
28884     
28885 });
28886
28887
28888 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28889     
28890     
28891     bindEl : false,
28892     
28893     delay : null, // can be { show : 300 , hide: 500}
28894     
28895     timeout : null,
28896     
28897     hoverState : null, //???
28898     
28899     placement : 'bottom', 
28900     
28901     alignment : false,
28902     
28903     getAutoCreate : function(){
28904     
28905         var cfg = {
28906            cls : 'tooltip',   
28907            role : 'tooltip',
28908            cn : [
28909                 {
28910                     cls : 'tooltip-arrow arrow'
28911                 },
28912                 {
28913                     cls : 'tooltip-inner'
28914                 }
28915            ]
28916         };
28917         
28918         return cfg;
28919     },
28920     bind : function(el)
28921     {
28922         this.bindEl = el;
28923     },
28924     
28925     initEvents : function()
28926     {
28927         this.arrowEl = this.el.select('.arrow', true).first();
28928         this.innerEl = this.el.select('.tooltip-inner', true).first();
28929     },
28930     
28931     enter : function () {
28932        
28933         if (this.timeout != null) {
28934             clearTimeout(this.timeout);
28935         }
28936         
28937         this.hoverState = 'in';
28938          //Roo.log("enter - show");
28939         if (!this.delay || !this.delay.show) {
28940             this.show();
28941             return;
28942         }
28943         var _t = this;
28944         this.timeout = setTimeout(function () {
28945             if (_t.hoverState == 'in') {
28946                 _t.show();
28947             }
28948         }, this.delay.show);
28949     },
28950     leave : function()
28951     {
28952         clearTimeout(this.timeout);
28953     
28954         this.hoverState = 'out';
28955          if (!this.delay || !this.delay.hide) {
28956             this.hide();
28957             return;
28958         }
28959        
28960         var _t = this;
28961         this.timeout = setTimeout(function () {
28962             //Roo.log("leave - timeout");
28963             
28964             if (_t.hoverState == 'out') {
28965                 _t.hide();
28966                 Roo.bootstrap.Tooltip.currentEl = false;
28967             }
28968         }, delay);
28969     },
28970     
28971     show : function (msg)
28972     {
28973         if (!this.el) {
28974             this.render(document.body);
28975         }
28976         // set content.
28977         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28978         
28979         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28980         
28981         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28982         
28983         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28984                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28985         
28986         var placement = typeof this.placement == 'function' ?
28987             this.placement.call(this, this.el, on_el) :
28988             this.placement;
28989             
28990         var autoToken = /\s?auto?\s?/i;
28991         var autoPlace = autoToken.test(placement);
28992         if (autoPlace) {
28993             placement = placement.replace(autoToken, '') || 'top';
28994         }
28995         
28996         //this.el.detach()
28997         //this.el.setXY([0,0]);
28998         this.el.show();
28999         //this.el.dom.style.display='block';
29000         
29001         //this.el.appendTo(on_el);
29002         
29003         var p = this.getPosition();
29004         var box = this.el.getBox();
29005         
29006         if (autoPlace) {
29007             // fixme..
29008         }
29009         
29010         var align = this.alignment[placement];
29011         
29012         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29013         
29014         if(placement == 'top' || placement == 'bottom'){
29015             if(xy[0] < 0){
29016                 placement = 'right';
29017             }
29018             
29019             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29020                 placement = 'left';
29021             }
29022             
29023             var scroll = Roo.select('body', true).first().getScroll();
29024             
29025             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29026                 placement = 'top';
29027             }
29028             
29029             align = this.alignment[placement];
29030             
29031             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29032             
29033         }
29034         
29035         this.el.alignTo(this.bindEl, align[0],align[1]);
29036         //var arrow = this.el.select('.arrow',true).first();
29037         //arrow.set(align[2], 
29038         
29039         this.el.addClass(placement);
29040         this.el.addClass("bs-tooltip-"+ placement);
29041         
29042         this.el.addClass('in fade show');
29043         
29044         this.hoverState = null;
29045         
29046         if (this.el.hasClass('fade')) {
29047             // fade it?
29048         }
29049         
29050         
29051         
29052         
29053         
29054     },
29055     hide : function()
29056     {
29057          
29058         if (!this.el) {
29059             return;
29060         }
29061         //this.el.setXY([0,0]);
29062         this.el.removeClass(['show', 'in']);
29063         //this.el.hide();
29064         
29065     }
29066     
29067 });
29068  
29069
29070  /*
29071  * - LGPL
29072  *
29073  * Location Picker
29074  * 
29075  */
29076
29077 /**
29078  * @class Roo.bootstrap.LocationPicker
29079  * @extends Roo.bootstrap.Component
29080  * Bootstrap LocationPicker class
29081  * @cfg {Number} latitude Position when init default 0
29082  * @cfg {Number} longitude Position when init default 0
29083  * @cfg {Number} zoom default 15
29084  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29085  * @cfg {Boolean} mapTypeControl default false
29086  * @cfg {Boolean} disableDoubleClickZoom default false
29087  * @cfg {Boolean} scrollwheel default true
29088  * @cfg {Boolean} streetViewControl default false
29089  * @cfg {Number} radius default 0
29090  * @cfg {String} locationName
29091  * @cfg {Boolean} draggable default true
29092  * @cfg {Boolean} enableAutocomplete default false
29093  * @cfg {Boolean} enableReverseGeocode default true
29094  * @cfg {String} markerTitle
29095  * 
29096  * @constructor
29097  * Create a new LocationPicker
29098  * @param {Object} config The config object
29099  */
29100
29101
29102 Roo.bootstrap.LocationPicker = function(config){
29103     
29104     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29105     
29106     this.addEvents({
29107         /**
29108          * @event initial
29109          * Fires when the picker initialized.
29110          * @param {Roo.bootstrap.LocationPicker} this
29111          * @param {Google Location} location
29112          */
29113         initial : true,
29114         /**
29115          * @event positionchanged
29116          * Fires when the picker position changed.
29117          * @param {Roo.bootstrap.LocationPicker} this
29118          * @param {Google Location} location
29119          */
29120         positionchanged : true,
29121         /**
29122          * @event resize
29123          * Fires when the map resize.
29124          * @param {Roo.bootstrap.LocationPicker} this
29125          */
29126         resize : true,
29127         /**
29128          * @event show
29129          * Fires when the map show.
29130          * @param {Roo.bootstrap.LocationPicker} this
29131          */
29132         show : true,
29133         /**
29134          * @event hide
29135          * Fires when the map hide.
29136          * @param {Roo.bootstrap.LocationPicker} this
29137          */
29138         hide : true,
29139         /**
29140          * @event mapClick
29141          * Fires when click the map.
29142          * @param {Roo.bootstrap.LocationPicker} this
29143          * @param {Map event} e
29144          */
29145         mapClick : true,
29146         /**
29147          * @event mapRightClick
29148          * Fires when right click the map.
29149          * @param {Roo.bootstrap.LocationPicker} this
29150          * @param {Map event} e
29151          */
29152         mapRightClick : true,
29153         /**
29154          * @event markerClick
29155          * Fires when click the marker.
29156          * @param {Roo.bootstrap.LocationPicker} this
29157          * @param {Map event} e
29158          */
29159         markerClick : true,
29160         /**
29161          * @event markerRightClick
29162          * Fires when right click the marker.
29163          * @param {Roo.bootstrap.LocationPicker} this
29164          * @param {Map event} e
29165          */
29166         markerRightClick : true,
29167         /**
29168          * @event OverlayViewDraw
29169          * Fires when OverlayView Draw
29170          * @param {Roo.bootstrap.LocationPicker} this
29171          */
29172         OverlayViewDraw : true,
29173         /**
29174          * @event OverlayViewOnAdd
29175          * Fires when OverlayView Draw
29176          * @param {Roo.bootstrap.LocationPicker} this
29177          */
29178         OverlayViewOnAdd : true,
29179         /**
29180          * @event OverlayViewOnRemove
29181          * Fires when OverlayView Draw
29182          * @param {Roo.bootstrap.LocationPicker} this
29183          */
29184         OverlayViewOnRemove : true,
29185         /**
29186          * @event OverlayViewShow
29187          * Fires when OverlayView Draw
29188          * @param {Roo.bootstrap.LocationPicker} this
29189          * @param {Pixel} cpx
29190          */
29191         OverlayViewShow : true,
29192         /**
29193          * @event OverlayViewHide
29194          * Fires when OverlayView Draw
29195          * @param {Roo.bootstrap.LocationPicker} this
29196          */
29197         OverlayViewHide : true,
29198         /**
29199          * @event loadexception
29200          * Fires when load google lib failed.
29201          * @param {Roo.bootstrap.LocationPicker} this
29202          */
29203         loadexception : true
29204     });
29205         
29206 };
29207
29208 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29209     
29210     gMapContext: false,
29211     
29212     latitude: 0,
29213     longitude: 0,
29214     zoom: 15,
29215     mapTypeId: false,
29216     mapTypeControl: false,
29217     disableDoubleClickZoom: false,
29218     scrollwheel: true,
29219     streetViewControl: false,
29220     radius: 0,
29221     locationName: '',
29222     draggable: true,
29223     enableAutocomplete: false,
29224     enableReverseGeocode: true,
29225     markerTitle: '',
29226     
29227     getAutoCreate: function()
29228     {
29229
29230         var cfg = {
29231             tag: 'div',
29232             cls: 'roo-location-picker'
29233         };
29234         
29235         return cfg
29236     },
29237     
29238     initEvents: function(ct, position)
29239     {       
29240         if(!this.el.getWidth() || this.isApplied()){
29241             return;
29242         }
29243         
29244         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29245         
29246         this.initial();
29247     },
29248     
29249     initial: function()
29250     {
29251         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29252             this.fireEvent('loadexception', this);
29253             return;
29254         }
29255         
29256         if(!this.mapTypeId){
29257             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29258         }
29259         
29260         this.gMapContext = this.GMapContext();
29261         
29262         this.initOverlayView();
29263         
29264         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29265         
29266         var _this = this;
29267                 
29268         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29269             _this.setPosition(_this.gMapContext.marker.position);
29270         });
29271         
29272         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29273             _this.fireEvent('mapClick', this, event);
29274             
29275         });
29276
29277         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29278             _this.fireEvent('mapRightClick', this, event);
29279             
29280         });
29281         
29282         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29283             _this.fireEvent('markerClick', this, event);
29284             
29285         });
29286
29287         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29288             _this.fireEvent('markerRightClick', this, event);
29289             
29290         });
29291         
29292         this.setPosition(this.gMapContext.location);
29293         
29294         this.fireEvent('initial', this, this.gMapContext.location);
29295     },
29296     
29297     initOverlayView: function()
29298     {
29299         var _this = this;
29300         
29301         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29302             
29303             draw: function()
29304             {
29305                 _this.fireEvent('OverlayViewDraw', _this);
29306             },
29307             
29308             onAdd: function()
29309             {
29310                 _this.fireEvent('OverlayViewOnAdd', _this);
29311             },
29312             
29313             onRemove: function()
29314             {
29315                 _this.fireEvent('OverlayViewOnRemove', _this);
29316             },
29317             
29318             show: function(cpx)
29319             {
29320                 _this.fireEvent('OverlayViewShow', _this, cpx);
29321             },
29322             
29323             hide: function()
29324             {
29325                 _this.fireEvent('OverlayViewHide', _this);
29326             }
29327             
29328         });
29329     },
29330     
29331     fromLatLngToContainerPixel: function(event)
29332     {
29333         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29334     },
29335     
29336     isApplied: function() 
29337     {
29338         return this.getGmapContext() == false ? false : true;
29339     },
29340     
29341     getGmapContext: function() 
29342     {
29343         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29344     },
29345     
29346     GMapContext: function() 
29347     {
29348         var position = new google.maps.LatLng(this.latitude, this.longitude);
29349         
29350         var _map = new google.maps.Map(this.el.dom, {
29351             center: position,
29352             zoom: this.zoom,
29353             mapTypeId: this.mapTypeId,
29354             mapTypeControl: this.mapTypeControl,
29355             disableDoubleClickZoom: this.disableDoubleClickZoom,
29356             scrollwheel: this.scrollwheel,
29357             streetViewControl: this.streetViewControl,
29358             locationName: this.locationName,
29359             draggable: this.draggable,
29360             enableAutocomplete: this.enableAutocomplete,
29361             enableReverseGeocode: this.enableReverseGeocode
29362         });
29363         
29364         var _marker = new google.maps.Marker({
29365             position: position,
29366             map: _map,
29367             title: this.markerTitle,
29368             draggable: this.draggable
29369         });
29370         
29371         return {
29372             map: _map,
29373             marker: _marker,
29374             circle: null,
29375             location: position,
29376             radius: this.radius,
29377             locationName: this.locationName,
29378             addressComponents: {
29379                 formatted_address: null,
29380                 addressLine1: null,
29381                 addressLine2: null,
29382                 streetName: null,
29383                 streetNumber: null,
29384                 city: null,
29385                 district: null,
29386                 state: null,
29387                 stateOrProvince: null
29388             },
29389             settings: this,
29390             domContainer: this.el.dom,
29391             geodecoder: new google.maps.Geocoder()
29392         };
29393     },
29394     
29395     drawCircle: function(center, radius, options) 
29396     {
29397         if (this.gMapContext.circle != null) {
29398             this.gMapContext.circle.setMap(null);
29399         }
29400         if (radius > 0) {
29401             radius *= 1;
29402             options = Roo.apply({}, options, {
29403                 strokeColor: "#0000FF",
29404                 strokeOpacity: .35,
29405                 strokeWeight: 2,
29406                 fillColor: "#0000FF",
29407                 fillOpacity: .2
29408             });
29409             
29410             options.map = this.gMapContext.map;
29411             options.radius = radius;
29412             options.center = center;
29413             this.gMapContext.circle = new google.maps.Circle(options);
29414             return this.gMapContext.circle;
29415         }
29416         
29417         return null;
29418     },
29419     
29420     setPosition: function(location) 
29421     {
29422         this.gMapContext.location = location;
29423         this.gMapContext.marker.setPosition(location);
29424         this.gMapContext.map.panTo(location);
29425         this.drawCircle(location, this.gMapContext.radius, {});
29426         
29427         var _this = this;
29428         
29429         if (this.gMapContext.settings.enableReverseGeocode) {
29430             this.gMapContext.geodecoder.geocode({
29431                 latLng: this.gMapContext.location
29432             }, function(results, status) {
29433                 
29434                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29435                     _this.gMapContext.locationName = results[0].formatted_address;
29436                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29437                     
29438                     _this.fireEvent('positionchanged', this, location);
29439                 }
29440             });
29441             
29442             return;
29443         }
29444         
29445         this.fireEvent('positionchanged', this, location);
29446     },
29447     
29448     resize: function()
29449     {
29450         google.maps.event.trigger(this.gMapContext.map, "resize");
29451         
29452         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29453         
29454         this.fireEvent('resize', this);
29455     },
29456     
29457     setPositionByLatLng: function(latitude, longitude)
29458     {
29459         this.setPosition(new google.maps.LatLng(latitude, longitude));
29460     },
29461     
29462     getCurrentPosition: function() 
29463     {
29464         return {
29465             latitude: this.gMapContext.location.lat(),
29466             longitude: this.gMapContext.location.lng()
29467         };
29468     },
29469     
29470     getAddressName: function() 
29471     {
29472         return this.gMapContext.locationName;
29473     },
29474     
29475     getAddressComponents: function() 
29476     {
29477         return this.gMapContext.addressComponents;
29478     },
29479     
29480     address_component_from_google_geocode: function(address_components) 
29481     {
29482         var result = {};
29483         
29484         for (var i = 0; i < address_components.length; i++) {
29485             var component = address_components[i];
29486             if (component.types.indexOf("postal_code") >= 0) {
29487                 result.postalCode = component.short_name;
29488             } else if (component.types.indexOf("street_number") >= 0) {
29489                 result.streetNumber = component.short_name;
29490             } else if (component.types.indexOf("route") >= 0) {
29491                 result.streetName = component.short_name;
29492             } else if (component.types.indexOf("neighborhood") >= 0) {
29493                 result.city = component.short_name;
29494             } else if (component.types.indexOf("locality") >= 0) {
29495                 result.city = component.short_name;
29496             } else if (component.types.indexOf("sublocality") >= 0) {
29497                 result.district = component.short_name;
29498             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29499                 result.stateOrProvince = component.short_name;
29500             } else if (component.types.indexOf("country") >= 0) {
29501                 result.country = component.short_name;
29502             }
29503         }
29504         
29505         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29506         result.addressLine2 = "";
29507         return result;
29508     },
29509     
29510     setZoomLevel: function(zoom)
29511     {
29512         this.gMapContext.map.setZoom(zoom);
29513     },
29514     
29515     show: function()
29516     {
29517         if(!this.el){
29518             return;
29519         }
29520         
29521         this.el.show();
29522         
29523         this.resize();
29524         
29525         this.fireEvent('show', this);
29526     },
29527     
29528     hide: function()
29529     {
29530         if(!this.el){
29531             return;
29532         }
29533         
29534         this.el.hide();
29535         
29536         this.fireEvent('hide', this);
29537     }
29538     
29539 });
29540
29541 Roo.apply(Roo.bootstrap.LocationPicker, {
29542     
29543     OverlayView : function(map, options)
29544     {
29545         options = options || {};
29546         
29547         this.setMap(map);
29548     }
29549     
29550     
29551 });/**
29552  * @class Roo.bootstrap.Alert
29553  * @extends Roo.bootstrap.Component
29554  * Bootstrap Alert class - shows an alert area box
29555  * eg
29556  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29557   Enter a valid email address
29558 </div>
29559  * @licence LGPL
29560  * @cfg {String} title The title of alert
29561  * @cfg {String} html The content of alert
29562  * @cfg {String} weight (  success | info | warning | danger )
29563  * @cfg {String} faicon font-awesomeicon
29564  * 
29565  * @constructor
29566  * Create a new alert
29567  * @param {Object} config The config object
29568  */
29569
29570
29571 Roo.bootstrap.Alert = function(config){
29572     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29573     
29574 };
29575
29576 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29577     
29578     title: '',
29579     html: '',
29580     weight: false,
29581     faicon: false,
29582     
29583     getAutoCreate : function()
29584     {
29585         
29586         var cfg = {
29587             tag : 'div',
29588             cls : 'alert',
29589             cn : [
29590                 {
29591                     tag : 'i',
29592                     cls : 'roo-alert-icon'
29593                     
29594                 },
29595                 {
29596                     tag : 'b',
29597                     cls : 'roo-alert-title',
29598                     html : this.title
29599                 },
29600                 {
29601                     tag : 'span',
29602                     cls : 'roo-alert-text',
29603                     html : this.html
29604                 }
29605             ]
29606         };
29607         
29608         if(this.faicon){
29609             cfg.cn[0].cls += ' fa ' + this.faicon;
29610         }
29611         
29612         if(this.weight){
29613             cfg.cls += ' alert-' + this.weight;
29614         }
29615         
29616         return cfg;
29617     },
29618     
29619     initEvents: function() 
29620     {
29621         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29622     },
29623     
29624     setTitle : function(str)
29625     {
29626         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29627     },
29628     
29629     setText : function(str)
29630     {
29631         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29632     },
29633     
29634     setWeight : function(weight)
29635     {
29636         if(this.weight){
29637             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29638         }
29639         
29640         this.weight = weight;
29641         
29642         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29643     },
29644     
29645     setIcon : function(icon)
29646     {
29647         if(this.faicon){
29648             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29649         }
29650         
29651         this.faicon = icon;
29652         
29653         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29654     },
29655     
29656     hide: function() 
29657     {
29658         this.el.hide();   
29659     },
29660     
29661     show: function() 
29662     {  
29663         this.el.show();   
29664     }
29665     
29666 });
29667
29668  
29669 /*
29670 * Licence: LGPL
29671 */
29672
29673 /**
29674  * @class Roo.bootstrap.UploadCropbox
29675  * @extends Roo.bootstrap.Component
29676  * Bootstrap UploadCropbox class
29677  * @cfg {String} emptyText show when image has been loaded
29678  * @cfg {String} rotateNotify show when image too small to rotate
29679  * @cfg {Number} errorTimeout default 3000
29680  * @cfg {Number} minWidth default 300
29681  * @cfg {Number} minHeight default 300
29682  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29683  * @cfg {Boolean} isDocument (true|false) default false
29684  * @cfg {String} url action url
29685  * @cfg {String} paramName default 'imageUpload'
29686  * @cfg {String} method default POST
29687  * @cfg {Boolean} loadMask (true|false) default true
29688  * @cfg {Boolean} loadingText default 'Loading...'
29689  * 
29690  * @constructor
29691  * Create a new UploadCropbox
29692  * @param {Object} config The config object
29693  */
29694
29695 Roo.bootstrap.UploadCropbox = function(config){
29696     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29697     
29698     this.addEvents({
29699         /**
29700          * @event beforeselectfile
29701          * Fire before select file
29702          * @param {Roo.bootstrap.UploadCropbox} this
29703          */
29704         "beforeselectfile" : true,
29705         /**
29706          * @event initial
29707          * Fire after initEvent
29708          * @param {Roo.bootstrap.UploadCropbox} this
29709          */
29710         "initial" : true,
29711         /**
29712          * @event crop
29713          * Fire after initEvent
29714          * @param {Roo.bootstrap.UploadCropbox} this
29715          * @param {String} data
29716          */
29717         "crop" : true,
29718         /**
29719          * @event prepare
29720          * Fire when preparing the file data
29721          * @param {Roo.bootstrap.UploadCropbox} this
29722          * @param {Object} file
29723          */
29724         "prepare" : true,
29725         /**
29726          * @event exception
29727          * Fire when get exception
29728          * @param {Roo.bootstrap.UploadCropbox} this
29729          * @param {XMLHttpRequest} xhr
29730          */
29731         "exception" : true,
29732         /**
29733          * @event beforeloadcanvas
29734          * Fire before load the canvas
29735          * @param {Roo.bootstrap.UploadCropbox} this
29736          * @param {String} src
29737          */
29738         "beforeloadcanvas" : true,
29739         /**
29740          * @event trash
29741          * Fire when trash image
29742          * @param {Roo.bootstrap.UploadCropbox} this
29743          */
29744         "trash" : true,
29745         /**
29746          * @event download
29747          * Fire when download the image
29748          * @param {Roo.bootstrap.UploadCropbox} this
29749          */
29750         "download" : true,
29751         /**
29752          * @event footerbuttonclick
29753          * Fire when footerbuttonclick
29754          * @param {Roo.bootstrap.UploadCropbox} this
29755          * @param {String} type
29756          */
29757         "footerbuttonclick" : true,
29758         /**
29759          * @event resize
29760          * Fire when resize
29761          * @param {Roo.bootstrap.UploadCropbox} this
29762          */
29763         "resize" : true,
29764         /**
29765          * @event rotate
29766          * Fire when rotate the image
29767          * @param {Roo.bootstrap.UploadCropbox} this
29768          * @param {String} pos
29769          */
29770         "rotate" : true,
29771         /**
29772          * @event inspect
29773          * Fire when inspect the file
29774          * @param {Roo.bootstrap.UploadCropbox} this
29775          * @param {Object} file
29776          */
29777         "inspect" : true,
29778         /**
29779          * @event upload
29780          * Fire when xhr upload the file
29781          * @param {Roo.bootstrap.UploadCropbox} this
29782          * @param {Object} data
29783          */
29784         "upload" : true,
29785         /**
29786          * @event arrange
29787          * Fire when arrange the file data
29788          * @param {Roo.bootstrap.UploadCropbox} this
29789          * @param {Object} formData
29790          */
29791         "arrange" : true
29792     });
29793     
29794     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29795 };
29796
29797 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29798     
29799     emptyText : 'Click to upload image',
29800     rotateNotify : 'Image is too small to rotate',
29801     errorTimeout : 3000,
29802     scale : 0,
29803     baseScale : 1,
29804     rotate : 0,
29805     dragable : false,
29806     pinching : false,
29807     mouseX : 0,
29808     mouseY : 0,
29809     cropData : false,
29810     minWidth : 300,
29811     minHeight : 300,
29812     file : false,
29813     exif : {},
29814     baseRotate : 1,
29815     cropType : 'image/jpeg',
29816     buttons : false,
29817     canvasLoaded : false,
29818     isDocument : false,
29819     method : 'POST',
29820     paramName : 'imageUpload',
29821     loadMask : true,
29822     loadingText : 'Loading...',
29823     maskEl : false,
29824     
29825     getAutoCreate : function()
29826     {
29827         var cfg = {
29828             tag : 'div',
29829             cls : 'roo-upload-cropbox',
29830             cn : [
29831                 {
29832                     tag : 'input',
29833                     cls : 'roo-upload-cropbox-selector',
29834                     type : 'file'
29835                 },
29836                 {
29837                     tag : 'div',
29838                     cls : 'roo-upload-cropbox-body',
29839                     style : 'cursor:pointer',
29840                     cn : [
29841                         {
29842                             tag : 'div',
29843                             cls : 'roo-upload-cropbox-preview'
29844                         },
29845                         {
29846                             tag : 'div',
29847                             cls : 'roo-upload-cropbox-thumb'
29848                         },
29849                         {
29850                             tag : 'div',
29851                             cls : 'roo-upload-cropbox-empty-notify',
29852                             html : this.emptyText
29853                         },
29854                         {
29855                             tag : 'div',
29856                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29857                             html : this.rotateNotify
29858                         }
29859                     ]
29860                 },
29861                 {
29862                     tag : 'div',
29863                     cls : 'roo-upload-cropbox-footer',
29864                     cn : {
29865                         tag : 'div',
29866                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29867                         cn : []
29868                     }
29869                 }
29870             ]
29871         };
29872         
29873         return cfg;
29874     },
29875     
29876     onRender : function(ct, position)
29877     {
29878         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29879         
29880         if (this.buttons.length) {
29881             
29882             Roo.each(this.buttons, function(bb) {
29883                 
29884                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29885                 
29886                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29887                 
29888             }, this);
29889         }
29890         
29891         if(this.loadMask){
29892             this.maskEl = this.el;
29893         }
29894     },
29895     
29896     initEvents : function()
29897     {
29898         this.urlAPI = (window.createObjectURL && window) || 
29899                                 (window.URL && URL.revokeObjectURL && URL) || 
29900                                 (window.webkitURL && webkitURL);
29901                         
29902         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29903         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29904         
29905         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29906         this.selectorEl.hide();
29907         
29908         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29909         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29910         
29911         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29912         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29913         this.thumbEl.hide();
29914         
29915         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29916         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29917         
29918         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29919         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29920         this.errorEl.hide();
29921         
29922         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29923         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29924         this.footerEl.hide();
29925         
29926         this.setThumbBoxSize();
29927         
29928         this.bind();
29929         
29930         this.resize();
29931         
29932         this.fireEvent('initial', this);
29933     },
29934
29935     bind : function()
29936     {
29937         var _this = this;
29938         
29939         window.addEventListener("resize", function() { _this.resize(); } );
29940         
29941         this.bodyEl.on('click', this.beforeSelectFile, this);
29942         
29943         if(Roo.isTouch){
29944             this.bodyEl.on('touchstart', this.onTouchStart, this);
29945             this.bodyEl.on('touchmove', this.onTouchMove, this);
29946             this.bodyEl.on('touchend', this.onTouchEnd, this);
29947         }
29948         
29949         if(!Roo.isTouch){
29950             this.bodyEl.on('mousedown', this.onMouseDown, this);
29951             this.bodyEl.on('mousemove', this.onMouseMove, this);
29952             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29953             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29954             Roo.get(document).on('mouseup', this.onMouseUp, this);
29955         }
29956         
29957         this.selectorEl.on('change', this.onFileSelected, this);
29958     },
29959     
29960     reset : function()
29961     {    
29962         this.scale = 0;
29963         this.baseScale = 1;
29964         this.rotate = 0;
29965         this.baseRotate = 1;
29966         this.dragable = false;
29967         this.pinching = false;
29968         this.mouseX = 0;
29969         this.mouseY = 0;
29970         this.cropData = false;
29971         this.notifyEl.dom.innerHTML = this.emptyText;
29972         
29973         this.selectorEl.dom.value = '';
29974         
29975     },
29976     
29977     resize : function()
29978     {
29979         if(this.fireEvent('resize', this) != false){
29980             this.setThumbBoxPosition();
29981             this.setCanvasPosition();
29982         }
29983     },
29984     
29985     onFooterButtonClick : function(e, el, o, type)
29986     {
29987         switch (type) {
29988             case 'rotate-left' :
29989                 this.onRotateLeft(e);
29990                 break;
29991             case 'rotate-right' :
29992                 this.onRotateRight(e);
29993                 break;
29994             case 'picture' :
29995                 this.beforeSelectFile(e);
29996                 break;
29997             case 'trash' :
29998                 this.trash(e);
29999                 break;
30000             case 'crop' :
30001                 this.crop(e);
30002                 break;
30003             case 'download' :
30004                 this.download(e);
30005                 break;
30006             default :
30007                 break;
30008         }
30009         
30010         this.fireEvent('footerbuttonclick', this, type);
30011     },
30012     
30013     beforeSelectFile : function(e)
30014     {
30015         e.preventDefault();
30016         
30017         if(this.fireEvent('beforeselectfile', this) != false){
30018             this.selectorEl.dom.click();
30019         }
30020     },
30021     
30022     onFileSelected : function(e)
30023     {
30024         e.preventDefault();
30025         
30026         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30027             return;
30028         }
30029         
30030         var file = this.selectorEl.dom.files[0];
30031         
30032         if(this.fireEvent('inspect', this, file) != false){
30033             this.prepare(file);
30034         }
30035         
30036     },
30037     
30038     trash : function(e)
30039     {
30040         this.fireEvent('trash', this);
30041     },
30042     
30043     download : function(e)
30044     {
30045         this.fireEvent('download', this);
30046     },
30047     
30048     loadCanvas : function(src)
30049     {   
30050         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30051             
30052             this.reset();
30053             
30054             this.imageEl = document.createElement('img');
30055             
30056             var _this = this;
30057             
30058             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30059             
30060             this.imageEl.src = src;
30061         }
30062     },
30063     
30064     onLoadCanvas : function()
30065     {   
30066         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30067         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30068         
30069         this.bodyEl.un('click', this.beforeSelectFile, this);
30070         
30071         this.notifyEl.hide();
30072         this.thumbEl.show();
30073         this.footerEl.show();
30074         
30075         this.baseRotateLevel();
30076         
30077         if(this.isDocument){
30078             this.setThumbBoxSize();
30079         }
30080         
30081         this.setThumbBoxPosition();
30082         
30083         this.baseScaleLevel();
30084         
30085         this.draw();
30086         
30087         this.resize();
30088         
30089         this.canvasLoaded = true;
30090         
30091         if(this.loadMask){
30092             this.maskEl.unmask();
30093         }
30094         
30095     },
30096     
30097     setCanvasPosition : function()
30098     {   
30099         if(!this.canvasEl){
30100             return;
30101         }
30102         
30103         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30104         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30105         
30106         this.previewEl.setLeft(pw);
30107         this.previewEl.setTop(ph);
30108         
30109     },
30110     
30111     onMouseDown : function(e)
30112     {   
30113         e.stopEvent();
30114         
30115         this.dragable = true;
30116         this.pinching = false;
30117         
30118         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30119             this.dragable = false;
30120             return;
30121         }
30122         
30123         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30124         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30125         
30126     },
30127     
30128     onMouseMove : function(e)
30129     {   
30130         e.stopEvent();
30131         
30132         if(!this.canvasLoaded){
30133             return;
30134         }
30135         
30136         if (!this.dragable){
30137             return;
30138         }
30139         
30140         var minX = Math.ceil(this.thumbEl.getLeft(true));
30141         var minY = Math.ceil(this.thumbEl.getTop(true));
30142         
30143         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30144         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30145         
30146         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30147         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30148         
30149         x = x - this.mouseX;
30150         y = y - this.mouseY;
30151         
30152         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30153         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30154         
30155         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30156         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30157         
30158         this.previewEl.setLeft(bgX);
30159         this.previewEl.setTop(bgY);
30160         
30161         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30162         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30163     },
30164     
30165     onMouseUp : function(e)
30166     {   
30167         e.stopEvent();
30168         
30169         this.dragable = false;
30170     },
30171     
30172     onMouseWheel : function(e)
30173     {   
30174         e.stopEvent();
30175         
30176         this.startScale = this.scale;
30177         
30178         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30179         
30180         if(!this.zoomable()){
30181             this.scale = this.startScale;
30182             return;
30183         }
30184         
30185         this.draw();
30186         
30187         return;
30188     },
30189     
30190     zoomable : function()
30191     {
30192         var minScale = this.thumbEl.getWidth() / this.minWidth;
30193         
30194         if(this.minWidth < this.minHeight){
30195             minScale = this.thumbEl.getHeight() / this.minHeight;
30196         }
30197         
30198         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30199         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30200         
30201         if(
30202                 this.isDocument &&
30203                 (this.rotate == 0 || this.rotate == 180) && 
30204                 (
30205                     width > this.imageEl.OriginWidth || 
30206                     height > this.imageEl.OriginHeight ||
30207                     (width < this.minWidth && height < this.minHeight)
30208                 )
30209         ){
30210             return false;
30211         }
30212         
30213         if(
30214                 this.isDocument &&
30215                 (this.rotate == 90 || this.rotate == 270) && 
30216                 (
30217                     width > this.imageEl.OriginWidth || 
30218                     height > this.imageEl.OriginHeight ||
30219                     (width < this.minHeight && height < this.minWidth)
30220                 )
30221         ){
30222             return false;
30223         }
30224         
30225         if(
30226                 !this.isDocument &&
30227                 (this.rotate == 0 || this.rotate == 180) && 
30228                 (
30229                     width < this.minWidth || 
30230                     width > this.imageEl.OriginWidth || 
30231                     height < this.minHeight || 
30232                     height > this.imageEl.OriginHeight
30233                 )
30234         ){
30235             return false;
30236         }
30237         
30238         if(
30239                 !this.isDocument &&
30240                 (this.rotate == 90 || this.rotate == 270) && 
30241                 (
30242                     width < this.minHeight || 
30243                     width > this.imageEl.OriginWidth || 
30244                     height < this.minWidth || 
30245                     height > this.imageEl.OriginHeight
30246                 )
30247         ){
30248             return false;
30249         }
30250         
30251         return true;
30252         
30253     },
30254     
30255     onRotateLeft : function(e)
30256     {   
30257         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30258             
30259             var minScale = this.thumbEl.getWidth() / this.minWidth;
30260             
30261             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30262             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30263             
30264             this.startScale = this.scale;
30265             
30266             while (this.getScaleLevel() < minScale){
30267             
30268                 this.scale = this.scale + 1;
30269                 
30270                 if(!this.zoomable()){
30271                     break;
30272                 }
30273                 
30274                 if(
30275                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30276                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30277                 ){
30278                     continue;
30279                 }
30280                 
30281                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30282
30283                 this.draw();
30284                 
30285                 return;
30286             }
30287             
30288             this.scale = this.startScale;
30289             
30290             this.onRotateFail();
30291             
30292             return false;
30293         }
30294         
30295         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30296
30297         if(this.isDocument){
30298             this.setThumbBoxSize();
30299             this.setThumbBoxPosition();
30300             this.setCanvasPosition();
30301         }
30302         
30303         this.draw();
30304         
30305         this.fireEvent('rotate', this, 'left');
30306         
30307     },
30308     
30309     onRotateRight : function(e)
30310     {
30311         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30312             
30313             var minScale = this.thumbEl.getWidth() / this.minWidth;
30314         
30315             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30316             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30317             
30318             this.startScale = this.scale;
30319             
30320             while (this.getScaleLevel() < minScale){
30321             
30322                 this.scale = this.scale + 1;
30323                 
30324                 if(!this.zoomable()){
30325                     break;
30326                 }
30327                 
30328                 if(
30329                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30330                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30331                 ){
30332                     continue;
30333                 }
30334                 
30335                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30336
30337                 this.draw();
30338                 
30339                 return;
30340             }
30341             
30342             this.scale = this.startScale;
30343             
30344             this.onRotateFail();
30345             
30346             return false;
30347         }
30348         
30349         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30350
30351         if(this.isDocument){
30352             this.setThumbBoxSize();
30353             this.setThumbBoxPosition();
30354             this.setCanvasPosition();
30355         }
30356         
30357         this.draw();
30358         
30359         this.fireEvent('rotate', this, 'right');
30360     },
30361     
30362     onRotateFail : function()
30363     {
30364         this.errorEl.show(true);
30365         
30366         var _this = this;
30367         
30368         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30369     },
30370     
30371     draw : function()
30372     {
30373         this.previewEl.dom.innerHTML = '';
30374         
30375         var canvasEl = document.createElement("canvas");
30376         
30377         var contextEl = canvasEl.getContext("2d");
30378         
30379         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30380         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30381         var center = this.imageEl.OriginWidth / 2;
30382         
30383         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30384             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30385             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30386             center = this.imageEl.OriginHeight / 2;
30387         }
30388         
30389         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30390         
30391         contextEl.translate(center, center);
30392         contextEl.rotate(this.rotate * Math.PI / 180);
30393
30394         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30395         
30396         this.canvasEl = document.createElement("canvas");
30397         
30398         this.contextEl = this.canvasEl.getContext("2d");
30399         
30400         switch (this.rotate) {
30401             case 0 :
30402                 
30403                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30404                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30405                 
30406                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30407                 
30408                 break;
30409             case 90 : 
30410                 
30411                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30412                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30413                 
30414                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30415                     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);
30416                     break;
30417                 }
30418                 
30419                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30420                 
30421                 break;
30422             case 180 :
30423                 
30424                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30425                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30426                 
30427                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30428                     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);
30429                     break;
30430                 }
30431                 
30432                 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);
30433                 
30434                 break;
30435             case 270 :
30436                 
30437                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30438                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30439         
30440                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30441                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30442                     break;
30443                 }
30444                 
30445                 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);
30446                 
30447                 break;
30448             default : 
30449                 break;
30450         }
30451         
30452         this.previewEl.appendChild(this.canvasEl);
30453         
30454         this.setCanvasPosition();
30455     },
30456     
30457     crop : function()
30458     {
30459         if(!this.canvasLoaded){
30460             return;
30461         }
30462         
30463         var imageCanvas = document.createElement("canvas");
30464         
30465         var imageContext = imageCanvas.getContext("2d");
30466         
30467         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30468         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30469         
30470         var center = imageCanvas.width / 2;
30471         
30472         imageContext.translate(center, center);
30473         
30474         imageContext.rotate(this.rotate * Math.PI / 180);
30475         
30476         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30477         
30478         var canvas = document.createElement("canvas");
30479         
30480         var context = canvas.getContext("2d");
30481                 
30482         canvas.width = this.minWidth;
30483         canvas.height = this.minHeight;
30484
30485         switch (this.rotate) {
30486             case 0 :
30487                 
30488                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30489                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30490                 
30491                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30492                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30493                 
30494                 var targetWidth = this.minWidth - 2 * x;
30495                 var targetHeight = this.minHeight - 2 * y;
30496                 
30497                 var scale = 1;
30498                 
30499                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30500                     scale = targetWidth / width;
30501                 }
30502                 
30503                 if(x > 0 && y == 0){
30504                     scale = targetHeight / height;
30505                 }
30506                 
30507                 if(x > 0 && y > 0){
30508                     scale = targetWidth / width;
30509                     
30510                     if(width < height){
30511                         scale = targetHeight / height;
30512                     }
30513                 }
30514                 
30515                 context.scale(scale, scale);
30516                 
30517                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30518                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30519
30520                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30521                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30522
30523                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30524                 
30525                 break;
30526             case 90 : 
30527                 
30528                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30529                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30530                 
30531                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30532                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30533                 
30534                 var targetWidth = this.minWidth - 2 * x;
30535                 var targetHeight = this.minHeight - 2 * y;
30536                 
30537                 var scale = 1;
30538                 
30539                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30540                     scale = targetWidth / width;
30541                 }
30542                 
30543                 if(x > 0 && y == 0){
30544                     scale = targetHeight / height;
30545                 }
30546                 
30547                 if(x > 0 && y > 0){
30548                     scale = targetWidth / width;
30549                     
30550                     if(width < height){
30551                         scale = targetHeight / height;
30552                     }
30553                 }
30554                 
30555                 context.scale(scale, scale);
30556                 
30557                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30558                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30559
30560                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30561                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30562                 
30563                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30564                 
30565                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30566                 
30567                 break;
30568             case 180 :
30569                 
30570                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30571                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30572                 
30573                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30574                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30575                 
30576                 var targetWidth = this.minWidth - 2 * x;
30577                 var targetHeight = this.minHeight - 2 * y;
30578                 
30579                 var scale = 1;
30580                 
30581                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30582                     scale = targetWidth / width;
30583                 }
30584                 
30585                 if(x > 0 && y == 0){
30586                     scale = targetHeight / height;
30587                 }
30588                 
30589                 if(x > 0 && y > 0){
30590                     scale = targetWidth / width;
30591                     
30592                     if(width < height){
30593                         scale = targetHeight / height;
30594                     }
30595                 }
30596                 
30597                 context.scale(scale, scale);
30598                 
30599                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30600                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30601
30602                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30603                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30604
30605                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30606                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30607                 
30608                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30609                 
30610                 break;
30611             case 270 :
30612                 
30613                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30614                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30615                 
30616                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30617                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30618                 
30619                 var targetWidth = this.minWidth - 2 * x;
30620                 var targetHeight = this.minHeight - 2 * y;
30621                 
30622                 var scale = 1;
30623                 
30624                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30625                     scale = targetWidth / width;
30626                 }
30627                 
30628                 if(x > 0 && y == 0){
30629                     scale = targetHeight / height;
30630                 }
30631                 
30632                 if(x > 0 && y > 0){
30633                     scale = targetWidth / width;
30634                     
30635                     if(width < height){
30636                         scale = targetHeight / height;
30637                     }
30638                 }
30639                 
30640                 context.scale(scale, scale);
30641                 
30642                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30643                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30644
30645                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30646                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30647                 
30648                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30649                 
30650                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30651                 
30652                 break;
30653             default : 
30654                 break;
30655         }
30656         
30657         this.cropData = canvas.toDataURL(this.cropType);
30658         
30659         if(this.fireEvent('crop', this, this.cropData) !== false){
30660             this.process(this.file, this.cropData);
30661         }
30662         
30663         return;
30664         
30665     },
30666     
30667     setThumbBoxSize : function()
30668     {
30669         var width, height;
30670         
30671         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30672             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30673             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30674             
30675             this.minWidth = width;
30676             this.minHeight = height;
30677             
30678             if(this.rotate == 90 || this.rotate == 270){
30679                 this.minWidth = height;
30680                 this.minHeight = width;
30681             }
30682         }
30683         
30684         height = 300;
30685         width = Math.ceil(this.minWidth * height / this.minHeight);
30686         
30687         if(this.minWidth > this.minHeight){
30688             width = 300;
30689             height = Math.ceil(this.minHeight * width / this.minWidth);
30690         }
30691         
30692         this.thumbEl.setStyle({
30693             width : width + 'px',
30694             height : height + 'px'
30695         });
30696
30697         return;
30698             
30699     },
30700     
30701     setThumbBoxPosition : function()
30702     {
30703         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30704         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30705         
30706         this.thumbEl.setLeft(x);
30707         this.thumbEl.setTop(y);
30708         
30709     },
30710     
30711     baseRotateLevel : function()
30712     {
30713         this.baseRotate = 1;
30714         
30715         if(
30716                 typeof(this.exif) != 'undefined' &&
30717                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30718                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30719         ){
30720             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30721         }
30722         
30723         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30724         
30725     },
30726     
30727     baseScaleLevel : function()
30728     {
30729         var width, height;
30730         
30731         if(this.isDocument){
30732             
30733             if(this.baseRotate == 6 || this.baseRotate == 8){
30734             
30735                 height = this.thumbEl.getHeight();
30736                 this.baseScale = height / this.imageEl.OriginWidth;
30737
30738                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30739                     width = this.thumbEl.getWidth();
30740                     this.baseScale = width / this.imageEl.OriginHeight;
30741                 }
30742
30743                 return;
30744             }
30745
30746             height = this.thumbEl.getHeight();
30747             this.baseScale = height / this.imageEl.OriginHeight;
30748
30749             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30750                 width = this.thumbEl.getWidth();
30751                 this.baseScale = width / this.imageEl.OriginWidth;
30752             }
30753
30754             return;
30755         }
30756         
30757         if(this.baseRotate == 6 || this.baseRotate == 8){
30758             
30759             width = this.thumbEl.getHeight();
30760             this.baseScale = width / this.imageEl.OriginHeight;
30761             
30762             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30763                 height = this.thumbEl.getWidth();
30764                 this.baseScale = height / this.imageEl.OriginHeight;
30765             }
30766             
30767             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30768                 height = this.thumbEl.getWidth();
30769                 this.baseScale = height / this.imageEl.OriginHeight;
30770                 
30771                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30772                     width = this.thumbEl.getHeight();
30773                     this.baseScale = width / this.imageEl.OriginWidth;
30774                 }
30775             }
30776             
30777             return;
30778         }
30779         
30780         width = this.thumbEl.getWidth();
30781         this.baseScale = width / this.imageEl.OriginWidth;
30782         
30783         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30784             height = this.thumbEl.getHeight();
30785             this.baseScale = height / this.imageEl.OriginHeight;
30786         }
30787         
30788         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30789             
30790             height = this.thumbEl.getHeight();
30791             this.baseScale = height / this.imageEl.OriginHeight;
30792             
30793             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30794                 width = this.thumbEl.getWidth();
30795                 this.baseScale = width / this.imageEl.OriginWidth;
30796             }
30797             
30798         }
30799         
30800         return;
30801     },
30802     
30803     getScaleLevel : function()
30804     {
30805         return this.baseScale * Math.pow(1.1, this.scale);
30806     },
30807     
30808     onTouchStart : function(e)
30809     {
30810         if(!this.canvasLoaded){
30811             this.beforeSelectFile(e);
30812             return;
30813         }
30814         
30815         var touches = e.browserEvent.touches;
30816         
30817         if(!touches){
30818             return;
30819         }
30820         
30821         if(touches.length == 1){
30822             this.onMouseDown(e);
30823             return;
30824         }
30825         
30826         if(touches.length != 2){
30827             return;
30828         }
30829         
30830         var coords = [];
30831         
30832         for(var i = 0, finger; finger = touches[i]; i++){
30833             coords.push(finger.pageX, finger.pageY);
30834         }
30835         
30836         var x = Math.pow(coords[0] - coords[2], 2);
30837         var y = Math.pow(coords[1] - coords[3], 2);
30838         
30839         this.startDistance = Math.sqrt(x + y);
30840         
30841         this.startScale = this.scale;
30842         
30843         this.pinching = true;
30844         this.dragable = false;
30845         
30846     },
30847     
30848     onTouchMove : function(e)
30849     {
30850         if(!this.pinching && !this.dragable){
30851             return;
30852         }
30853         
30854         var touches = e.browserEvent.touches;
30855         
30856         if(!touches){
30857             return;
30858         }
30859         
30860         if(this.dragable){
30861             this.onMouseMove(e);
30862             return;
30863         }
30864         
30865         var coords = [];
30866         
30867         for(var i = 0, finger; finger = touches[i]; i++){
30868             coords.push(finger.pageX, finger.pageY);
30869         }
30870         
30871         var x = Math.pow(coords[0] - coords[2], 2);
30872         var y = Math.pow(coords[1] - coords[3], 2);
30873         
30874         this.endDistance = Math.sqrt(x + y);
30875         
30876         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30877         
30878         if(!this.zoomable()){
30879             this.scale = this.startScale;
30880             return;
30881         }
30882         
30883         this.draw();
30884         
30885     },
30886     
30887     onTouchEnd : function(e)
30888     {
30889         this.pinching = false;
30890         this.dragable = false;
30891         
30892     },
30893     
30894     process : function(file, crop)
30895     {
30896         if(this.loadMask){
30897             this.maskEl.mask(this.loadingText);
30898         }
30899         
30900         this.xhr = new XMLHttpRequest();
30901         
30902         file.xhr = this.xhr;
30903
30904         this.xhr.open(this.method, this.url, true);
30905         
30906         var headers = {
30907             "Accept": "application/json",
30908             "Cache-Control": "no-cache",
30909             "X-Requested-With": "XMLHttpRequest"
30910         };
30911         
30912         for (var headerName in headers) {
30913             var headerValue = headers[headerName];
30914             if (headerValue) {
30915                 this.xhr.setRequestHeader(headerName, headerValue);
30916             }
30917         }
30918         
30919         var _this = this;
30920         
30921         this.xhr.onload = function()
30922         {
30923             _this.xhrOnLoad(_this.xhr);
30924         }
30925         
30926         this.xhr.onerror = function()
30927         {
30928             _this.xhrOnError(_this.xhr);
30929         }
30930         
30931         var formData = new FormData();
30932
30933         formData.append('returnHTML', 'NO');
30934         
30935         if(crop){
30936             formData.append('crop', crop);
30937         }
30938         
30939         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30940             formData.append(this.paramName, file, file.name);
30941         }
30942         
30943         if(typeof(file.filename) != 'undefined'){
30944             formData.append('filename', file.filename);
30945         }
30946         
30947         if(typeof(file.mimetype) != 'undefined'){
30948             formData.append('mimetype', file.mimetype);
30949         }
30950         
30951         if(this.fireEvent('arrange', this, formData) != false){
30952             this.xhr.send(formData);
30953         };
30954     },
30955     
30956     xhrOnLoad : function(xhr)
30957     {
30958         if(this.loadMask){
30959             this.maskEl.unmask();
30960         }
30961         
30962         if (xhr.readyState !== 4) {
30963             this.fireEvent('exception', this, xhr);
30964             return;
30965         }
30966
30967         var response = Roo.decode(xhr.responseText);
30968         
30969         if(!response.success){
30970             this.fireEvent('exception', this, xhr);
30971             return;
30972         }
30973         
30974         var response = Roo.decode(xhr.responseText);
30975         
30976         this.fireEvent('upload', this, response);
30977         
30978     },
30979     
30980     xhrOnError : function()
30981     {
30982         if(this.loadMask){
30983             this.maskEl.unmask();
30984         }
30985         
30986         Roo.log('xhr on error');
30987         
30988         var response = Roo.decode(xhr.responseText);
30989           
30990         Roo.log(response);
30991         
30992     },
30993     
30994     prepare : function(file)
30995     {   
30996         if(this.loadMask){
30997             this.maskEl.mask(this.loadingText);
30998         }
30999         
31000         this.file = false;
31001         this.exif = {};
31002         
31003         if(typeof(file) === 'string'){
31004             this.loadCanvas(file);
31005             return;
31006         }
31007         
31008         if(!file || !this.urlAPI){
31009             return;
31010         }
31011         
31012         this.file = file;
31013         this.cropType = file.type;
31014         
31015         var _this = this;
31016         
31017         if(this.fireEvent('prepare', this, this.file) != false){
31018             
31019             var reader = new FileReader();
31020             
31021             reader.onload = function (e) {
31022                 if (e.target.error) {
31023                     Roo.log(e.target.error);
31024                     return;
31025                 }
31026                 
31027                 var buffer = e.target.result,
31028                     dataView = new DataView(buffer),
31029                     offset = 2,
31030                     maxOffset = dataView.byteLength - 4,
31031                     markerBytes,
31032                     markerLength;
31033                 
31034                 if (dataView.getUint16(0) === 0xffd8) {
31035                     while (offset < maxOffset) {
31036                         markerBytes = dataView.getUint16(offset);
31037                         
31038                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31039                             markerLength = dataView.getUint16(offset + 2) + 2;
31040                             if (offset + markerLength > dataView.byteLength) {
31041                                 Roo.log('Invalid meta data: Invalid segment size.');
31042                                 break;
31043                             }
31044                             
31045                             if(markerBytes == 0xffe1){
31046                                 _this.parseExifData(
31047                                     dataView,
31048                                     offset,
31049                                     markerLength
31050                                 );
31051                             }
31052                             
31053                             offset += markerLength;
31054                             
31055                             continue;
31056                         }
31057                         
31058                         break;
31059                     }
31060                     
31061                 }
31062                 
31063                 var url = _this.urlAPI.createObjectURL(_this.file);
31064                 
31065                 _this.loadCanvas(url);
31066                 
31067                 return;
31068             }
31069             
31070             reader.readAsArrayBuffer(this.file);
31071             
31072         }
31073         
31074     },
31075     
31076     parseExifData : function(dataView, offset, length)
31077     {
31078         var tiffOffset = offset + 10,
31079             littleEndian,
31080             dirOffset;
31081     
31082         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31083             // No Exif data, might be XMP data instead
31084             return;
31085         }
31086         
31087         // Check for the ASCII code for "Exif" (0x45786966):
31088         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31089             // No Exif data, might be XMP data instead
31090             return;
31091         }
31092         if (tiffOffset + 8 > dataView.byteLength) {
31093             Roo.log('Invalid Exif data: Invalid segment size.');
31094             return;
31095         }
31096         // Check for the two null bytes:
31097         if (dataView.getUint16(offset + 8) !== 0x0000) {
31098             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31099             return;
31100         }
31101         // Check the byte alignment:
31102         switch (dataView.getUint16(tiffOffset)) {
31103         case 0x4949:
31104             littleEndian = true;
31105             break;
31106         case 0x4D4D:
31107             littleEndian = false;
31108             break;
31109         default:
31110             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31111             return;
31112         }
31113         // Check for the TIFF tag marker (0x002A):
31114         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31115             Roo.log('Invalid Exif data: Missing TIFF marker.');
31116             return;
31117         }
31118         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31119         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31120         
31121         this.parseExifTags(
31122             dataView,
31123             tiffOffset,
31124             tiffOffset + dirOffset,
31125             littleEndian
31126         );
31127     },
31128     
31129     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31130     {
31131         var tagsNumber,
31132             dirEndOffset,
31133             i;
31134         if (dirOffset + 6 > dataView.byteLength) {
31135             Roo.log('Invalid Exif data: Invalid directory offset.');
31136             return;
31137         }
31138         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31139         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31140         if (dirEndOffset + 4 > dataView.byteLength) {
31141             Roo.log('Invalid Exif data: Invalid directory size.');
31142             return;
31143         }
31144         for (i = 0; i < tagsNumber; i += 1) {
31145             this.parseExifTag(
31146                 dataView,
31147                 tiffOffset,
31148                 dirOffset + 2 + 12 * i, // tag offset
31149                 littleEndian
31150             );
31151         }
31152         // Return the offset to the next directory:
31153         return dataView.getUint32(dirEndOffset, littleEndian);
31154     },
31155     
31156     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31157     {
31158         var tag = dataView.getUint16(offset, littleEndian);
31159         
31160         this.exif[tag] = this.getExifValue(
31161             dataView,
31162             tiffOffset,
31163             offset,
31164             dataView.getUint16(offset + 2, littleEndian), // tag type
31165             dataView.getUint32(offset + 4, littleEndian), // tag length
31166             littleEndian
31167         );
31168     },
31169     
31170     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31171     {
31172         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31173             tagSize,
31174             dataOffset,
31175             values,
31176             i,
31177             str,
31178             c;
31179     
31180         if (!tagType) {
31181             Roo.log('Invalid Exif data: Invalid tag type.');
31182             return;
31183         }
31184         
31185         tagSize = tagType.size * length;
31186         // Determine if the value is contained in the dataOffset bytes,
31187         // or if the value at the dataOffset is a pointer to the actual data:
31188         dataOffset = tagSize > 4 ?
31189                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31190         if (dataOffset + tagSize > dataView.byteLength) {
31191             Roo.log('Invalid Exif data: Invalid data offset.');
31192             return;
31193         }
31194         if (length === 1) {
31195             return tagType.getValue(dataView, dataOffset, littleEndian);
31196         }
31197         values = [];
31198         for (i = 0; i < length; i += 1) {
31199             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31200         }
31201         
31202         if (tagType.ascii) {
31203             str = '';
31204             // Concatenate the chars:
31205             for (i = 0; i < values.length; i += 1) {
31206                 c = values[i];
31207                 // Ignore the terminating NULL byte(s):
31208                 if (c === '\u0000') {
31209                     break;
31210                 }
31211                 str += c;
31212             }
31213             return str;
31214         }
31215         return values;
31216     }
31217     
31218 });
31219
31220 Roo.apply(Roo.bootstrap.UploadCropbox, {
31221     tags : {
31222         'Orientation': 0x0112
31223     },
31224     
31225     Orientation: {
31226             1: 0, //'top-left',
31227 //            2: 'top-right',
31228             3: 180, //'bottom-right',
31229 //            4: 'bottom-left',
31230 //            5: 'left-top',
31231             6: 90, //'right-top',
31232 //            7: 'right-bottom',
31233             8: 270 //'left-bottom'
31234     },
31235     
31236     exifTagTypes : {
31237         // byte, 8-bit unsigned int:
31238         1: {
31239             getValue: function (dataView, dataOffset) {
31240                 return dataView.getUint8(dataOffset);
31241             },
31242             size: 1
31243         },
31244         // ascii, 8-bit byte:
31245         2: {
31246             getValue: function (dataView, dataOffset) {
31247                 return String.fromCharCode(dataView.getUint8(dataOffset));
31248             },
31249             size: 1,
31250             ascii: true
31251         },
31252         // short, 16 bit int:
31253         3: {
31254             getValue: function (dataView, dataOffset, littleEndian) {
31255                 return dataView.getUint16(dataOffset, littleEndian);
31256             },
31257             size: 2
31258         },
31259         // long, 32 bit int:
31260         4: {
31261             getValue: function (dataView, dataOffset, littleEndian) {
31262                 return dataView.getUint32(dataOffset, littleEndian);
31263             },
31264             size: 4
31265         },
31266         // rational = two long values, first is numerator, second is denominator:
31267         5: {
31268             getValue: function (dataView, dataOffset, littleEndian) {
31269                 return dataView.getUint32(dataOffset, littleEndian) /
31270                     dataView.getUint32(dataOffset + 4, littleEndian);
31271             },
31272             size: 8
31273         },
31274         // slong, 32 bit signed int:
31275         9: {
31276             getValue: function (dataView, dataOffset, littleEndian) {
31277                 return dataView.getInt32(dataOffset, littleEndian);
31278             },
31279             size: 4
31280         },
31281         // srational, two slongs, first is numerator, second is denominator:
31282         10: {
31283             getValue: function (dataView, dataOffset, littleEndian) {
31284                 return dataView.getInt32(dataOffset, littleEndian) /
31285                     dataView.getInt32(dataOffset + 4, littleEndian);
31286             },
31287             size: 8
31288         }
31289     },
31290     
31291     footer : {
31292         STANDARD : [
31293             {
31294                 tag : 'div',
31295                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31296                 action : 'rotate-left',
31297                 cn : [
31298                     {
31299                         tag : 'button',
31300                         cls : 'btn btn-default',
31301                         html : '<i class="fa fa-undo"></i>'
31302                     }
31303                 ]
31304             },
31305             {
31306                 tag : 'div',
31307                 cls : 'btn-group roo-upload-cropbox-picture',
31308                 action : 'picture',
31309                 cn : [
31310                     {
31311                         tag : 'button',
31312                         cls : 'btn btn-default',
31313                         html : '<i class="fa fa-picture-o"></i>'
31314                     }
31315                 ]
31316             },
31317             {
31318                 tag : 'div',
31319                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31320                 action : 'rotate-right',
31321                 cn : [
31322                     {
31323                         tag : 'button',
31324                         cls : 'btn btn-default',
31325                         html : '<i class="fa fa-repeat"></i>'
31326                     }
31327                 ]
31328             }
31329         ],
31330         DOCUMENT : [
31331             {
31332                 tag : 'div',
31333                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31334                 action : 'rotate-left',
31335                 cn : [
31336                     {
31337                         tag : 'button',
31338                         cls : 'btn btn-default',
31339                         html : '<i class="fa fa-undo"></i>'
31340                     }
31341                 ]
31342             },
31343             {
31344                 tag : 'div',
31345                 cls : 'btn-group roo-upload-cropbox-download',
31346                 action : 'download',
31347                 cn : [
31348                     {
31349                         tag : 'button',
31350                         cls : 'btn btn-default',
31351                         html : '<i class="fa fa-download"></i>'
31352                     }
31353                 ]
31354             },
31355             {
31356                 tag : 'div',
31357                 cls : 'btn-group roo-upload-cropbox-crop',
31358                 action : 'crop',
31359                 cn : [
31360                     {
31361                         tag : 'button',
31362                         cls : 'btn btn-default',
31363                         html : '<i class="fa fa-crop"></i>'
31364                     }
31365                 ]
31366             },
31367             {
31368                 tag : 'div',
31369                 cls : 'btn-group roo-upload-cropbox-trash',
31370                 action : 'trash',
31371                 cn : [
31372                     {
31373                         tag : 'button',
31374                         cls : 'btn btn-default',
31375                         html : '<i class="fa fa-trash"></i>'
31376                     }
31377                 ]
31378             },
31379             {
31380                 tag : 'div',
31381                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31382                 action : 'rotate-right',
31383                 cn : [
31384                     {
31385                         tag : 'button',
31386                         cls : 'btn btn-default',
31387                         html : '<i class="fa fa-repeat"></i>'
31388                     }
31389                 ]
31390             }
31391         ],
31392         ROTATOR : [
31393             {
31394                 tag : 'div',
31395                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31396                 action : 'rotate-left',
31397                 cn : [
31398                     {
31399                         tag : 'button',
31400                         cls : 'btn btn-default',
31401                         html : '<i class="fa fa-undo"></i>'
31402                     }
31403                 ]
31404             },
31405             {
31406                 tag : 'div',
31407                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31408                 action : 'rotate-right',
31409                 cn : [
31410                     {
31411                         tag : 'button',
31412                         cls : 'btn btn-default',
31413                         html : '<i class="fa fa-repeat"></i>'
31414                     }
31415                 ]
31416             }
31417         ]
31418     }
31419 });
31420
31421 /*
31422 * Licence: LGPL
31423 */
31424
31425 /**
31426  * @class Roo.bootstrap.DocumentManager
31427  * @extends Roo.bootstrap.Component
31428  * Bootstrap DocumentManager class
31429  * @cfg {String} paramName default 'imageUpload'
31430  * @cfg {String} toolTipName default 'filename'
31431  * @cfg {String} method default POST
31432  * @cfg {String} url action url
31433  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31434  * @cfg {Boolean} multiple multiple upload default true
31435  * @cfg {Number} thumbSize default 300
31436  * @cfg {String} fieldLabel
31437  * @cfg {Number} labelWidth default 4
31438  * @cfg {String} labelAlign (left|top) default left
31439  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31440 * @cfg {Number} labellg set the width of label (1-12)
31441  * @cfg {Number} labelmd set the width of label (1-12)
31442  * @cfg {Number} labelsm set the width of label (1-12)
31443  * @cfg {Number} labelxs set the width of label (1-12)
31444  * 
31445  * @constructor
31446  * Create a new DocumentManager
31447  * @param {Object} config The config object
31448  */
31449
31450 Roo.bootstrap.DocumentManager = function(config){
31451     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31452     
31453     this.files = [];
31454     this.delegates = [];
31455     
31456     this.addEvents({
31457         /**
31458          * @event initial
31459          * Fire when initial the DocumentManager
31460          * @param {Roo.bootstrap.DocumentManager} this
31461          */
31462         "initial" : true,
31463         /**
31464          * @event inspect
31465          * inspect selected file
31466          * @param {Roo.bootstrap.DocumentManager} this
31467          * @param {File} file
31468          */
31469         "inspect" : true,
31470         /**
31471          * @event exception
31472          * Fire when xhr load exception
31473          * @param {Roo.bootstrap.DocumentManager} this
31474          * @param {XMLHttpRequest} xhr
31475          */
31476         "exception" : true,
31477         /**
31478          * @event afterupload
31479          * Fire when xhr load exception
31480          * @param {Roo.bootstrap.DocumentManager} this
31481          * @param {XMLHttpRequest} xhr
31482          */
31483         "afterupload" : true,
31484         /**
31485          * @event prepare
31486          * prepare the form data
31487          * @param {Roo.bootstrap.DocumentManager} this
31488          * @param {Object} formData
31489          */
31490         "prepare" : true,
31491         /**
31492          * @event remove
31493          * Fire when remove the file
31494          * @param {Roo.bootstrap.DocumentManager} this
31495          * @param {Object} file
31496          */
31497         "remove" : true,
31498         /**
31499          * @event refresh
31500          * Fire after refresh the file
31501          * @param {Roo.bootstrap.DocumentManager} this
31502          */
31503         "refresh" : true,
31504         /**
31505          * @event click
31506          * Fire after click the image
31507          * @param {Roo.bootstrap.DocumentManager} this
31508          * @param {Object} file
31509          */
31510         "click" : true,
31511         /**
31512          * @event edit
31513          * Fire when upload a image and editable set to true
31514          * @param {Roo.bootstrap.DocumentManager} this
31515          * @param {Object} file
31516          */
31517         "edit" : true,
31518         /**
31519          * @event beforeselectfile
31520          * Fire before select file
31521          * @param {Roo.bootstrap.DocumentManager} this
31522          */
31523         "beforeselectfile" : true,
31524         /**
31525          * @event process
31526          * Fire before process file
31527          * @param {Roo.bootstrap.DocumentManager} this
31528          * @param {Object} file
31529          */
31530         "process" : true,
31531         /**
31532          * @event previewrendered
31533          * Fire when preview rendered
31534          * @param {Roo.bootstrap.DocumentManager} this
31535          * @param {Object} file
31536          */
31537         "previewrendered" : true,
31538         /**
31539          */
31540         "previewResize" : true
31541         
31542     });
31543 };
31544
31545 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31546     
31547     boxes : 0,
31548     inputName : '',
31549     thumbSize : 300,
31550     multiple : true,
31551     files : false,
31552     method : 'POST',
31553     url : '',
31554     paramName : 'imageUpload',
31555     toolTipName : 'filename',
31556     fieldLabel : '',
31557     labelWidth : 4,
31558     labelAlign : 'left',
31559     editable : true,
31560     delegates : false,
31561     xhr : false, 
31562     
31563     labellg : 0,
31564     labelmd : 0,
31565     labelsm : 0,
31566     labelxs : 0,
31567     
31568     getAutoCreate : function()
31569     {   
31570         var managerWidget = {
31571             tag : 'div',
31572             cls : 'roo-document-manager',
31573             cn : [
31574                 {
31575                     tag : 'input',
31576                     cls : 'roo-document-manager-selector',
31577                     type : 'file'
31578                 },
31579                 {
31580                     tag : 'div',
31581                     cls : 'roo-document-manager-uploader',
31582                     cn : [
31583                         {
31584                             tag : 'div',
31585                             cls : 'roo-document-manager-upload-btn',
31586                             html : '<i class="fa fa-plus"></i>'
31587                         }
31588                     ]
31589                     
31590                 }
31591             ]
31592         };
31593         
31594         var content = [
31595             {
31596                 tag : 'div',
31597                 cls : 'column col-md-12',
31598                 cn : managerWidget
31599             }
31600         ];
31601         
31602         if(this.fieldLabel.length){
31603             
31604             content = [
31605                 {
31606                     tag : 'div',
31607                     cls : 'column col-md-12',
31608                     html : this.fieldLabel
31609                 },
31610                 {
31611                     tag : 'div',
31612                     cls : 'column col-md-12',
31613                     cn : managerWidget
31614                 }
31615             ];
31616
31617             if(this.labelAlign == 'left'){
31618                 content = [
31619                     {
31620                         tag : 'div',
31621                         cls : 'column',
31622                         html : this.fieldLabel
31623                     },
31624                     {
31625                         tag : 'div',
31626                         cls : 'column',
31627                         cn : managerWidget
31628                     }
31629                 ];
31630                 
31631                 if(this.labelWidth > 12){
31632                     content[0].style = "width: " + this.labelWidth + 'px';
31633                 }
31634
31635                 if(this.labelWidth < 13 && this.labelmd == 0){
31636                     this.labelmd = this.labelWidth;
31637                 }
31638
31639                 if(this.labellg > 0){
31640                     content[0].cls += ' col-lg-' + this.labellg;
31641                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31642                 }
31643
31644                 if(this.labelmd > 0){
31645                     content[0].cls += ' col-md-' + this.labelmd;
31646                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31647                 }
31648
31649                 if(this.labelsm > 0){
31650                     content[0].cls += ' col-sm-' + this.labelsm;
31651                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31652                 }
31653
31654                 if(this.labelxs > 0){
31655                     content[0].cls += ' col-xs-' + this.labelxs;
31656                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31657                 }
31658                 
31659             }
31660         }
31661         
31662         var cfg = {
31663             tag : 'div',
31664             cls : 'row clearfix',
31665             cn : content
31666         };
31667         
31668         return cfg;
31669         
31670     },
31671     
31672     initEvents : function()
31673     {
31674         this.managerEl = this.el.select('.roo-document-manager', true).first();
31675         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31676         
31677         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31678         this.selectorEl.hide();
31679         
31680         if(this.multiple){
31681             this.selectorEl.attr('multiple', 'multiple');
31682         }
31683         
31684         this.selectorEl.on('change', this.onFileSelected, this);
31685         
31686         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31687         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31688         
31689         this.uploader.on('click', this.onUploaderClick, this);
31690         
31691         this.renderProgressDialog();
31692         
31693         var _this = this;
31694         
31695         window.addEventListener("resize", function() { _this.refresh(); } );
31696         
31697         this.fireEvent('initial', this);
31698     },
31699     
31700     renderProgressDialog : function()
31701     {
31702         var _this = this;
31703         
31704         this.progressDialog = new Roo.bootstrap.Modal({
31705             cls : 'roo-document-manager-progress-dialog',
31706             allow_close : false,
31707             animate : false,
31708             title : '',
31709             buttons : [
31710                 {
31711                     name  :'cancel',
31712                     weight : 'danger',
31713                     html : 'Cancel'
31714                 }
31715             ], 
31716             listeners : { 
31717                 btnclick : function() {
31718                     _this.uploadCancel();
31719                     this.hide();
31720                 }
31721             }
31722         });
31723          
31724         this.progressDialog.render(Roo.get(document.body));
31725          
31726         this.progress = new Roo.bootstrap.Progress({
31727             cls : 'roo-document-manager-progress',
31728             active : true,
31729             striped : true
31730         });
31731         
31732         this.progress.render(this.progressDialog.getChildContainer());
31733         
31734         this.progressBar = new Roo.bootstrap.ProgressBar({
31735             cls : 'roo-document-manager-progress-bar',
31736             aria_valuenow : 0,
31737             aria_valuemin : 0,
31738             aria_valuemax : 12,
31739             panel : 'success'
31740         });
31741         
31742         this.progressBar.render(this.progress.getChildContainer());
31743     },
31744     
31745     onUploaderClick : function(e)
31746     {
31747         e.preventDefault();
31748      
31749         if(this.fireEvent('beforeselectfile', this) != false){
31750             this.selectorEl.dom.click();
31751         }
31752         
31753     },
31754     
31755     onFileSelected : function(e)
31756     {
31757         e.preventDefault();
31758         
31759         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31760             return;
31761         }
31762         
31763         Roo.each(this.selectorEl.dom.files, function(file){
31764             if(this.fireEvent('inspect', this, file) != false){
31765                 this.files.push(file);
31766             }
31767         }, this);
31768         
31769         this.queue();
31770         
31771     },
31772     
31773     queue : function()
31774     {
31775         this.selectorEl.dom.value = '';
31776         
31777         if(!this.files || !this.files.length){
31778             return;
31779         }
31780         
31781         if(this.boxes > 0 && this.files.length > this.boxes){
31782             this.files = this.files.slice(0, this.boxes);
31783         }
31784         
31785         this.uploader.show();
31786         
31787         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31788             this.uploader.hide();
31789         }
31790         
31791         var _this = this;
31792         
31793         var files = [];
31794         
31795         var docs = [];
31796         
31797         Roo.each(this.files, function(file){
31798             
31799             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31800                 var f = this.renderPreview(file);
31801                 files.push(f);
31802                 return;
31803             }
31804             
31805             if(file.type.indexOf('image') != -1){
31806                 this.delegates.push(
31807                     (function(){
31808                         _this.process(file);
31809                     }).createDelegate(this)
31810                 );
31811         
31812                 return;
31813             }
31814             
31815             docs.push(
31816                 (function(){
31817                     _this.process(file);
31818                 }).createDelegate(this)
31819             );
31820             
31821         }, this);
31822         
31823         this.files = files;
31824         
31825         this.delegates = this.delegates.concat(docs);
31826         
31827         if(!this.delegates.length){
31828             this.refresh();
31829             return;
31830         }
31831         
31832         this.progressBar.aria_valuemax = this.delegates.length;
31833         
31834         this.arrange();
31835         
31836         return;
31837     },
31838     
31839     arrange : function()
31840     {
31841         if(!this.delegates.length){
31842             this.progressDialog.hide();
31843             this.refresh();
31844             return;
31845         }
31846         
31847         var delegate = this.delegates.shift();
31848         
31849         this.progressDialog.show();
31850         
31851         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31852         
31853         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31854         
31855         delegate();
31856     },
31857     
31858     refresh : function()
31859     {
31860         this.uploader.show();
31861         
31862         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31863             this.uploader.hide();
31864         }
31865         
31866         Roo.isTouch ? this.closable(false) : this.closable(true);
31867         
31868         this.fireEvent('refresh', this);
31869     },
31870     
31871     onRemove : function(e, el, o)
31872     {
31873         e.preventDefault();
31874         
31875         this.fireEvent('remove', this, o);
31876         
31877     },
31878     
31879     remove : function(o)
31880     {
31881         var files = [];
31882         
31883         Roo.each(this.files, function(file){
31884             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31885                 files.push(file);
31886                 return;
31887             }
31888
31889             o.target.remove();
31890
31891         }, this);
31892         
31893         this.files = files;
31894         
31895         this.refresh();
31896     },
31897     
31898     clear : function()
31899     {
31900         Roo.each(this.files, function(file){
31901             if(!file.target){
31902                 return;
31903             }
31904             
31905             file.target.remove();
31906
31907         }, this);
31908         
31909         this.files = [];
31910         
31911         this.refresh();
31912     },
31913     
31914     onClick : function(e, el, o)
31915     {
31916         e.preventDefault();
31917         
31918         this.fireEvent('click', this, o);
31919         
31920     },
31921     
31922     closable : function(closable)
31923     {
31924         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31925             
31926             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31927             
31928             if(closable){
31929                 el.show();
31930                 return;
31931             }
31932             
31933             el.hide();
31934             
31935         }, this);
31936     },
31937     
31938     xhrOnLoad : function(xhr)
31939     {
31940         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31941             el.remove();
31942         }, this);
31943         
31944         if (xhr.readyState !== 4) {
31945             this.arrange();
31946             this.fireEvent('exception', this, xhr);
31947             return;
31948         }
31949
31950         var response = Roo.decode(xhr.responseText);
31951         
31952         if(!response.success){
31953             this.arrange();
31954             this.fireEvent('exception', this, xhr);
31955             return;
31956         }
31957         
31958         var file = this.renderPreview(response.data);
31959         
31960         this.files.push(file);
31961         
31962         this.arrange();
31963         
31964         this.fireEvent('afterupload', this, xhr);
31965         
31966     },
31967     
31968     xhrOnError : function(xhr)
31969     {
31970         Roo.log('xhr on error');
31971         
31972         var response = Roo.decode(xhr.responseText);
31973           
31974         Roo.log(response);
31975         
31976         this.arrange();
31977     },
31978     
31979     process : function(file)
31980     {
31981         if(this.fireEvent('process', this, file) !== false){
31982             if(this.editable && file.type.indexOf('image') != -1){
31983                 this.fireEvent('edit', this, file);
31984                 return;
31985             }
31986
31987             this.uploadStart(file, false);
31988
31989             return;
31990         }
31991         
31992     },
31993     
31994     uploadStart : function(file, crop)
31995     {
31996         this.xhr = new XMLHttpRequest();
31997         
31998         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31999             this.arrange();
32000             return;
32001         }
32002         
32003         file.xhr = this.xhr;
32004             
32005         this.managerEl.createChild({
32006             tag : 'div',
32007             cls : 'roo-document-manager-loading',
32008             cn : [
32009                 {
32010                     tag : 'div',
32011                     tooltip : file.name,
32012                     cls : 'roo-document-manager-thumb',
32013                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32014                 }
32015             ]
32016
32017         });
32018
32019         this.xhr.open(this.method, this.url, true);
32020         
32021         var headers = {
32022             "Accept": "application/json",
32023             "Cache-Control": "no-cache",
32024             "X-Requested-With": "XMLHttpRequest"
32025         };
32026         
32027         for (var headerName in headers) {
32028             var headerValue = headers[headerName];
32029             if (headerValue) {
32030                 this.xhr.setRequestHeader(headerName, headerValue);
32031             }
32032         }
32033         
32034         var _this = this;
32035         
32036         this.xhr.onload = function()
32037         {
32038             _this.xhrOnLoad(_this.xhr);
32039         }
32040         
32041         this.xhr.onerror = function()
32042         {
32043             _this.xhrOnError(_this.xhr);
32044         }
32045         
32046         var formData = new FormData();
32047
32048         formData.append('returnHTML', 'NO');
32049         
32050         if(crop){
32051             formData.append('crop', crop);
32052         }
32053         
32054         formData.append(this.paramName, file, file.name);
32055         
32056         var options = {
32057             file : file, 
32058             manually : false
32059         };
32060         
32061         if(this.fireEvent('prepare', this, formData, options) != false){
32062             
32063             if(options.manually){
32064                 return;
32065             }
32066             
32067             this.xhr.send(formData);
32068             return;
32069         };
32070         
32071         this.uploadCancel();
32072     },
32073     
32074     uploadCancel : function()
32075     {
32076         if (this.xhr) {
32077             this.xhr.abort();
32078         }
32079         
32080         this.delegates = [];
32081         
32082         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32083             el.remove();
32084         }, this);
32085         
32086         this.arrange();
32087     },
32088     
32089     renderPreview : function(file)
32090     {
32091         if(typeof(file.target) != 'undefined' && file.target){
32092             return file;
32093         }
32094         
32095         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32096         
32097         var previewEl = this.managerEl.createChild({
32098             tag : 'div',
32099             cls : 'roo-document-manager-preview',
32100             cn : [
32101                 {
32102                     tag : 'div',
32103                     tooltip : file[this.toolTipName],
32104                     cls : 'roo-document-manager-thumb',
32105                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32106                 },
32107                 {
32108                     tag : 'button',
32109                     cls : 'close',
32110                     html : '<i class="fa fa-times-circle"></i>'
32111                 }
32112             ]
32113         });
32114
32115         var close = previewEl.select('button.close', true).first();
32116
32117         close.on('click', this.onRemove, this, file);
32118
32119         file.target = previewEl;
32120
32121         var image = previewEl.select('img', true).first();
32122         
32123         var _this = this;
32124         
32125         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32126         
32127         image.on('click', this.onClick, this, file);
32128         
32129         this.fireEvent('previewrendered', this, file);
32130         
32131         return file;
32132         
32133     },
32134     
32135     onPreviewLoad : function(file, image)
32136     {
32137         if(typeof(file.target) == 'undefined' || !file.target){
32138             return;
32139         }
32140         
32141         var width = image.dom.naturalWidth || image.dom.width;
32142         var height = image.dom.naturalHeight || image.dom.height;
32143         
32144         if(!this.previewResize) {
32145             return;
32146         }
32147         
32148         if(width > height){
32149             file.target.addClass('wide');
32150             return;
32151         }
32152         
32153         file.target.addClass('tall');
32154         return;
32155         
32156     },
32157     
32158     uploadFromSource : function(file, crop)
32159     {
32160         this.xhr = new XMLHttpRequest();
32161         
32162         this.managerEl.createChild({
32163             tag : 'div',
32164             cls : 'roo-document-manager-loading',
32165             cn : [
32166                 {
32167                     tag : 'div',
32168                     tooltip : file.name,
32169                     cls : 'roo-document-manager-thumb',
32170                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32171                 }
32172             ]
32173
32174         });
32175
32176         this.xhr.open(this.method, this.url, true);
32177         
32178         var headers = {
32179             "Accept": "application/json",
32180             "Cache-Control": "no-cache",
32181             "X-Requested-With": "XMLHttpRequest"
32182         };
32183         
32184         for (var headerName in headers) {
32185             var headerValue = headers[headerName];
32186             if (headerValue) {
32187                 this.xhr.setRequestHeader(headerName, headerValue);
32188             }
32189         }
32190         
32191         var _this = this;
32192         
32193         this.xhr.onload = function()
32194         {
32195             _this.xhrOnLoad(_this.xhr);
32196         }
32197         
32198         this.xhr.onerror = function()
32199         {
32200             _this.xhrOnError(_this.xhr);
32201         }
32202         
32203         var formData = new FormData();
32204
32205         formData.append('returnHTML', 'NO');
32206         
32207         formData.append('crop', crop);
32208         
32209         if(typeof(file.filename) != 'undefined'){
32210             formData.append('filename', file.filename);
32211         }
32212         
32213         if(typeof(file.mimetype) != 'undefined'){
32214             formData.append('mimetype', file.mimetype);
32215         }
32216         
32217         Roo.log(formData);
32218         
32219         if(this.fireEvent('prepare', this, formData) != false){
32220             this.xhr.send(formData);
32221         };
32222     }
32223 });
32224
32225 /*
32226 * Licence: LGPL
32227 */
32228
32229 /**
32230  * @class Roo.bootstrap.DocumentViewer
32231  * @extends Roo.bootstrap.Component
32232  * Bootstrap DocumentViewer class
32233  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32234  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32235  * 
32236  * @constructor
32237  * Create a new DocumentViewer
32238  * @param {Object} config The config object
32239  */
32240
32241 Roo.bootstrap.DocumentViewer = function(config){
32242     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32243     
32244     this.addEvents({
32245         /**
32246          * @event initial
32247          * Fire after initEvent
32248          * @param {Roo.bootstrap.DocumentViewer} this
32249          */
32250         "initial" : true,
32251         /**
32252          * @event click
32253          * Fire after click
32254          * @param {Roo.bootstrap.DocumentViewer} this
32255          */
32256         "click" : true,
32257         /**
32258          * @event download
32259          * Fire after download button
32260          * @param {Roo.bootstrap.DocumentViewer} this
32261          */
32262         "download" : true,
32263         /**
32264          * @event trash
32265          * Fire after trash button
32266          * @param {Roo.bootstrap.DocumentViewer} this
32267          */
32268         "trash" : true
32269         
32270     });
32271 };
32272
32273 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32274     
32275     showDownload : true,
32276     
32277     showTrash : true,
32278     
32279     getAutoCreate : function()
32280     {
32281         var cfg = {
32282             tag : 'div',
32283             cls : 'roo-document-viewer',
32284             cn : [
32285                 {
32286                     tag : 'div',
32287                     cls : 'roo-document-viewer-body',
32288                     cn : [
32289                         {
32290                             tag : 'div',
32291                             cls : 'roo-document-viewer-thumb',
32292                             cn : [
32293                                 {
32294                                     tag : 'img',
32295                                     cls : 'roo-document-viewer-image'
32296                                 }
32297                             ]
32298                         }
32299                     ]
32300                 },
32301                 {
32302                     tag : 'div',
32303                     cls : 'roo-document-viewer-footer',
32304                     cn : {
32305                         tag : 'div',
32306                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32307                         cn : [
32308                             {
32309                                 tag : 'div',
32310                                 cls : 'btn-group roo-document-viewer-download',
32311                                 cn : [
32312                                     {
32313                                         tag : 'button',
32314                                         cls : 'btn btn-default',
32315                                         html : '<i class="fa fa-download"></i>'
32316                                     }
32317                                 ]
32318                             },
32319                             {
32320                                 tag : 'div',
32321                                 cls : 'btn-group roo-document-viewer-trash',
32322                                 cn : [
32323                                     {
32324                                         tag : 'button',
32325                                         cls : 'btn btn-default',
32326                                         html : '<i class="fa fa-trash"></i>'
32327                                     }
32328                                 ]
32329                             }
32330                         ]
32331                     }
32332                 }
32333             ]
32334         };
32335         
32336         return cfg;
32337     },
32338     
32339     initEvents : function()
32340     {
32341         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32342         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32343         
32344         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32345         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32346         
32347         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32348         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32349         
32350         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32351         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32352         
32353         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32354         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32355         
32356         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32357         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32358         
32359         this.bodyEl.on('click', this.onClick, this);
32360         this.downloadBtn.on('click', this.onDownload, this);
32361         this.trashBtn.on('click', this.onTrash, this);
32362         
32363         this.downloadBtn.hide();
32364         this.trashBtn.hide();
32365         
32366         if(this.showDownload){
32367             this.downloadBtn.show();
32368         }
32369         
32370         if(this.showTrash){
32371             this.trashBtn.show();
32372         }
32373         
32374         if(!this.showDownload && !this.showTrash) {
32375             this.footerEl.hide();
32376         }
32377         
32378     },
32379     
32380     initial : function()
32381     {
32382         this.fireEvent('initial', this);
32383         
32384     },
32385     
32386     onClick : function(e)
32387     {
32388         e.preventDefault();
32389         
32390         this.fireEvent('click', this);
32391     },
32392     
32393     onDownload : function(e)
32394     {
32395         e.preventDefault();
32396         
32397         this.fireEvent('download', this);
32398     },
32399     
32400     onTrash : function(e)
32401     {
32402         e.preventDefault();
32403         
32404         this.fireEvent('trash', this);
32405     }
32406     
32407 });
32408 /*
32409  * - LGPL
32410  *
32411  * nav progress bar
32412  * 
32413  */
32414
32415 /**
32416  * @class Roo.bootstrap.NavProgressBar
32417  * @extends Roo.bootstrap.Component
32418  * Bootstrap NavProgressBar class
32419  * 
32420  * @constructor
32421  * Create a new nav progress bar
32422  * @param {Object} config The config object
32423  */
32424
32425 Roo.bootstrap.NavProgressBar = function(config){
32426     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32427
32428     this.bullets = this.bullets || [];
32429    
32430 //    Roo.bootstrap.NavProgressBar.register(this);
32431      this.addEvents({
32432         /**
32433              * @event changed
32434              * Fires when the active item changes
32435              * @param {Roo.bootstrap.NavProgressBar} this
32436              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32437              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32438          */
32439         'changed': true
32440      });
32441     
32442 };
32443
32444 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32445     
32446     bullets : [],
32447     barItems : [],
32448     
32449     getAutoCreate : function()
32450     {
32451         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32452         
32453         cfg = {
32454             tag : 'div',
32455             cls : 'roo-navigation-bar-group',
32456             cn : [
32457                 {
32458                     tag : 'div',
32459                     cls : 'roo-navigation-top-bar'
32460                 },
32461                 {
32462                     tag : 'div',
32463                     cls : 'roo-navigation-bullets-bar',
32464                     cn : [
32465                         {
32466                             tag : 'ul',
32467                             cls : 'roo-navigation-bar'
32468                         }
32469                     ]
32470                 },
32471                 
32472                 {
32473                     tag : 'div',
32474                     cls : 'roo-navigation-bottom-bar'
32475                 }
32476             ]
32477             
32478         };
32479         
32480         return cfg;
32481         
32482     },
32483     
32484     initEvents: function() 
32485     {
32486         
32487     },
32488     
32489     onRender : function(ct, position) 
32490     {
32491         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32492         
32493         if(this.bullets.length){
32494             Roo.each(this.bullets, function(b){
32495                this.addItem(b);
32496             }, this);
32497         }
32498         
32499         this.format();
32500         
32501     },
32502     
32503     addItem : function(cfg)
32504     {
32505         var item = new Roo.bootstrap.NavProgressItem(cfg);
32506         
32507         item.parentId = this.id;
32508         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32509         
32510         if(cfg.html){
32511             var top = new Roo.bootstrap.Element({
32512                 tag : 'div',
32513                 cls : 'roo-navigation-bar-text'
32514             });
32515             
32516             var bottom = new Roo.bootstrap.Element({
32517                 tag : 'div',
32518                 cls : 'roo-navigation-bar-text'
32519             });
32520             
32521             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32522             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32523             
32524             var topText = new Roo.bootstrap.Element({
32525                 tag : 'span',
32526                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32527             });
32528             
32529             var bottomText = new Roo.bootstrap.Element({
32530                 tag : 'span',
32531                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32532             });
32533             
32534             topText.onRender(top.el, null);
32535             bottomText.onRender(bottom.el, null);
32536             
32537             item.topEl = top;
32538             item.bottomEl = bottom;
32539         }
32540         
32541         this.barItems.push(item);
32542         
32543         return item;
32544     },
32545     
32546     getActive : function()
32547     {
32548         var active = false;
32549         
32550         Roo.each(this.barItems, function(v){
32551             
32552             if (!v.isActive()) {
32553                 return;
32554             }
32555             
32556             active = v;
32557             return false;
32558             
32559         });
32560         
32561         return active;
32562     },
32563     
32564     setActiveItem : function(item)
32565     {
32566         var prev = false;
32567         
32568         Roo.each(this.barItems, function(v){
32569             if (v.rid == item.rid) {
32570                 return ;
32571             }
32572             
32573             if (v.isActive()) {
32574                 v.setActive(false);
32575                 prev = v;
32576             }
32577         });
32578
32579         item.setActive(true);
32580         
32581         this.fireEvent('changed', this, item, prev);
32582     },
32583     
32584     getBarItem: function(rid)
32585     {
32586         var ret = false;
32587         
32588         Roo.each(this.barItems, function(e) {
32589             if (e.rid != rid) {
32590                 return;
32591             }
32592             
32593             ret =  e;
32594             return false;
32595         });
32596         
32597         return ret;
32598     },
32599     
32600     indexOfItem : function(item)
32601     {
32602         var index = false;
32603         
32604         Roo.each(this.barItems, function(v, i){
32605             
32606             if (v.rid != item.rid) {
32607                 return;
32608             }
32609             
32610             index = i;
32611             return false
32612         });
32613         
32614         return index;
32615     },
32616     
32617     setActiveNext : function()
32618     {
32619         var i = this.indexOfItem(this.getActive());
32620         
32621         if (i > this.barItems.length) {
32622             return;
32623         }
32624         
32625         this.setActiveItem(this.barItems[i+1]);
32626     },
32627     
32628     setActivePrev : function()
32629     {
32630         var i = this.indexOfItem(this.getActive());
32631         
32632         if (i  < 1) {
32633             return;
32634         }
32635         
32636         this.setActiveItem(this.barItems[i-1]);
32637     },
32638     
32639     format : function()
32640     {
32641         if(!this.barItems.length){
32642             return;
32643         }
32644      
32645         var width = 100 / this.barItems.length;
32646         
32647         Roo.each(this.barItems, function(i){
32648             i.el.setStyle('width', width + '%');
32649             i.topEl.el.setStyle('width', width + '%');
32650             i.bottomEl.el.setStyle('width', width + '%');
32651         }, this);
32652         
32653     }
32654     
32655 });
32656 /*
32657  * - LGPL
32658  *
32659  * Nav Progress Item
32660  * 
32661  */
32662
32663 /**
32664  * @class Roo.bootstrap.NavProgressItem
32665  * @extends Roo.bootstrap.Component
32666  * Bootstrap NavProgressItem class
32667  * @cfg {String} rid the reference id
32668  * @cfg {Boolean} active (true|false) Is item active default false
32669  * @cfg {Boolean} disabled (true|false) Is item active default false
32670  * @cfg {String} html
32671  * @cfg {String} position (top|bottom) text position default bottom
32672  * @cfg {String} icon show icon instead of number
32673  * 
32674  * @constructor
32675  * Create a new NavProgressItem
32676  * @param {Object} config The config object
32677  */
32678 Roo.bootstrap.NavProgressItem = function(config){
32679     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32680     this.addEvents({
32681         // raw events
32682         /**
32683          * @event click
32684          * The raw click event for the entire grid.
32685          * @param {Roo.bootstrap.NavProgressItem} this
32686          * @param {Roo.EventObject} e
32687          */
32688         "click" : true
32689     });
32690    
32691 };
32692
32693 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32694     
32695     rid : '',
32696     active : false,
32697     disabled : false,
32698     html : '',
32699     position : 'bottom',
32700     icon : false,
32701     
32702     getAutoCreate : function()
32703     {
32704         var iconCls = 'roo-navigation-bar-item-icon';
32705         
32706         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32707         
32708         var cfg = {
32709             tag: 'li',
32710             cls: 'roo-navigation-bar-item',
32711             cn : [
32712                 {
32713                     tag : 'i',
32714                     cls : iconCls
32715                 }
32716             ]
32717         };
32718         
32719         if(this.active){
32720             cfg.cls += ' active';
32721         }
32722         if(this.disabled){
32723             cfg.cls += ' disabled';
32724         }
32725         
32726         return cfg;
32727     },
32728     
32729     disable : function()
32730     {
32731         this.setDisabled(true);
32732     },
32733     
32734     enable : function()
32735     {
32736         this.setDisabled(false);
32737     },
32738     
32739     initEvents: function() 
32740     {
32741         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32742         
32743         this.iconEl.on('click', this.onClick, this);
32744     },
32745     
32746     onClick : function(e)
32747     {
32748         e.preventDefault();
32749         
32750         if(this.disabled){
32751             return;
32752         }
32753         
32754         if(this.fireEvent('click', this, e) === false){
32755             return;
32756         };
32757         
32758         this.parent().setActiveItem(this);
32759     },
32760     
32761     isActive: function () 
32762     {
32763         return this.active;
32764     },
32765     
32766     setActive : function(state)
32767     {
32768         if(this.active == state){
32769             return;
32770         }
32771         
32772         this.active = state;
32773         
32774         if (state) {
32775             this.el.addClass('active');
32776             return;
32777         }
32778         
32779         this.el.removeClass('active');
32780         
32781         return;
32782     },
32783     
32784     setDisabled : function(state)
32785     {
32786         if(this.disabled == state){
32787             return;
32788         }
32789         
32790         this.disabled = state;
32791         
32792         if (state) {
32793             this.el.addClass('disabled');
32794             return;
32795         }
32796         
32797         this.el.removeClass('disabled');
32798     },
32799     
32800     tooltipEl : function()
32801     {
32802         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32803     }
32804 });
32805  
32806
32807  /*
32808  * - LGPL
32809  *
32810  * FieldLabel
32811  * 
32812  */
32813
32814 /**
32815  * @class Roo.bootstrap.FieldLabel
32816  * @extends Roo.bootstrap.Component
32817  * Bootstrap FieldLabel class
32818  * @cfg {String} html contents of the element
32819  * @cfg {String} tag tag of the element default label
32820  * @cfg {String} cls class of the element
32821  * @cfg {String} target label target 
32822  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32823  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32824  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32825  * @cfg {String} iconTooltip default "This field is required"
32826  * @cfg {String} indicatorpos (left|right) default left
32827  * 
32828  * @constructor
32829  * Create a new FieldLabel
32830  * @param {Object} config The config object
32831  */
32832
32833 Roo.bootstrap.FieldLabel = function(config){
32834     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32835     
32836     this.addEvents({
32837             /**
32838              * @event invalid
32839              * Fires after the field has been marked as invalid.
32840              * @param {Roo.form.FieldLabel} this
32841              * @param {String} msg The validation message
32842              */
32843             invalid : true,
32844             /**
32845              * @event valid
32846              * Fires after the field has been validated with no errors.
32847              * @param {Roo.form.FieldLabel} this
32848              */
32849             valid : true
32850         });
32851 };
32852
32853 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32854     
32855     tag: 'label',
32856     cls: '',
32857     html: '',
32858     target: '',
32859     allowBlank : true,
32860     invalidClass : 'has-warning',
32861     validClass : 'has-success',
32862     iconTooltip : 'This field is required',
32863     indicatorpos : 'left',
32864     
32865     getAutoCreate : function(){
32866         
32867         var cls = "";
32868         if (!this.allowBlank) {
32869             cls  = "visible";
32870         }
32871         
32872         var cfg = {
32873             tag : this.tag,
32874             cls : 'roo-bootstrap-field-label ' + this.cls,
32875             for : this.target,
32876             cn : [
32877                 {
32878                     tag : 'i',
32879                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32880                     tooltip : this.iconTooltip
32881                 },
32882                 {
32883                     tag : 'span',
32884                     html : this.html
32885                 }
32886             ] 
32887         };
32888         
32889         if(this.indicatorpos == 'right'){
32890             var cfg = {
32891                 tag : this.tag,
32892                 cls : 'roo-bootstrap-field-label ' + this.cls,
32893                 for : this.target,
32894                 cn : [
32895                     {
32896                         tag : 'span',
32897                         html : this.html
32898                     },
32899                     {
32900                         tag : 'i',
32901                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32902                         tooltip : this.iconTooltip
32903                     }
32904                 ] 
32905             };
32906         }
32907         
32908         return cfg;
32909     },
32910     
32911     initEvents: function() 
32912     {
32913         Roo.bootstrap.Element.superclass.initEvents.call(this);
32914         
32915         this.indicator = this.indicatorEl();
32916         
32917         if(this.indicator){
32918             this.indicator.removeClass('visible');
32919             this.indicator.addClass('invisible');
32920         }
32921         
32922         Roo.bootstrap.FieldLabel.register(this);
32923     },
32924     
32925     indicatorEl : function()
32926     {
32927         var indicator = this.el.select('i.roo-required-indicator',true).first();
32928         
32929         if(!indicator){
32930             return false;
32931         }
32932         
32933         return indicator;
32934         
32935     },
32936     
32937     /**
32938      * Mark this field as valid
32939      */
32940     markValid : function()
32941     {
32942         if(this.indicator){
32943             this.indicator.removeClass('visible');
32944             this.indicator.addClass('invisible');
32945         }
32946         if (Roo.bootstrap.version == 3) {
32947             this.el.removeClass(this.invalidClass);
32948             this.el.addClass(this.validClass);
32949         } else {
32950             this.el.removeClass('is-invalid');
32951             this.el.addClass('is-valid');
32952         }
32953         
32954         
32955         this.fireEvent('valid', this);
32956     },
32957     
32958     /**
32959      * Mark this field as invalid
32960      * @param {String} msg The validation message
32961      */
32962     markInvalid : function(msg)
32963     {
32964         if(this.indicator){
32965             this.indicator.removeClass('invisible');
32966             this.indicator.addClass('visible');
32967         }
32968           if (Roo.bootstrap.version == 3) {
32969             this.el.removeClass(this.validClass);
32970             this.el.addClass(this.invalidClass);
32971         } else {
32972             this.el.removeClass('is-valid');
32973             this.el.addClass('is-invalid');
32974         }
32975         
32976         
32977         this.fireEvent('invalid', this, msg);
32978     }
32979     
32980    
32981 });
32982
32983 Roo.apply(Roo.bootstrap.FieldLabel, {
32984     
32985     groups: {},
32986     
32987      /**
32988     * register a FieldLabel Group
32989     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32990     */
32991     register : function(label)
32992     {
32993         if(this.groups.hasOwnProperty(label.target)){
32994             return;
32995         }
32996      
32997         this.groups[label.target] = label;
32998         
32999     },
33000     /**
33001     * fetch a FieldLabel Group based on the target
33002     * @param {string} target
33003     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33004     */
33005     get: function(target) {
33006         if (typeof(this.groups[target]) == 'undefined') {
33007             return false;
33008         }
33009         
33010         return this.groups[target] ;
33011     }
33012 });
33013
33014  
33015
33016  /*
33017  * - LGPL
33018  *
33019  * page DateSplitField.
33020  * 
33021  */
33022
33023
33024 /**
33025  * @class Roo.bootstrap.DateSplitField
33026  * @extends Roo.bootstrap.Component
33027  * Bootstrap DateSplitField class
33028  * @cfg {string} fieldLabel - the label associated
33029  * @cfg {Number} labelWidth set the width of label (0-12)
33030  * @cfg {String} labelAlign (top|left)
33031  * @cfg {Boolean} dayAllowBlank (true|false) default false
33032  * @cfg {Boolean} monthAllowBlank (true|false) default false
33033  * @cfg {Boolean} yearAllowBlank (true|false) default false
33034  * @cfg {string} dayPlaceholder 
33035  * @cfg {string} monthPlaceholder
33036  * @cfg {string} yearPlaceholder
33037  * @cfg {string} dayFormat default 'd'
33038  * @cfg {string} monthFormat default 'm'
33039  * @cfg {string} yearFormat default 'Y'
33040  * @cfg {Number} labellg set the width of label (1-12)
33041  * @cfg {Number} labelmd set the width of label (1-12)
33042  * @cfg {Number} labelsm set the width of label (1-12)
33043  * @cfg {Number} labelxs set the width of label (1-12)
33044
33045  *     
33046  * @constructor
33047  * Create a new DateSplitField
33048  * @param {Object} config The config object
33049  */
33050
33051 Roo.bootstrap.DateSplitField = function(config){
33052     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33053     
33054     this.addEvents({
33055         // raw events
33056          /**
33057          * @event years
33058          * getting the data of years
33059          * @param {Roo.bootstrap.DateSplitField} this
33060          * @param {Object} years
33061          */
33062         "years" : true,
33063         /**
33064          * @event days
33065          * getting the data of days
33066          * @param {Roo.bootstrap.DateSplitField} this
33067          * @param {Object} days
33068          */
33069         "days" : true,
33070         /**
33071          * @event invalid
33072          * Fires after the field has been marked as invalid.
33073          * @param {Roo.form.Field} this
33074          * @param {String} msg The validation message
33075          */
33076         invalid : true,
33077        /**
33078          * @event valid
33079          * Fires after the field has been validated with no errors.
33080          * @param {Roo.form.Field} this
33081          */
33082         valid : true
33083     });
33084 };
33085
33086 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33087     
33088     fieldLabel : '',
33089     labelAlign : 'top',
33090     labelWidth : 3,
33091     dayAllowBlank : false,
33092     monthAllowBlank : false,
33093     yearAllowBlank : false,
33094     dayPlaceholder : '',
33095     monthPlaceholder : '',
33096     yearPlaceholder : '',
33097     dayFormat : 'd',
33098     monthFormat : 'm',
33099     yearFormat : 'Y',
33100     isFormField : true,
33101     labellg : 0,
33102     labelmd : 0,
33103     labelsm : 0,
33104     labelxs : 0,
33105     
33106     getAutoCreate : function()
33107     {
33108         var cfg = {
33109             tag : 'div',
33110             cls : 'row roo-date-split-field-group',
33111             cn : [
33112                 {
33113                     tag : 'input',
33114                     type : 'hidden',
33115                     cls : 'form-hidden-field roo-date-split-field-group-value',
33116                     name : this.name
33117                 }
33118             ]
33119         };
33120         
33121         var labelCls = 'col-md-12';
33122         var contentCls = 'col-md-4';
33123         
33124         if(this.fieldLabel){
33125             
33126             var label = {
33127                 tag : 'div',
33128                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33129                 cn : [
33130                     {
33131                         tag : 'label',
33132                         html : this.fieldLabel
33133                     }
33134                 ]
33135             };
33136             
33137             if(this.labelAlign == 'left'){
33138             
33139                 if(this.labelWidth > 12){
33140                     label.style = "width: " + this.labelWidth + 'px';
33141                 }
33142
33143                 if(this.labelWidth < 13 && this.labelmd == 0){
33144                     this.labelmd = this.labelWidth;
33145                 }
33146
33147                 if(this.labellg > 0){
33148                     labelCls = ' col-lg-' + this.labellg;
33149                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33150                 }
33151
33152                 if(this.labelmd > 0){
33153                     labelCls = ' col-md-' + this.labelmd;
33154                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33155                 }
33156
33157                 if(this.labelsm > 0){
33158                     labelCls = ' col-sm-' + this.labelsm;
33159                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33160                 }
33161
33162                 if(this.labelxs > 0){
33163                     labelCls = ' col-xs-' + this.labelxs;
33164                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33165                 }
33166             }
33167             
33168             label.cls += ' ' + labelCls;
33169             
33170             cfg.cn.push(label);
33171         }
33172         
33173         Roo.each(['day', 'month', 'year'], function(t){
33174             cfg.cn.push({
33175                 tag : 'div',
33176                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33177             });
33178         }, this);
33179         
33180         return cfg;
33181     },
33182     
33183     inputEl: function ()
33184     {
33185         return this.el.select('.roo-date-split-field-group-value', true).first();
33186     },
33187     
33188     onRender : function(ct, position) 
33189     {
33190         var _this = this;
33191         
33192         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33193         
33194         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33195         
33196         this.dayField = new Roo.bootstrap.ComboBox({
33197             allowBlank : this.dayAllowBlank,
33198             alwaysQuery : true,
33199             displayField : 'value',
33200             editable : false,
33201             fieldLabel : '',
33202             forceSelection : true,
33203             mode : 'local',
33204             placeholder : this.dayPlaceholder,
33205             selectOnFocus : true,
33206             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33207             triggerAction : 'all',
33208             typeAhead : true,
33209             valueField : 'value',
33210             store : new Roo.data.SimpleStore({
33211                 data : (function() {    
33212                     var days = [];
33213                     _this.fireEvent('days', _this, days);
33214                     return days;
33215                 })(),
33216                 fields : [ 'value' ]
33217             }),
33218             listeners : {
33219                 select : function (_self, record, index)
33220                 {
33221                     _this.setValue(_this.getValue());
33222                 }
33223             }
33224         });
33225
33226         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33227         
33228         this.monthField = new Roo.bootstrap.MonthField({
33229             after : '<i class=\"fa fa-calendar\"></i>',
33230             allowBlank : this.monthAllowBlank,
33231             placeholder : this.monthPlaceholder,
33232             readOnly : true,
33233             listeners : {
33234                 render : function (_self)
33235                 {
33236                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33237                         e.preventDefault();
33238                         _self.focus();
33239                     });
33240                 },
33241                 select : function (_self, oldvalue, newvalue)
33242                 {
33243                     _this.setValue(_this.getValue());
33244                 }
33245             }
33246         });
33247         
33248         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33249         
33250         this.yearField = new Roo.bootstrap.ComboBox({
33251             allowBlank : this.yearAllowBlank,
33252             alwaysQuery : true,
33253             displayField : 'value',
33254             editable : false,
33255             fieldLabel : '',
33256             forceSelection : true,
33257             mode : 'local',
33258             placeholder : this.yearPlaceholder,
33259             selectOnFocus : true,
33260             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33261             triggerAction : 'all',
33262             typeAhead : true,
33263             valueField : 'value',
33264             store : new Roo.data.SimpleStore({
33265                 data : (function() {
33266                     var years = [];
33267                     _this.fireEvent('years', _this, years);
33268                     return years;
33269                 })(),
33270                 fields : [ 'value' ]
33271             }),
33272             listeners : {
33273                 select : function (_self, record, index)
33274                 {
33275                     _this.setValue(_this.getValue());
33276                 }
33277             }
33278         });
33279
33280         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33281     },
33282     
33283     setValue : function(v, format)
33284     {
33285         this.inputEl.dom.value = v;
33286         
33287         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33288         
33289         var d = Date.parseDate(v, f);
33290         
33291         if(!d){
33292             this.validate();
33293             return;
33294         }
33295         
33296         this.setDay(d.format(this.dayFormat));
33297         this.setMonth(d.format(this.monthFormat));
33298         this.setYear(d.format(this.yearFormat));
33299         
33300         this.validate();
33301         
33302         return;
33303     },
33304     
33305     setDay : function(v)
33306     {
33307         this.dayField.setValue(v);
33308         this.inputEl.dom.value = this.getValue();
33309         this.validate();
33310         return;
33311     },
33312     
33313     setMonth : function(v)
33314     {
33315         this.monthField.setValue(v, true);
33316         this.inputEl.dom.value = this.getValue();
33317         this.validate();
33318         return;
33319     },
33320     
33321     setYear : function(v)
33322     {
33323         this.yearField.setValue(v);
33324         this.inputEl.dom.value = this.getValue();
33325         this.validate();
33326         return;
33327     },
33328     
33329     getDay : function()
33330     {
33331         return this.dayField.getValue();
33332     },
33333     
33334     getMonth : function()
33335     {
33336         return this.monthField.getValue();
33337     },
33338     
33339     getYear : function()
33340     {
33341         return this.yearField.getValue();
33342     },
33343     
33344     getValue : function()
33345     {
33346         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33347         
33348         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33349         
33350         return date;
33351     },
33352     
33353     reset : function()
33354     {
33355         this.setDay('');
33356         this.setMonth('');
33357         this.setYear('');
33358         this.inputEl.dom.value = '';
33359         this.validate();
33360         return;
33361     },
33362     
33363     validate : function()
33364     {
33365         var d = this.dayField.validate();
33366         var m = this.monthField.validate();
33367         var y = this.yearField.validate();
33368         
33369         var valid = true;
33370         
33371         if(
33372                 (!this.dayAllowBlank && !d) ||
33373                 (!this.monthAllowBlank && !m) ||
33374                 (!this.yearAllowBlank && !y)
33375         ){
33376             valid = false;
33377         }
33378         
33379         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33380             return valid;
33381         }
33382         
33383         if(valid){
33384             this.markValid();
33385             return valid;
33386         }
33387         
33388         this.markInvalid();
33389         
33390         return valid;
33391     },
33392     
33393     markValid : function()
33394     {
33395         
33396         var label = this.el.select('label', true).first();
33397         var icon = this.el.select('i.fa-star', true).first();
33398
33399         if(label && icon){
33400             icon.remove();
33401         }
33402         
33403         this.fireEvent('valid', this);
33404     },
33405     
33406      /**
33407      * Mark this field as invalid
33408      * @param {String} msg The validation message
33409      */
33410     markInvalid : function(msg)
33411     {
33412         
33413         var label = this.el.select('label', true).first();
33414         var icon = this.el.select('i.fa-star', true).first();
33415
33416         if(label && !icon){
33417             this.el.select('.roo-date-split-field-label', true).createChild({
33418                 tag : 'i',
33419                 cls : 'text-danger fa fa-lg fa-star',
33420                 tooltip : 'This field is required',
33421                 style : 'margin-right:5px;'
33422             }, label, true);
33423         }
33424         
33425         this.fireEvent('invalid', this, msg);
33426     },
33427     
33428     clearInvalid : function()
33429     {
33430         var label = this.el.select('label', true).first();
33431         var icon = this.el.select('i.fa-star', true).first();
33432
33433         if(label && icon){
33434             icon.remove();
33435         }
33436         
33437         this.fireEvent('valid', this);
33438     },
33439     
33440     getName: function()
33441     {
33442         return this.name;
33443     }
33444     
33445 });
33446
33447  /**
33448  *
33449  * This is based on 
33450  * http://masonry.desandro.com
33451  *
33452  * The idea is to render all the bricks based on vertical width...
33453  *
33454  * The original code extends 'outlayer' - we might need to use that....
33455  * 
33456  */
33457
33458
33459 /**
33460  * @class Roo.bootstrap.LayoutMasonry
33461  * @extends Roo.bootstrap.Component
33462  * Bootstrap Layout Masonry class
33463  * 
33464  * @constructor
33465  * Create a new Element
33466  * @param {Object} config The config object
33467  */
33468
33469 Roo.bootstrap.LayoutMasonry = function(config){
33470     
33471     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33472     
33473     this.bricks = [];
33474     
33475     Roo.bootstrap.LayoutMasonry.register(this);
33476     
33477     this.addEvents({
33478         // raw events
33479         /**
33480          * @event layout
33481          * Fire after layout the items
33482          * @param {Roo.bootstrap.LayoutMasonry} this
33483          * @param {Roo.EventObject} e
33484          */
33485         "layout" : true
33486     });
33487     
33488 };
33489
33490 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33491     
33492     /**
33493      * @cfg {Boolean} isLayoutInstant = no animation?
33494      */   
33495     isLayoutInstant : false, // needed?
33496    
33497     /**
33498      * @cfg {Number} boxWidth  width of the columns
33499      */   
33500     boxWidth : 450,
33501     
33502       /**
33503      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33504      */   
33505     boxHeight : 0,
33506     
33507     /**
33508      * @cfg {Number} padWidth padding below box..
33509      */   
33510     padWidth : 10, 
33511     
33512     /**
33513      * @cfg {Number} gutter gutter width..
33514      */   
33515     gutter : 10,
33516     
33517      /**
33518      * @cfg {Number} maxCols maximum number of columns
33519      */   
33520     
33521     maxCols: 0,
33522     
33523     /**
33524      * @cfg {Boolean} isAutoInitial defalut true
33525      */   
33526     isAutoInitial : true, 
33527     
33528     containerWidth: 0,
33529     
33530     /**
33531      * @cfg {Boolean} isHorizontal defalut false
33532      */   
33533     isHorizontal : false, 
33534
33535     currentSize : null,
33536     
33537     tag: 'div',
33538     
33539     cls: '',
33540     
33541     bricks: null, //CompositeElement
33542     
33543     cols : 1,
33544     
33545     _isLayoutInited : false,
33546     
33547 //    isAlternative : false, // only use for vertical layout...
33548     
33549     /**
33550      * @cfg {Number} alternativePadWidth padding below box..
33551      */   
33552     alternativePadWidth : 50,
33553     
33554     selectedBrick : [],
33555     
33556     getAutoCreate : function(){
33557         
33558         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33559         
33560         var cfg = {
33561             tag: this.tag,
33562             cls: 'blog-masonary-wrapper ' + this.cls,
33563             cn : {
33564                 cls : 'mas-boxes masonary'
33565             }
33566         };
33567         
33568         return cfg;
33569     },
33570     
33571     getChildContainer: function( )
33572     {
33573         if (this.boxesEl) {
33574             return this.boxesEl;
33575         }
33576         
33577         this.boxesEl = this.el.select('.mas-boxes').first();
33578         
33579         return this.boxesEl;
33580     },
33581     
33582     
33583     initEvents : function()
33584     {
33585         var _this = this;
33586         
33587         if(this.isAutoInitial){
33588             Roo.log('hook children rendered');
33589             this.on('childrenrendered', function() {
33590                 Roo.log('children rendered');
33591                 _this.initial();
33592             } ,this);
33593         }
33594     },
33595     
33596     initial : function()
33597     {
33598         this.selectedBrick = [];
33599         
33600         this.currentSize = this.el.getBox(true);
33601         
33602         Roo.EventManager.onWindowResize(this.resize, this); 
33603
33604         if(!this.isAutoInitial){
33605             this.layout();
33606             return;
33607         }
33608         
33609         this.layout();
33610         
33611         return;
33612         //this.layout.defer(500,this);
33613         
33614     },
33615     
33616     resize : function()
33617     {
33618         var cs = this.el.getBox(true);
33619         
33620         if (
33621                 this.currentSize.width == cs.width && 
33622                 this.currentSize.x == cs.x && 
33623                 this.currentSize.height == cs.height && 
33624                 this.currentSize.y == cs.y 
33625         ) {
33626             Roo.log("no change in with or X or Y");
33627             return;
33628         }
33629         
33630         this.currentSize = cs;
33631         
33632         this.layout();
33633         
33634     },
33635     
33636     layout : function()
33637     {   
33638         this._resetLayout();
33639         
33640         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33641         
33642         this.layoutItems( isInstant );
33643       
33644         this._isLayoutInited = true;
33645         
33646         this.fireEvent('layout', this);
33647         
33648     },
33649     
33650     _resetLayout : function()
33651     {
33652         if(this.isHorizontal){
33653             this.horizontalMeasureColumns();
33654             return;
33655         }
33656         
33657         this.verticalMeasureColumns();
33658         
33659     },
33660     
33661     verticalMeasureColumns : function()
33662     {
33663         this.getContainerWidth();
33664         
33665 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33666 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33667 //            return;
33668 //        }
33669         
33670         var boxWidth = this.boxWidth + this.padWidth;
33671         
33672         if(this.containerWidth < this.boxWidth){
33673             boxWidth = this.containerWidth
33674         }
33675         
33676         var containerWidth = this.containerWidth;
33677         
33678         var cols = Math.floor(containerWidth / boxWidth);
33679         
33680         this.cols = Math.max( cols, 1 );
33681         
33682         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33683         
33684         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33685         
33686         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33687         
33688         this.colWidth = boxWidth + avail - this.padWidth;
33689         
33690         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33691         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33692     },
33693     
33694     horizontalMeasureColumns : function()
33695     {
33696         this.getContainerWidth();
33697         
33698         var boxWidth = this.boxWidth;
33699         
33700         if(this.containerWidth < boxWidth){
33701             boxWidth = this.containerWidth;
33702         }
33703         
33704         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33705         
33706         this.el.setHeight(boxWidth);
33707         
33708     },
33709     
33710     getContainerWidth : function()
33711     {
33712         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33713     },
33714     
33715     layoutItems : function( isInstant )
33716     {
33717         Roo.log(this.bricks);
33718         
33719         var items = Roo.apply([], this.bricks);
33720         
33721         if(this.isHorizontal){
33722             this._horizontalLayoutItems( items , isInstant );
33723             return;
33724         }
33725         
33726 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33727 //            this._verticalAlternativeLayoutItems( items , isInstant );
33728 //            return;
33729 //        }
33730         
33731         this._verticalLayoutItems( items , isInstant );
33732         
33733     },
33734     
33735     _verticalLayoutItems : function ( items , isInstant)
33736     {
33737         if ( !items || !items.length ) {
33738             return;
33739         }
33740         
33741         var standard = [
33742             ['xs', 'xs', 'xs', 'tall'],
33743             ['xs', 'xs', 'tall'],
33744             ['xs', 'xs', 'sm'],
33745             ['xs', 'xs', 'xs'],
33746             ['xs', 'tall'],
33747             ['xs', 'sm'],
33748             ['xs', 'xs'],
33749             ['xs'],
33750             
33751             ['sm', 'xs', 'xs'],
33752             ['sm', 'xs'],
33753             ['sm'],
33754             
33755             ['tall', 'xs', 'xs', 'xs'],
33756             ['tall', 'xs', 'xs'],
33757             ['tall', 'xs'],
33758             ['tall']
33759             
33760         ];
33761         
33762         var queue = [];
33763         
33764         var boxes = [];
33765         
33766         var box = [];
33767         
33768         Roo.each(items, function(item, k){
33769             
33770             switch (item.size) {
33771                 // these layouts take up a full box,
33772                 case 'md' :
33773                 case 'md-left' :
33774                 case 'md-right' :
33775                 case 'wide' :
33776                     
33777                     if(box.length){
33778                         boxes.push(box);
33779                         box = [];
33780                     }
33781                     
33782                     boxes.push([item]);
33783                     
33784                     break;
33785                     
33786                 case 'xs' :
33787                 case 'sm' :
33788                 case 'tall' :
33789                     
33790                     box.push(item);
33791                     
33792                     break;
33793                 default :
33794                     break;
33795                     
33796             }
33797             
33798         }, this);
33799         
33800         if(box.length){
33801             boxes.push(box);
33802             box = [];
33803         }
33804         
33805         var filterPattern = function(box, length)
33806         {
33807             if(!box.length){
33808                 return;
33809             }
33810             
33811             var match = false;
33812             
33813             var pattern = box.slice(0, length);
33814             
33815             var format = [];
33816             
33817             Roo.each(pattern, function(i){
33818                 format.push(i.size);
33819             }, this);
33820             
33821             Roo.each(standard, function(s){
33822                 
33823                 if(String(s) != String(format)){
33824                     return;
33825                 }
33826                 
33827                 match = true;
33828                 return false;
33829                 
33830             }, this);
33831             
33832             if(!match && length == 1){
33833                 return;
33834             }
33835             
33836             if(!match){
33837                 filterPattern(box, length - 1);
33838                 return;
33839             }
33840                 
33841             queue.push(pattern);
33842
33843             box = box.slice(length, box.length);
33844
33845             filterPattern(box, 4);
33846
33847             return;
33848             
33849         }
33850         
33851         Roo.each(boxes, function(box, k){
33852             
33853             if(!box.length){
33854                 return;
33855             }
33856             
33857             if(box.length == 1){
33858                 queue.push(box);
33859                 return;
33860             }
33861             
33862             filterPattern(box, 4);
33863             
33864         }, this);
33865         
33866         this._processVerticalLayoutQueue( queue, isInstant );
33867         
33868     },
33869     
33870 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33871 //    {
33872 //        if ( !items || !items.length ) {
33873 //            return;
33874 //        }
33875 //
33876 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33877 //        
33878 //    },
33879     
33880     _horizontalLayoutItems : function ( items , isInstant)
33881     {
33882         if ( !items || !items.length || items.length < 3) {
33883             return;
33884         }
33885         
33886         items.reverse();
33887         
33888         var eItems = items.slice(0, 3);
33889         
33890         items = items.slice(3, items.length);
33891         
33892         var standard = [
33893             ['xs', 'xs', 'xs', 'wide'],
33894             ['xs', 'xs', 'wide'],
33895             ['xs', 'xs', 'sm'],
33896             ['xs', 'xs', 'xs'],
33897             ['xs', 'wide'],
33898             ['xs', 'sm'],
33899             ['xs', 'xs'],
33900             ['xs'],
33901             
33902             ['sm', 'xs', 'xs'],
33903             ['sm', 'xs'],
33904             ['sm'],
33905             
33906             ['wide', 'xs', 'xs', 'xs'],
33907             ['wide', 'xs', 'xs'],
33908             ['wide', 'xs'],
33909             ['wide'],
33910             
33911             ['wide-thin']
33912         ];
33913         
33914         var queue = [];
33915         
33916         var boxes = [];
33917         
33918         var box = [];
33919         
33920         Roo.each(items, function(item, k){
33921             
33922             switch (item.size) {
33923                 case 'md' :
33924                 case 'md-left' :
33925                 case 'md-right' :
33926                 case 'tall' :
33927                     
33928                     if(box.length){
33929                         boxes.push(box);
33930                         box = [];
33931                     }
33932                     
33933                     boxes.push([item]);
33934                     
33935                     break;
33936                     
33937                 case 'xs' :
33938                 case 'sm' :
33939                 case 'wide' :
33940                 case 'wide-thin' :
33941                     
33942                     box.push(item);
33943                     
33944                     break;
33945                 default :
33946                     break;
33947                     
33948             }
33949             
33950         }, this);
33951         
33952         if(box.length){
33953             boxes.push(box);
33954             box = [];
33955         }
33956         
33957         var filterPattern = function(box, length)
33958         {
33959             if(!box.length){
33960                 return;
33961             }
33962             
33963             var match = false;
33964             
33965             var pattern = box.slice(0, length);
33966             
33967             var format = [];
33968             
33969             Roo.each(pattern, function(i){
33970                 format.push(i.size);
33971             }, this);
33972             
33973             Roo.each(standard, function(s){
33974                 
33975                 if(String(s) != String(format)){
33976                     return;
33977                 }
33978                 
33979                 match = true;
33980                 return false;
33981                 
33982             }, this);
33983             
33984             if(!match && length == 1){
33985                 return;
33986             }
33987             
33988             if(!match){
33989                 filterPattern(box, length - 1);
33990                 return;
33991             }
33992                 
33993             queue.push(pattern);
33994
33995             box = box.slice(length, box.length);
33996
33997             filterPattern(box, 4);
33998
33999             return;
34000             
34001         }
34002         
34003         Roo.each(boxes, function(box, k){
34004             
34005             if(!box.length){
34006                 return;
34007             }
34008             
34009             if(box.length == 1){
34010                 queue.push(box);
34011                 return;
34012             }
34013             
34014             filterPattern(box, 4);
34015             
34016         }, this);
34017         
34018         
34019         var prune = [];
34020         
34021         var pos = this.el.getBox(true);
34022         
34023         var minX = pos.x;
34024         
34025         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34026         
34027         var hit_end = false;
34028         
34029         Roo.each(queue, function(box){
34030             
34031             if(hit_end){
34032                 
34033                 Roo.each(box, function(b){
34034                 
34035                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34036                     b.el.hide();
34037
34038                 }, this);
34039
34040                 return;
34041             }
34042             
34043             var mx = 0;
34044             
34045             Roo.each(box, function(b){
34046                 
34047                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34048                 b.el.show();
34049
34050                 mx = Math.max(mx, b.x);
34051                 
34052             }, this);
34053             
34054             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34055             
34056             if(maxX < minX){
34057                 
34058                 Roo.each(box, function(b){
34059                 
34060                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34061                     b.el.hide();
34062                     
34063                 }, this);
34064                 
34065                 hit_end = true;
34066                 
34067                 return;
34068             }
34069             
34070             prune.push(box);
34071             
34072         }, this);
34073         
34074         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34075     },
34076     
34077     /** Sets position of item in DOM
34078     * @param {Element} item
34079     * @param {Number} x - horizontal position
34080     * @param {Number} y - vertical position
34081     * @param {Boolean} isInstant - disables transitions
34082     */
34083     _processVerticalLayoutQueue : function( queue, isInstant )
34084     {
34085         var pos = this.el.getBox(true);
34086         var x = pos.x;
34087         var y = pos.y;
34088         var maxY = [];
34089         
34090         for (var i = 0; i < this.cols; i++){
34091             maxY[i] = pos.y;
34092         }
34093         
34094         Roo.each(queue, function(box, k){
34095             
34096             var col = k % this.cols;
34097             
34098             Roo.each(box, function(b,kk){
34099                 
34100                 b.el.position('absolute');
34101                 
34102                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34103                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34104                 
34105                 if(b.size == 'md-left' || b.size == 'md-right'){
34106                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34107                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34108                 }
34109                 
34110                 b.el.setWidth(width);
34111                 b.el.setHeight(height);
34112                 // iframe?
34113                 b.el.select('iframe',true).setSize(width,height);
34114                 
34115             }, this);
34116             
34117             for (var i = 0; i < this.cols; i++){
34118                 
34119                 if(maxY[i] < maxY[col]){
34120                     col = i;
34121                     continue;
34122                 }
34123                 
34124                 col = Math.min(col, i);
34125                 
34126             }
34127             
34128             x = pos.x + col * (this.colWidth + this.padWidth);
34129             
34130             y = maxY[col];
34131             
34132             var positions = [];
34133             
34134             switch (box.length){
34135                 case 1 :
34136                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34137                     break;
34138                 case 2 :
34139                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34140                     break;
34141                 case 3 :
34142                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34143                     break;
34144                 case 4 :
34145                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34146                     break;
34147                 default :
34148                     break;
34149             }
34150             
34151             Roo.each(box, function(b,kk){
34152                 
34153                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34154                 
34155                 var sz = b.el.getSize();
34156                 
34157                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34158                 
34159             }, this);
34160             
34161         }, this);
34162         
34163         var mY = 0;
34164         
34165         for (var i = 0; i < this.cols; i++){
34166             mY = Math.max(mY, maxY[i]);
34167         }
34168         
34169         this.el.setHeight(mY - pos.y);
34170         
34171     },
34172     
34173 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34174 //    {
34175 //        var pos = this.el.getBox(true);
34176 //        var x = pos.x;
34177 //        var y = pos.y;
34178 //        var maxX = pos.right;
34179 //        
34180 //        var maxHeight = 0;
34181 //        
34182 //        Roo.each(items, function(item, k){
34183 //            
34184 //            var c = k % 2;
34185 //            
34186 //            item.el.position('absolute');
34187 //                
34188 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34189 //
34190 //            item.el.setWidth(width);
34191 //
34192 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34193 //
34194 //            item.el.setHeight(height);
34195 //            
34196 //            if(c == 0){
34197 //                item.el.setXY([x, y], isInstant ? false : true);
34198 //            } else {
34199 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34200 //            }
34201 //            
34202 //            y = y + height + this.alternativePadWidth;
34203 //            
34204 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34205 //            
34206 //        }, this);
34207 //        
34208 //        this.el.setHeight(maxHeight);
34209 //        
34210 //    },
34211     
34212     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34213     {
34214         var pos = this.el.getBox(true);
34215         
34216         var minX = pos.x;
34217         var minY = pos.y;
34218         
34219         var maxX = pos.right;
34220         
34221         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34222         
34223         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34224         
34225         Roo.each(queue, function(box, k){
34226             
34227             Roo.each(box, function(b, kk){
34228                 
34229                 b.el.position('absolute');
34230                 
34231                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34232                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34233                 
34234                 if(b.size == 'md-left' || b.size == 'md-right'){
34235                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34236                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34237                 }
34238                 
34239                 b.el.setWidth(width);
34240                 b.el.setHeight(height);
34241                 
34242             }, this);
34243             
34244             if(!box.length){
34245                 return;
34246             }
34247             
34248             var positions = [];
34249             
34250             switch (box.length){
34251                 case 1 :
34252                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34253                     break;
34254                 case 2 :
34255                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34256                     break;
34257                 case 3 :
34258                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34259                     break;
34260                 case 4 :
34261                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34262                     break;
34263                 default :
34264                     break;
34265             }
34266             
34267             Roo.each(box, function(b,kk){
34268                 
34269                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34270                 
34271                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34272                 
34273             }, this);
34274             
34275         }, this);
34276         
34277     },
34278     
34279     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34280     {
34281         Roo.each(eItems, function(b,k){
34282             
34283             b.size = (k == 0) ? 'sm' : 'xs';
34284             b.x = (k == 0) ? 2 : 1;
34285             b.y = (k == 0) ? 2 : 1;
34286             
34287             b.el.position('absolute');
34288             
34289             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34290                 
34291             b.el.setWidth(width);
34292             
34293             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34294             
34295             b.el.setHeight(height);
34296             
34297         }, this);
34298
34299         var positions = [];
34300         
34301         positions.push({
34302             x : maxX - this.unitWidth * 2 - this.gutter,
34303             y : minY
34304         });
34305         
34306         positions.push({
34307             x : maxX - this.unitWidth,
34308             y : minY + (this.unitWidth + this.gutter) * 2
34309         });
34310         
34311         positions.push({
34312             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34313             y : minY
34314         });
34315         
34316         Roo.each(eItems, function(b,k){
34317             
34318             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34319
34320         }, this);
34321         
34322     },
34323     
34324     getVerticalOneBoxColPositions : function(x, y, box)
34325     {
34326         var pos = [];
34327         
34328         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34329         
34330         if(box[0].size == 'md-left'){
34331             rand = 0;
34332         }
34333         
34334         if(box[0].size == 'md-right'){
34335             rand = 1;
34336         }
34337         
34338         pos.push({
34339             x : x + (this.unitWidth + this.gutter) * rand,
34340             y : y
34341         });
34342         
34343         return pos;
34344     },
34345     
34346     getVerticalTwoBoxColPositions : function(x, y, box)
34347     {
34348         var pos = [];
34349         
34350         if(box[0].size == 'xs'){
34351             
34352             pos.push({
34353                 x : x,
34354                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34355             });
34356
34357             pos.push({
34358                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34359                 y : y
34360             });
34361             
34362             return pos;
34363             
34364         }
34365         
34366         pos.push({
34367             x : x,
34368             y : y
34369         });
34370
34371         pos.push({
34372             x : x + (this.unitWidth + this.gutter) * 2,
34373             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34374         });
34375         
34376         return pos;
34377         
34378     },
34379     
34380     getVerticalThreeBoxColPositions : function(x, y, box)
34381     {
34382         var pos = [];
34383         
34384         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34385             
34386             pos.push({
34387                 x : x,
34388                 y : y
34389             });
34390
34391             pos.push({
34392                 x : x + (this.unitWidth + this.gutter) * 1,
34393                 y : y
34394             });
34395             
34396             pos.push({
34397                 x : x + (this.unitWidth + this.gutter) * 2,
34398                 y : y
34399             });
34400             
34401             return pos;
34402             
34403         }
34404         
34405         if(box[0].size == 'xs' && box[1].size == 'xs'){
34406             
34407             pos.push({
34408                 x : x,
34409                 y : y
34410             });
34411
34412             pos.push({
34413                 x : x,
34414                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34415             });
34416             
34417             pos.push({
34418                 x : x + (this.unitWidth + this.gutter) * 1,
34419                 y : y
34420             });
34421             
34422             return pos;
34423             
34424         }
34425         
34426         pos.push({
34427             x : x,
34428             y : y
34429         });
34430
34431         pos.push({
34432             x : x + (this.unitWidth + this.gutter) * 2,
34433             y : y
34434         });
34435
34436         pos.push({
34437             x : x + (this.unitWidth + this.gutter) * 2,
34438             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34439         });
34440             
34441         return pos;
34442         
34443     },
34444     
34445     getVerticalFourBoxColPositions : function(x, y, box)
34446     {
34447         var pos = [];
34448         
34449         if(box[0].size == 'xs'){
34450             
34451             pos.push({
34452                 x : x,
34453                 y : y
34454             });
34455
34456             pos.push({
34457                 x : x,
34458                 y : y + (this.unitHeight + this.gutter) * 1
34459             });
34460             
34461             pos.push({
34462                 x : x,
34463                 y : y + (this.unitHeight + this.gutter) * 2
34464             });
34465             
34466             pos.push({
34467                 x : x + (this.unitWidth + this.gutter) * 1,
34468                 y : y
34469             });
34470             
34471             return pos;
34472             
34473         }
34474         
34475         pos.push({
34476             x : x,
34477             y : y
34478         });
34479
34480         pos.push({
34481             x : x + (this.unitWidth + this.gutter) * 2,
34482             y : y
34483         });
34484
34485         pos.push({
34486             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34487             y : y + (this.unitHeight + this.gutter) * 1
34488         });
34489
34490         pos.push({
34491             x : x + (this.unitWidth + this.gutter) * 2,
34492             y : y + (this.unitWidth + this.gutter) * 2
34493         });
34494
34495         return pos;
34496         
34497     },
34498     
34499     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34500     {
34501         var pos = [];
34502         
34503         if(box[0].size == 'md-left'){
34504             pos.push({
34505                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34506                 y : minY
34507             });
34508             
34509             return pos;
34510         }
34511         
34512         if(box[0].size == 'md-right'){
34513             pos.push({
34514                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34515                 y : minY + (this.unitWidth + this.gutter) * 1
34516             });
34517             
34518             return pos;
34519         }
34520         
34521         var rand = Math.floor(Math.random() * (4 - box[0].y));
34522         
34523         pos.push({
34524             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34525             y : minY + (this.unitWidth + this.gutter) * rand
34526         });
34527         
34528         return pos;
34529         
34530     },
34531     
34532     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34533     {
34534         var pos = [];
34535         
34536         if(box[0].size == 'xs'){
34537             
34538             pos.push({
34539                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34540                 y : minY
34541             });
34542
34543             pos.push({
34544                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34545                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34546             });
34547             
34548             return pos;
34549             
34550         }
34551         
34552         pos.push({
34553             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34554             y : minY
34555         });
34556
34557         pos.push({
34558             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34559             y : minY + (this.unitWidth + this.gutter) * 2
34560         });
34561         
34562         return pos;
34563         
34564     },
34565     
34566     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34567     {
34568         var pos = [];
34569         
34570         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34571             
34572             pos.push({
34573                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34574                 y : minY
34575             });
34576
34577             pos.push({
34578                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34579                 y : minY + (this.unitWidth + this.gutter) * 1
34580             });
34581             
34582             pos.push({
34583                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34584                 y : minY + (this.unitWidth + this.gutter) * 2
34585             });
34586             
34587             return pos;
34588             
34589         }
34590         
34591         if(box[0].size == 'xs' && box[1].size == 'xs'){
34592             
34593             pos.push({
34594                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34595                 y : minY
34596             });
34597
34598             pos.push({
34599                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34600                 y : minY
34601             });
34602             
34603             pos.push({
34604                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34605                 y : minY + (this.unitWidth + this.gutter) * 1
34606             });
34607             
34608             return pos;
34609             
34610         }
34611         
34612         pos.push({
34613             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34614             y : minY
34615         });
34616
34617         pos.push({
34618             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34619             y : minY + (this.unitWidth + this.gutter) * 2
34620         });
34621
34622         pos.push({
34623             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34624             y : minY + (this.unitWidth + this.gutter) * 2
34625         });
34626             
34627         return pos;
34628         
34629     },
34630     
34631     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34632     {
34633         var pos = [];
34634         
34635         if(box[0].size == 'xs'){
34636             
34637             pos.push({
34638                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34639                 y : minY
34640             });
34641
34642             pos.push({
34643                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34644                 y : minY
34645             });
34646             
34647             pos.push({
34648                 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),
34649                 y : minY
34650             });
34651             
34652             pos.push({
34653                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34654                 y : minY + (this.unitWidth + this.gutter) * 1
34655             });
34656             
34657             return pos;
34658             
34659         }
34660         
34661         pos.push({
34662             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34663             y : minY
34664         });
34665         
34666         pos.push({
34667             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34668             y : minY + (this.unitWidth + this.gutter) * 2
34669         });
34670         
34671         pos.push({
34672             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34673             y : minY + (this.unitWidth + this.gutter) * 2
34674         });
34675         
34676         pos.push({
34677             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),
34678             y : minY + (this.unitWidth + this.gutter) * 2
34679         });
34680
34681         return pos;
34682         
34683     },
34684     
34685     /**
34686     * remove a Masonry Brick
34687     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34688     */
34689     removeBrick : function(brick_id)
34690     {
34691         if (!brick_id) {
34692             return;
34693         }
34694         
34695         for (var i = 0; i<this.bricks.length; i++) {
34696             if (this.bricks[i].id == brick_id) {
34697                 this.bricks.splice(i,1);
34698                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34699                 this.initial();
34700             }
34701         }
34702     },
34703     
34704     /**
34705     * adds a Masonry Brick
34706     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34707     */
34708     addBrick : function(cfg)
34709     {
34710         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34711         //this.register(cn);
34712         cn.parentId = this.id;
34713         cn.render(this.el);
34714         return cn;
34715     },
34716     
34717     /**
34718     * register a Masonry Brick
34719     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34720     */
34721     
34722     register : function(brick)
34723     {
34724         this.bricks.push(brick);
34725         brick.masonryId = this.id;
34726     },
34727     
34728     /**
34729     * clear all the Masonry Brick
34730     */
34731     clearAll : function()
34732     {
34733         this.bricks = [];
34734         //this.getChildContainer().dom.innerHTML = "";
34735         this.el.dom.innerHTML = '';
34736     },
34737     
34738     getSelected : function()
34739     {
34740         if (!this.selectedBrick) {
34741             return false;
34742         }
34743         
34744         return this.selectedBrick;
34745     }
34746 });
34747
34748 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34749     
34750     groups: {},
34751      /**
34752     * register a Masonry Layout
34753     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34754     */
34755     
34756     register : function(layout)
34757     {
34758         this.groups[layout.id] = layout;
34759     },
34760     /**
34761     * fetch a  Masonry Layout based on the masonry layout ID
34762     * @param {string} the masonry layout to add
34763     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34764     */
34765     
34766     get: function(layout_id) {
34767         if (typeof(this.groups[layout_id]) == 'undefined') {
34768             return false;
34769         }
34770         return this.groups[layout_id] ;
34771     }
34772     
34773     
34774     
34775 });
34776
34777  
34778
34779  /**
34780  *
34781  * This is based on 
34782  * http://masonry.desandro.com
34783  *
34784  * The idea is to render all the bricks based on vertical width...
34785  *
34786  * The original code extends 'outlayer' - we might need to use that....
34787  * 
34788  */
34789
34790
34791 /**
34792  * @class Roo.bootstrap.LayoutMasonryAuto
34793  * @extends Roo.bootstrap.Component
34794  * Bootstrap Layout Masonry class
34795  * 
34796  * @constructor
34797  * Create a new Element
34798  * @param {Object} config The config object
34799  */
34800
34801 Roo.bootstrap.LayoutMasonryAuto = function(config){
34802     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34803 };
34804
34805 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34806     
34807       /**
34808      * @cfg {Boolean} isFitWidth  - resize the width..
34809      */   
34810     isFitWidth : false,  // options..
34811     /**
34812      * @cfg {Boolean} isOriginLeft = left align?
34813      */   
34814     isOriginLeft : true,
34815     /**
34816      * @cfg {Boolean} isOriginTop = top align?
34817      */   
34818     isOriginTop : false,
34819     /**
34820      * @cfg {Boolean} isLayoutInstant = no animation?
34821      */   
34822     isLayoutInstant : false, // needed?
34823     /**
34824      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34825      */   
34826     isResizingContainer : true,
34827     /**
34828      * @cfg {Number} columnWidth  width of the columns 
34829      */   
34830     
34831     columnWidth : 0,
34832     
34833     /**
34834      * @cfg {Number} maxCols maximum number of columns
34835      */   
34836     
34837     maxCols: 0,
34838     /**
34839      * @cfg {Number} padHeight padding below box..
34840      */   
34841     
34842     padHeight : 10, 
34843     
34844     /**
34845      * @cfg {Boolean} isAutoInitial defalut true
34846      */   
34847     
34848     isAutoInitial : true, 
34849     
34850     // private?
34851     gutter : 0,
34852     
34853     containerWidth: 0,
34854     initialColumnWidth : 0,
34855     currentSize : null,
34856     
34857     colYs : null, // array.
34858     maxY : 0,
34859     padWidth: 10,
34860     
34861     
34862     tag: 'div',
34863     cls: '',
34864     bricks: null, //CompositeElement
34865     cols : 0, // array?
34866     // element : null, // wrapped now this.el
34867     _isLayoutInited : null, 
34868     
34869     
34870     getAutoCreate : function(){
34871         
34872         var cfg = {
34873             tag: this.tag,
34874             cls: 'blog-masonary-wrapper ' + this.cls,
34875             cn : {
34876                 cls : 'mas-boxes masonary'
34877             }
34878         };
34879         
34880         return cfg;
34881     },
34882     
34883     getChildContainer: function( )
34884     {
34885         if (this.boxesEl) {
34886             return this.boxesEl;
34887         }
34888         
34889         this.boxesEl = this.el.select('.mas-boxes').first();
34890         
34891         return this.boxesEl;
34892     },
34893     
34894     
34895     initEvents : function()
34896     {
34897         var _this = this;
34898         
34899         if(this.isAutoInitial){
34900             Roo.log('hook children rendered');
34901             this.on('childrenrendered', function() {
34902                 Roo.log('children rendered');
34903                 _this.initial();
34904             } ,this);
34905         }
34906         
34907     },
34908     
34909     initial : function()
34910     {
34911         this.reloadItems();
34912
34913         this.currentSize = this.el.getBox(true);
34914
34915         /// was window resize... - let's see if this works..
34916         Roo.EventManager.onWindowResize(this.resize, this); 
34917
34918         if(!this.isAutoInitial){
34919             this.layout();
34920             return;
34921         }
34922         
34923         this.layout.defer(500,this);
34924     },
34925     
34926     reloadItems: function()
34927     {
34928         this.bricks = this.el.select('.masonry-brick', true);
34929         
34930         this.bricks.each(function(b) {
34931             //Roo.log(b.getSize());
34932             if (!b.attr('originalwidth')) {
34933                 b.attr('originalwidth',  b.getSize().width);
34934             }
34935             
34936         });
34937         
34938         Roo.log(this.bricks.elements.length);
34939     },
34940     
34941     resize : function()
34942     {
34943         Roo.log('resize');
34944         var cs = this.el.getBox(true);
34945         
34946         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34947             Roo.log("no change in with or X");
34948             return;
34949         }
34950         this.currentSize = cs;
34951         this.layout();
34952     },
34953     
34954     layout : function()
34955     {
34956          Roo.log('layout');
34957         this._resetLayout();
34958         //this._manageStamps();
34959       
34960         // don't animate first layout
34961         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34962         this.layoutItems( isInstant );
34963       
34964         // flag for initalized
34965         this._isLayoutInited = true;
34966     },
34967     
34968     layoutItems : function( isInstant )
34969     {
34970         //var items = this._getItemsForLayout( this.items );
34971         // original code supports filtering layout items.. we just ignore it..
34972         
34973         this._layoutItems( this.bricks , isInstant );
34974       
34975         this._postLayout();
34976     },
34977     _layoutItems : function ( items , isInstant)
34978     {
34979        //this.fireEvent( 'layout', this, items );
34980     
34981
34982         if ( !items || !items.elements.length ) {
34983           // no items, emit event with empty array
34984             return;
34985         }
34986
34987         var queue = [];
34988         items.each(function(item) {
34989             Roo.log("layout item");
34990             Roo.log(item);
34991             // get x/y object from method
34992             var position = this._getItemLayoutPosition( item );
34993             // enqueue
34994             position.item = item;
34995             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34996             queue.push( position );
34997         }, this);
34998       
34999         this._processLayoutQueue( queue );
35000     },
35001     /** Sets position of item in DOM
35002     * @param {Element} item
35003     * @param {Number} x - horizontal position
35004     * @param {Number} y - vertical position
35005     * @param {Boolean} isInstant - disables transitions
35006     */
35007     _processLayoutQueue : function( queue )
35008     {
35009         for ( var i=0, len = queue.length; i < len; i++ ) {
35010             var obj = queue[i];
35011             obj.item.position('absolute');
35012             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35013         }
35014     },
35015       
35016     
35017     /**
35018     * Any logic you want to do after each layout,
35019     * i.e. size the container
35020     */
35021     _postLayout : function()
35022     {
35023         this.resizeContainer();
35024     },
35025     
35026     resizeContainer : function()
35027     {
35028         if ( !this.isResizingContainer ) {
35029             return;
35030         }
35031         var size = this._getContainerSize();
35032         if ( size ) {
35033             this.el.setSize(size.width,size.height);
35034             this.boxesEl.setSize(size.width,size.height);
35035         }
35036     },
35037     
35038     
35039     
35040     _resetLayout : function()
35041     {
35042         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35043         this.colWidth = this.el.getWidth();
35044         //this.gutter = this.el.getWidth(); 
35045         
35046         this.measureColumns();
35047
35048         // reset column Y
35049         var i = this.cols;
35050         this.colYs = [];
35051         while (i--) {
35052             this.colYs.push( 0 );
35053         }
35054     
35055         this.maxY = 0;
35056     },
35057
35058     measureColumns : function()
35059     {
35060         this.getContainerWidth();
35061       // if columnWidth is 0, default to outerWidth of first item
35062         if ( !this.columnWidth ) {
35063             var firstItem = this.bricks.first();
35064             Roo.log(firstItem);
35065             this.columnWidth  = this.containerWidth;
35066             if (firstItem && firstItem.attr('originalwidth') ) {
35067                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35068             }
35069             // columnWidth fall back to item of first element
35070             Roo.log("set column width?");
35071                         this.initialColumnWidth = this.columnWidth  ;
35072
35073             // if first elem has no width, default to size of container
35074             
35075         }
35076         
35077         
35078         if (this.initialColumnWidth) {
35079             this.columnWidth = this.initialColumnWidth;
35080         }
35081         
35082         
35083             
35084         // column width is fixed at the top - however if container width get's smaller we should
35085         // reduce it...
35086         
35087         // this bit calcs how man columns..
35088             
35089         var columnWidth = this.columnWidth += this.gutter;
35090       
35091         // calculate columns
35092         var containerWidth = this.containerWidth + this.gutter;
35093         
35094         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35095         // fix rounding errors, typically with gutters
35096         var excess = columnWidth - containerWidth % columnWidth;
35097         
35098         
35099         // if overshoot is less than a pixel, round up, otherwise floor it
35100         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35101         cols = Math[ mathMethod ]( cols );
35102         this.cols = Math.max( cols, 1 );
35103         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35104         
35105          // padding positioning..
35106         var totalColWidth = this.cols * this.columnWidth;
35107         var padavail = this.containerWidth - totalColWidth;
35108         // so for 2 columns - we need 3 'pads'
35109         
35110         var padNeeded = (1+this.cols) * this.padWidth;
35111         
35112         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35113         
35114         this.columnWidth += padExtra
35115         //this.padWidth = Math.floor(padavail /  ( this.cols));
35116         
35117         // adjust colum width so that padding is fixed??
35118         
35119         // we have 3 columns ... total = width * 3
35120         // we have X left over... that should be used by 
35121         
35122         //if (this.expandC) {
35123             
35124         //}
35125         
35126         
35127         
35128     },
35129     
35130     getContainerWidth : function()
35131     {
35132        /* // container is parent if fit width
35133         var container = this.isFitWidth ? this.element.parentNode : this.element;
35134         // check that this.size and size are there
35135         // IE8 triggers resize on body size change, so they might not be
35136         
35137         var size = getSize( container );  //FIXME
35138         this.containerWidth = size && size.innerWidth; //FIXME
35139         */
35140          
35141         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35142         
35143     },
35144     
35145     _getItemLayoutPosition : function( item )  // what is item?
35146     {
35147         // we resize the item to our columnWidth..
35148       
35149         item.setWidth(this.columnWidth);
35150         item.autoBoxAdjust  = false;
35151         
35152         var sz = item.getSize();
35153  
35154         // how many columns does this brick span
35155         var remainder = this.containerWidth % this.columnWidth;
35156         
35157         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35158         // round if off by 1 pixel, otherwise use ceil
35159         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35160         colSpan = Math.min( colSpan, this.cols );
35161         
35162         // normally this should be '1' as we dont' currently allow multi width columns..
35163         
35164         var colGroup = this._getColGroup( colSpan );
35165         // get the minimum Y value from the columns
35166         var minimumY = Math.min.apply( Math, colGroup );
35167         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35168         
35169         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35170          
35171         // position the brick
35172         var position = {
35173             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35174             y: this.currentSize.y + minimumY + this.padHeight
35175         };
35176         
35177         Roo.log(position);
35178         // apply setHeight to necessary columns
35179         var setHeight = minimumY + sz.height + this.padHeight;
35180         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35181         
35182         var setSpan = this.cols + 1 - colGroup.length;
35183         for ( var i = 0; i < setSpan; i++ ) {
35184           this.colYs[ shortColIndex + i ] = setHeight ;
35185         }
35186       
35187         return position;
35188     },
35189     
35190     /**
35191      * @param {Number} colSpan - number of columns the element spans
35192      * @returns {Array} colGroup
35193      */
35194     _getColGroup : function( colSpan )
35195     {
35196         if ( colSpan < 2 ) {
35197           // if brick spans only one column, use all the column Ys
35198           return this.colYs;
35199         }
35200       
35201         var colGroup = [];
35202         // how many different places could this brick fit horizontally
35203         var groupCount = this.cols + 1 - colSpan;
35204         // for each group potential horizontal position
35205         for ( var i = 0; i < groupCount; i++ ) {
35206           // make an array of colY values for that one group
35207           var groupColYs = this.colYs.slice( i, i + colSpan );
35208           // and get the max value of the array
35209           colGroup[i] = Math.max.apply( Math, groupColYs );
35210         }
35211         return colGroup;
35212     },
35213     /*
35214     _manageStamp : function( stamp )
35215     {
35216         var stampSize =  stamp.getSize();
35217         var offset = stamp.getBox();
35218         // get the columns that this stamp affects
35219         var firstX = this.isOriginLeft ? offset.x : offset.right;
35220         var lastX = firstX + stampSize.width;
35221         var firstCol = Math.floor( firstX / this.columnWidth );
35222         firstCol = Math.max( 0, firstCol );
35223         
35224         var lastCol = Math.floor( lastX / this.columnWidth );
35225         // lastCol should not go over if multiple of columnWidth #425
35226         lastCol -= lastX % this.columnWidth ? 0 : 1;
35227         lastCol = Math.min( this.cols - 1, lastCol );
35228         
35229         // set colYs to bottom of the stamp
35230         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35231             stampSize.height;
35232             
35233         for ( var i = firstCol; i <= lastCol; i++ ) {
35234           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35235         }
35236     },
35237     */
35238     
35239     _getContainerSize : function()
35240     {
35241         this.maxY = Math.max.apply( Math, this.colYs );
35242         var size = {
35243             height: this.maxY
35244         };
35245       
35246         if ( this.isFitWidth ) {
35247             size.width = this._getContainerFitWidth();
35248         }
35249       
35250         return size;
35251     },
35252     
35253     _getContainerFitWidth : function()
35254     {
35255         var unusedCols = 0;
35256         // count unused columns
35257         var i = this.cols;
35258         while ( --i ) {
35259           if ( this.colYs[i] !== 0 ) {
35260             break;
35261           }
35262           unusedCols++;
35263         }
35264         // fit container to columns that have been used
35265         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35266     },
35267     
35268     needsResizeLayout : function()
35269     {
35270         var previousWidth = this.containerWidth;
35271         this.getContainerWidth();
35272         return previousWidth !== this.containerWidth;
35273     }
35274  
35275 });
35276
35277  
35278
35279  /*
35280  * - LGPL
35281  *
35282  * element
35283  * 
35284  */
35285
35286 /**
35287  * @class Roo.bootstrap.MasonryBrick
35288  * @extends Roo.bootstrap.Component
35289  * Bootstrap MasonryBrick class
35290  * 
35291  * @constructor
35292  * Create a new MasonryBrick
35293  * @param {Object} config The config object
35294  */
35295
35296 Roo.bootstrap.MasonryBrick = function(config){
35297     
35298     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35299     
35300     Roo.bootstrap.MasonryBrick.register(this);
35301     
35302     this.addEvents({
35303         // raw events
35304         /**
35305          * @event click
35306          * When a MasonryBrick is clcik
35307          * @param {Roo.bootstrap.MasonryBrick} this
35308          * @param {Roo.EventObject} e
35309          */
35310         "click" : true
35311     });
35312 };
35313
35314 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35315     
35316     /**
35317      * @cfg {String} title
35318      */   
35319     title : '',
35320     /**
35321      * @cfg {String} html
35322      */   
35323     html : '',
35324     /**
35325      * @cfg {String} bgimage
35326      */   
35327     bgimage : '',
35328     /**
35329      * @cfg {String} videourl
35330      */   
35331     videourl : '',
35332     /**
35333      * @cfg {String} cls
35334      */   
35335     cls : '',
35336     /**
35337      * @cfg {String} href
35338      */   
35339     href : '',
35340     /**
35341      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35342      */   
35343     size : 'xs',
35344     
35345     /**
35346      * @cfg {String} placetitle (center|bottom)
35347      */   
35348     placetitle : '',
35349     
35350     /**
35351      * @cfg {Boolean} isFitContainer defalut true
35352      */   
35353     isFitContainer : true, 
35354     
35355     /**
35356      * @cfg {Boolean} preventDefault defalut false
35357      */   
35358     preventDefault : false, 
35359     
35360     /**
35361      * @cfg {Boolean} inverse defalut false
35362      */   
35363     maskInverse : false, 
35364     
35365     getAutoCreate : function()
35366     {
35367         if(!this.isFitContainer){
35368             return this.getSplitAutoCreate();
35369         }
35370         
35371         var cls = 'masonry-brick masonry-brick-full';
35372         
35373         if(this.href.length){
35374             cls += ' masonry-brick-link';
35375         }
35376         
35377         if(this.bgimage.length){
35378             cls += ' masonry-brick-image';
35379         }
35380         
35381         if(this.maskInverse){
35382             cls += ' mask-inverse';
35383         }
35384         
35385         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35386             cls += ' enable-mask';
35387         }
35388         
35389         if(this.size){
35390             cls += ' masonry-' + this.size + '-brick';
35391         }
35392         
35393         if(this.placetitle.length){
35394             
35395             switch (this.placetitle) {
35396                 case 'center' :
35397                     cls += ' masonry-center-title';
35398                     break;
35399                 case 'bottom' :
35400                     cls += ' masonry-bottom-title';
35401                     break;
35402                 default:
35403                     break;
35404             }
35405             
35406         } else {
35407             if(!this.html.length && !this.bgimage.length){
35408                 cls += ' masonry-center-title';
35409             }
35410
35411             if(!this.html.length && this.bgimage.length){
35412                 cls += ' masonry-bottom-title';
35413             }
35414         }
35415         
35416         if(this.cls){
35417             cls += ' ' + this.cls;
35418         }
35419         
35420         var cfg = {
35421             tag: (this.href.length) ? 'a' : 'div',
35422             cls: cls,
35423             cn: [
35424                 {
35425                     tag: 'div',
35426                     cls: 'masonry-brick-mask'
35427                 },
35428                 {
35429                     tag: 'div',
35430                     cls: 'masonry-brick-paragraph',
35431                     cn: []
35432                 }
35433             ]
35434         };
35435         
35436         if(this.href.length){
35437             cfg.href = this.href;
35438         }
35439         
35440         var cn = cfg.cn[1].cn;
35441         
35442         if(this.title.length){
35443             cn.push({
35444                 tag: 'h4',
35445                 cls: 'masonry-brick-title',
35446                 html: this.title
35447             });
35448         }
35449         
35450         if(this.html.length){
35451             cn.push({
35452                 tag: 'p',
35453                 cls: 'masonry-brick-text',
35454                 html: this.html
35455             });
35456         }
35457         
35458         if (!this.title.length && !this.html.length) {
35459             cfg.cn[1].cls += ' hide';
35460         }
35461         
35462         if(this.bgimage.length){
35463             cfg.cn.push({
35464                 tag: 'img',
35465                 cls: 'masonry-brick-image-view',
35466                 src: this.bgimage
35467             });
35468         }
35469         
35470         if(this.videourl.length){
35471             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35472             // youtube support only?
35473             cfg.cn.push({
35474                 tag: 'iframe',
35475                 cls: 'masonry-brick-image-view',
35476                 src: vurl,
35477                 frameborder : 0,
35478                 allowfullscreen : true
35479             });
35480         }
35481         
35482         return cfg;
35483         
35484     },
35485     
35486     getSplitAutoCreate : function()
35487     {
35488         var cls = 'masonry-brick masonry-brick-split';
35489         
35490         if(this.href.length){
35491             cls += ' masonry-brick-link';
35492         }
35493         
35494         if(this.bgimage.length){
35495             cls += ' masonry-brick-image';
35496         }
35497         
35498         if(this.size){
35499             cls += ' masonry-' + this.size + '-brick';
35500         }
35501         
35502         switch (this.placetitle) {
35503             case 'center' :
35504                 cls += ' masonry-center-title';
35505                 break;
35506             case 'bottom' :
35507                 cls += ' masonry-bottom-title';
35508                 break;
35509             default:
35510                 if(!this.bgimage.length){
35511                     cls += ' masonry-center-title';
35512                 }
35513
35514                 if(this.bgimage.length){
35515                     cls += ' masonry-bottom-title';
35516                 }
35517                 break;
35518         }
35519         
35520         if(this.cls){
35521             cls += ' ' + this.cls;
35522         }
35523         
35524         var cfg = {
35525             tag: (this.href.length) ? 'a' : 'div',
35526             cls: cls,
35527             cn: [
35528                 {
35529                     tag: 'div',
35530                     cls: 'masonry-brick-split-head',
35531                     cn: [
35532                         {
35533                             tag: 'div',
35534                             cls: 'masonry-brick-paragraph',
35535                             cn: []
35536                         }
35537                     ]
35538                 },
35539                 {
35540                     tag: 'div',
35541                     cls: 'masonry-brick-split-body',
35542                     cn: []
35543                 }
35544             ]
35545         };
35546         
35547         if(this.href.length){
35548             cfg.href = this.href;
35549         }
35550         
35551         if(this.title.length){
35552             cfg.cn[0].cn[0].cn.push({
35553                 tag: 'h4',
35554                 cls: 'masonry-brick-title',
35555                 html: this.title
35556             });
35557         }
35558         
35559         if(this.html.length){
35560             cfg.cn[1].cn.push({
35561                 tag: 'p',
35562                 cls: 'masonry-brick-text',
35563                 html: this.html
35564             });
35565         }
35566
35567         if(this.bgimage.length){
35568             cfg.cn[0].cn.push({
35569                 tag: 'img',
35570                 cls: 'masonry-brick-image-view',
35571                 src: this.bgimage
35572             });
35573         }
35574         
35575         if(this.videourl.length){
35576             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35577             // youtube support only?
35578             cfg.cn[0].cn.cn.push({
35579                 tag: 'iframe',
35580                 cls: 'masonry-brick-image-view',
35581                 src: vurl,
35582                 frameborder : 0,
35583                 allowfullscreen : true
35584             });
35585         }
35586         
35587         return cfg;
35588     },
35589     
35590     initEvents: function() 
35591     {
35592         switch (this.size) {
35593             case 'xs' :
35594                 this.x = 1;
35595                 this.y = 1;
35596                 break;
35597             case 'sm' :
35598                 this.x = 2;
35599                 this.y = 2;
35600                 break;
35601             case 'md' :
35602             case 'md-left' :
35603             case 'md-right' :
35604                 this.x = 3;
35605                 this.y = 3;
35606                 break;
35607             case 'tall' :
35608                 this.x = 2;
35609                 this.y = 3;
35610                 break;
35611             case 'wide' :
35612                 this.x = 3;
35613                 this.y = 2;
35614                 break;
35615             case 'wide-thin' :
35616                 this.x = 3;
35617                 this.y = 1;
35618                 break;
35619                         
35620             default :
35621                 break;
35622         }
35623         
35624         if(Roo.isTouch){
35625             this.el.on('touchstart', this.onTouchStart, this);
35626             this.el.on('touchmove', this.onTouchMove, this);
35627             this.el.on('touchend', this.onTouchEnd, this);
35628             this.el.on('contextmenu', this.onContextMenu, this);
35629         } else {
35630             this.el.on('mouseenter'  ,this.enter, this);
35631             this.el.on('mouseleave', this.leave, this);
35632             this.el.on('click', this.onClick, this);
35633         }
35634         
35635         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35636             this.parent().bricks.push(this);   
35637         }
35638         
35639     },
35640     
35641     onClick: function(e, el)
35642     {
35643         var time = this.endTimer - this.startTimer;
35644         // Roo.log(e.preventDefault());
35645         if(Roo.isTouch){
35646             if(time > 1000){
35647                 e.preventDefault();
35648                 return;
35649             }
35650         }
35651         
35652         if(!this.preventDefault){
35653             return;
35654         }
35655         
35656         e.preventDefault();
35657         
35658         if (this.activeClass != '') {
35659             this.selectBrick();
35660         }
35661         
35662         this.fireEvent('click', this, e);
35663     },
35664     
35665     enter: function(e, el)
35666     {
35667         e.preventDefault();
35668         
35669         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35670             return;
35671         }
35672         
35673         if(this.bgimage.length && this.html.length){
35674             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35675         }
35676     },
35677     
35678     leave: function(e, el)
35679     {
35680         e.preventDefault();
35681         
35682         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35683             return;
35684         }
35685         
35686         if(this.bgimage.length && this.html.length){
35687             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35688         }
35689     },
35690     
35691     onTouchStart: function(e, el)
35692     {
35693 //        e.preventDefault();
35694         
35695         this.touchmoved = false;
35696         
35697         if(!this.isFitContainer){
35698             return;
35699         }
35700         
35701         if(!this.bgimage.length || !this.html.length){
35702             return;
35703         }
35704         
35705         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35706         
35707         this.timer = new Date().getTime();
35708         
35709     },
35710     
35711     onTouchMove: function(e, el)
35712     {
35713         this.touchmoved = true;
35714     },
35715     
35716     onContextMenu : function(e,el)
35717     {
35718         e.preventDefault();
35719         e.stopPropagation();
35720         return false;
35721     },
35722     
35723     onTouchEnd: function(e, el)
35724     {
35725 //        e.preventDefault();
35726         
35727         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35728         
35729             this.leave(e,el);
35730             
35731             return;
35732         }
35733         
35734         if(!this.bgimage.length || !this.html.length){
35735             
35736             if(this.href.length){
35737                 window.location.href = this.href;
35738             }
35739             
35740             return;
35741         }
35742         
35743         if(!this.isFitContainer){
35744             return;
35745         }
35746         
35747         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35748         
35749         window.location.href = this.href;
35750     },
35751     
35752     //selection on single brick only
35753     selectBrick : function() {
35754         
35755         if (!this.parentId) {
35756             return;
35757         }
35758         
35759         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35760         var index = m.selectedBrick.indexOf(this.id);
35761         
35762         if ( index > -1) {
35763             m.selectedBrick.splice(index,1);
35764             this.el.removeClass(this.activeClass);
35765             return;
35766         }
35767         
35768         for(var i = 0; i < m.selectedBrick.length; i++) {
35769             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35770             b.el.removeClass(b.activeClass);
35771         }
35772         
35773         m.selectedBrick = [];
35774         
35775         m.selectedBrick.push(this.id);
35776         this.el.addClass(this.activeClass);
35777         return;
35778     },
35779     
35780     isSelected : function(){
35781         return this.el.hasClass(this.activeClass);
35782         
35783     }
35784 });
35785
35786 Roo.apply(Roo.bootstrap.MasonryBrick, {
35787     
35788     //groups: {},
35789     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35790      /**
35791     * register a Masonry Brick
35792     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35793     */
35794     
35795     register : function(brick)
35796     {
35797         //this.groups[brick.id] = brick;
35798         this.groups.add(brick.id, brick);
35799     },
35800     /**
35801     * fetch a  masonry brick based on the masonry brick ID
35802     * @param {string} the masonry brick to add
35803     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35804     */
35805     
35806     get: function(brick_id) 
35807     {
35808         // if (typeof(this.groups[brick_id]) == 'undefined') {
35809         //     return false;
35810         // }
35811         // return this.groups[brick_id] ;
35812         
35813         if(this.groups.key(brick_id)) {
35814             return this.groups.key(brick_id);
35815         }
35816         
35817         return false;
35818     }
35819     
35820     
35821     
35822 });
35823
35824  /*
35825  * - LGPL
35826  *
35827  * element
35828  * 
35829  */
35830
35831 /**
35832  * @class Roo.bootstrap.Brick
35833  * @extends Roo.bootstrap.Component
35834  * Bootstrap Brick class
35835  * 
35836  * @constructor
35837  * Create a new Brick
35838  * @param {Object} config The config object
35839  */
35840
35841 Roo.bootstrap.Brick = function(config){
35842     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35843     
35844     this.addEvents({
35845         // raw events
35846         /**
35847          * @event click
35848          * When a Brick is click
35849          * @param {Roo.bootstrap.Brick} this
35850          * @param {Roo.EventObject} e
35851          */
35852         "click" : true
35853     });
35854 };
35855
35856 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35857     
35858     /**
35859      * @cfg {String} title
35860      */   
35861     title : '',
35862     /**
35863      * @cfg {String} html
35864      */   
35865     html : '',
35866     /**
35867      * @cfg {String} bgimage
35868      */   
35869     bgimage : '',
35870     /**
35871      * @cfg {String} cls
35872      */   
35873     cls : '',
35874     /**
35875      * @cfg {String} href
35876      */   
35877     href : '',
35878     /**
35879      * @cfg {String} video
35880      */   
35881     video : '',
35882     /**
35883      * @cfg {Boolean} square
35884      */   
35885     square : true,
35886     
35887     getAutoCreate : function()
35888     {
35889         var cls = 'roo-brick';
35890         
35891         if(this.href.length){
35892             cls += ' roo-brick-link';
35893         }
35894         
35895         if(this.bgimage.length){
35896             cls += ' roo-brick-image';
35897         }
35898         
35899         if(!this.html.length && !this.bgimage.length){
35900             cls += ' roo-brick-center-title';
35901         }
35902         
35903         if(!this.html.length && this.bgimage.length){
35904             cls += ' roo-brick-bottom-title';
35905         }
35906         
35907         if(this.cls){
35908             cls += ' ' + this.cls;
35909         }
35910         
35911         var cfg = {
35912             tag: (this.href.length) ? 'a' : 'div',
35913             cls: cls,
35914             cn: [
35915                 {
35916                     tag: 'div',
35917                     cls: 'roo-brick-paragraph',
35918                     cn: []
35919                 }
35920             ]
35921         };
35922         
35923         if(this.href.length){
35924             cfg.href = this.href;
35925         }
35926         
35927         var cn = cfg.cn[0].cn;
35928         
35929         if(this.title.length){
35930             cn.push({
35931                 tag: 'h4',
35932                 cls: 'roo-brick-title',
35933                 html: this.title
35934             });
35935         }
35936         
35937         if(this.html.length){
35938             cn.push({
35939                 tag: 'p',
35940                 cls: 'roo-brick-text',
35941                 html: this.html
35942             });
35943         } else {
35944             cn.cls += ' hide';
35945         }
35946         
35947         if(this.bgimage.length){
35948             cfg.cn.push({
35949                 tag: 'img',
35950                 cls: 'roo-brick-image-view',
35951                 src: this.bgimage
35952             });
35953         }
35954         
35955         return cfg;
35956     },
35957     
35958     initEvents: function() 
35959     {
35960         if(this.title.length || this.html.length){
35961             this.el.on('mouseenter'  ,this.enter, this);
35962             this.el.on('mouseleave', this.leave, this);
35963         }
35964         
35965         Roo.EventManager.onWindowResize(this.resize, this); 
35966         
35967         if(this.bgimage.length){
35968             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35969             this.imageEl.on('load', this.onImageLoad, this);
35970             return;
35971         }
35972         
35973         this.resize();
35974     },
35975     
35976     onImageLoad : function()
35977     {
35978         this.resize();
35979     },
35980     
35981     resize : function()
35982     {
35983         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35984         
35985         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35986         
35987         if(this.bgimage.length){
35988             var image = this.el.select('.roo-brick-image-view', true).first();
35989             
35990             image.setWidth(paragraph.getWidth());
35991             
35992             if(this.square){
35993                 image.setHeight(paragraph.getWidth());
35994             }
35995             
35996             this.el.setHeight(image.getHeight());
35997             paragraph.setHeight(image.getHeight());
35998             
35999         }
36000         
36001     },
36002     
36003     enter: function(e, el)
36004     {
36005         e.preventDefault();
36006         
36007         if(this.bgimage.length){
36008             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36009             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36010         }
36011     },
36012     
36013     leave: function(e, el)
36014     {
36015         e.preventDefault();
36016         
36017         if(this.bgimage.length){
36018             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36019             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36020         }
36021     }
36022     
36023 });
36024
36025  
36026
36027  /*
36028  * - LGPL
36029  *
36030  * Number field 
36031  */
36032
36033 /**
36034  * @class Roo.bootstrap.NumberField
36035  * @extends Roo.bootstrap.Input
36036  * Bootstrap NumberField class
36037  * 
36038  * 
36039  * 
36040  * 
36041  * @constructor
36042  * Create a new NumberField
36043  * @param {Object} config The config object
36044  */
36045
36046 Roo.bootstrap.NumberField = function(config){
36047     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36048 };
36049
36050 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36051     
36052     /**
36053      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36054      */
36055     allowDecimals : true,
36056     /**
36057      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36058      */
36059     decimalSeparator : ".",
36060     /**
36061      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36062      */
36063     decimalPrecision : 2,
36064     /**
36065      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36066      */
36067     allowNegative : true,
36068     
36069     /**
36070      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36071      */
36072     allowZero: true,
36073     /**
36074      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36075      */
36076     minValue : Number.NEGATIVE_INFINITY,
36077     /**
36078      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36079      */
36080     maxValue : Number.MAX_VALUE,
36081     /**
36082      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36083      */
36084     minText : "The minimum value for this field is {0}",
36085     /**
36086      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36087      */
36088     maxText : "The maximum value for this field is {0}",
36089     /**
36090      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36091      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36092      */
36093     nanText : "{0} is not a valid number",
36094     /**
36095      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36096      */
36097     thousandsDelimiter : false,
36098     /**
36099      * @cfg {String} valueAlign alignment of value
36100      */
36101     valueAlign : "left",
36102
36103     getAutoCreate : function()
36104     {
36105         var hiddenInput = {
36106             tag: 'input',
36107             type: 'hidden',
36108             id: Roo.id(),
36109             cls: 'hidden-number-input'
36110         };
36111         
36112         if (this.name) {
36113             hiddenInput.name = this.name;
36114         }
36115         
36116         this.name = '';
36117         
36118         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36119         
36120         this.name = hiddenInput.name;
36121         
36122         if(cfg.cn.length > 0) {
36123             cfg.cn.push(hiddenInput);
36124         }
36125         
36126         return cfg;
36127     },
36128
36129     // private
36130     initEvents : function()
36131     {   
36132         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36133         
36134         var allowed = "0123456789";
36135         
36136         if(this.allowDecimals){
36137             allowed += this.decimalSeparator;
36138         }
36139         
36140         if(this.allowNegative){
36141             allowed += "-";
36142         }
36143         
36144         if(this.thousandsDelimiter) {
36145             allowed += ",";
36146         }
36147         
36148         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36149         
36150         var keyPress = function(e){
36151             
36152             var k = e.getKey();
36153             
36154             var c = e.getCharCode();
36155             
36156             if(
36157                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36158                     allowed.indexOf(String.fromCharCode(c)) === -1
36159             ){
36160                 e.stopEvent();
36161                 return;
36162             }
36163             
36164             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36165                 return;
36166             }
36167             
36168             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36169                 e.stopEvent();
36170             }
36171         };
36172         
36173         this.el.on("keypress", keyPress, this);
36174     },
36175     
36176     validateValue : function(value)
36177     {
36178         
36179         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36180             return false;
36181         }
36182         
36183         var num = this.parseValue(value);
36184         
36185         if(isNaN(num)){
36186             this.markInvalid(String.format(this.nanText, value));
36187             return false;
36188         }
36189         
36190         if(num < this.minValue){
36191             this.markInvalid(String.format(this.minText, this.minValue));
36192             return false;
36193         }
36194         
36195         if(num > this.maxValue){
36196             this.markInvalid(String.format(this.maxText, this.maxValue));
36197             return false;
36198         }
36199         
36200         return true;
36201     },
36202
36203     getValue : function()
36204     {
36205         var v = this.hiddenEl().getValue();
36206         
36207         return this.fixPrecision(this.parseValue(v));
36208     },
36209
36210     parseValue : function(value)
36211     {
36212         if(this.thousandsDelimiter) {
36213             value += "";
36214             r = new RegExp(",", "g");
36215             value = value.replace(r, "");
36216         }
36217         
36218         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36219         return isNaN(value) ? '' : value;
36220     },
36221
36222     fixPrecision : function(value)
36223     {
36224         if(this.thousandsDelimiter) {
36225             value += "";
36226             r = new RegExp(",", "g");
36227             value = value.replace(r, "");
36228         }
36229         
36230         var nan = isNaN(value);
36231         
36232         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36233             return nan ? '' : value;
36234         }
36235         return parseFloat(value).toFixed(this.decimalPrecision);
36236     },
36237
36238     setValue : function(v)
36239     {
36240         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36241         
36242         this.value = v;
36243         
36244         if(this.rendered){
36245             
36246             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36247             
36248             this.inputEl().dom.value = (v == '') ? '' :
36249                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36250             
36251             if(!this.allowZero && v === '0') {
36252                 this.hiddenEl().dom.value = '';
36253                 this.inputEl().dom.value = '';
36254             }
36255             
36256             this.validate();
36257         }
36258     },
36259
36260     decimalPrecisionFcn : function(v)
36261     {
36262         return Math.floor(v);
36263     },
36264
36265     beforeBlur : function()
36266     {
36267         var v = this.parseValue(this.getRawValue());
36268         
36269         if(v || v === 0 || v === ''){
36270             this.setValue(v);
36271         }
36272     },
36273     
36274     hiddenEl : function()
36275     {
36276         return this.el.select('input.hidden-number-input',true).first();
36277     }
36278     
36279 });
36280
36281  
36282
36283 /*
36284 * Licence: LGPL
36285 */
36286
36287 /**
36288  * @class Roo.bootstrap.DocumentSlider
36289  * @extends Roo.bootstrap.Component
36290  * Bootstrap DocumentSlider class
36291  * 
36292  * @constructor
36293  * Create a new DocumentViewer
36294  * @param {Object} config The config object
36295  */
36296
36297 Roo.bootstrap.DocumentSlider = function(config){
36298     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36299     
36300     this.files = [];
36301     
36302     this.addEvents({
36303         /**
36304          * @event initial
36305          * Fire after initEvent
36306          * @param {Roo.bootstrap.DocumentSlider} this
36307          */
36308         "initial" : true,
36309         /**
36310          * @event update
36311          * Fire after update
36312          * @param {Roo.bootstrap.DocumentSlider} this
36313          */
36314         "update" : true,
36315         /**
36316          * @event click
36317          * Fire after click
36318          * @param {Roo.bootstrap.DocumentSlider} this
36319          */
36320         "click" : true
36321     });
36322 };
36323
36324 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36325     
36326     files : false,
36327     
36328     indicator : 0,
36329     
36330     getAutoCreate : function()
36331     {
36332         var cfg = {
36333             tag : 'div',
36334             cls : 'roo-document-slider',
36335             cn : [
36336                 {
36337                     tag : 'div',
36338                     cls : 'roo-document-slider-header',
36339                     cn : [
36340                         {
36341                             tag : 'div',
36342                             cls : 'roo-document-slider-header-title'
36343                         }
36344                     ]
36345                 },
36346                 {
36347                     tag : 'div',
36348                     cls : 'roo-document-slider-body',
36349                     cn : [
36350                         {
36351                             tag : 'div',
36352                             cls : 'roo-document-slider-prev',
36353                             cn : [
36354                                 {
36355                                     tag : 'i',
36356                                     cls : 'fa fa-chevron-left'
36357                                 }
36358                             ]
36359                         },
36360                         {
36361                             tag : 'div',
36362                             cls : 'roo-document-slider-thumb',
36363                             cn : [
36364                                 {
36365                                     tag : 'img',
36366                                     cls : 'roo-document-slider-image'
36367                                 }
36368                             ]
36369                         },
36370                         {
36371                             tag : 'div',
36372                             cls : 'roo-document-slider-next',
36373                             cn : [
36374                                 {
36375                                     tag : 'i',
36376                                     cls : 'fa fa-chevron-right'
36377                                 }
36378                             ]
36379                         }
36380                     ]
36381                 }
36382             ]
36383         };
36384         
36385         return cfg;
36386     },
36387     
36388     initEvents : function()
36389     {
36390         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36391         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36392         
36393         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36394         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36395         
36396         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36397         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36398         
36399         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36400         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36401         
36402         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36403         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36404         
36405         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36406         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36407         
36408         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36409         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36410         
36411         this.thumbEl.on('click', this.onClick, this);
36412         
36413         this.prevIndicator.on('click', this.prev, this);
36414         
36415         this.nextIndicator.on('click', this.next, this);
36416         
36417     },
36418     
36419     initial : function()
36420     {
36421         if(this.files.length){
36422             this.indicator = 1;
36423             this.update()
36424         }
36425         
36426         this.fireEvent('initial', this);
36427     },
36428     
36429     update : function()
36430     {
36431         this.imageEl.attr('src', this.files[this.indicator - 1]);
36432         
36433         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36434         
36435         this.prevIndicator.show();
36436         
36437         if(this.indicator == 1){
36438             this.prevIndicator.hide();
36439         }
36440         
36441         this.nextIndicator.show();
36442         
36443         if(this.indicator == this.files.length){
36444             this.nextIndicator.hide();
36445         }
36446         
36447         this.thumbEl.scrollTo('top');
36448         
36449         this.fireEvent('update', this);
36450     },
36451     
36452     onClick : function(e)
36453     {
36454         e.preventDefault();
36455         
36456         this.fireEvent('click', this);
36457     },
36458     
36459     prev : function(e)
36460     {
36461         e.preventDefault();
36462         
36463         this.indicator = Math.max(1, this.indicator - 1);
36464         
36465         this.update();
36466     },
36467     
36468     next : function(e)
36469     {
36470         e.preventDefault();
36471         
36472         this.indicator = Math.min(this.files.length, this.indicator + 1);
36473         
36474         this.update();
36475     }
36476 });
36477 /*
36478  * - LGPL
36479  *
36480  * RadioSet
36481  *
36482  *
36483  */
36484
36485 /**
36486  * @class Roo.bootstrap.RadioSet
36487  * @extends Roo.bootstrap.Input
36488  * Bootstrap RadioSet class
36489  * @cfg {String} indicatorpos (left|right) default left
36490  * @cfg {Boolean} inline (true|false) inline the element (default true)
36491  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36492  * @constructor
36493  * Create a new RadioSet
36494  * @param {Object} config The config object
36495  */
36496
36497 Roo.bootstrap.RadioSet = function(config){
36498     
36499     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36500     
36501     this.radioes = [];
36502     
36503     Roo.bootstrap.RadioSet.register(this);
36504     
36505     this.addEvents({
36506         /**
36507         * @event check
36508         * Fires when the element is checked or unchecked.
36509         * @param {Roo.bootstrap.RadioSet} this This radio
36510         * @param {Roo.bootstrap.Radio} item The checked item
36511         */
36512        check : true,
36513        /**
36514         * @event click
36515         * Fires when the element is click.
36516         * @param {Roo.bootstrap.RadioSet} this This radio set
36517         * @param {Roo.bootstrap.Radio} item The checked item
36518         * @param {Roo.EventObject} e The event object
36519         */
36520        click : true
36521     });
36522     
36523 };
36524
36525 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36526
36527     radioes : false,
36528     
36529     inline : true,
36530     
36531     weight : '',
36532     
36533     indicatorpos : 'left',
36534     
36535     getAutoCreate : function()
36536     {
36537         var label = {
36538             tag : 'label',
36539             cls : 'roo-radio-set-label',
36540             cn : [
36541                 {
36542                     tag : 'span',
36543                     html : this.fieldLabel
36544                 }
36545             ]
36546         };
36547         if (Roo.bootstrap.version == 3) {
36548             
36549             
36550             if(this.indicatorpos == 'left'){
36551                 label.cn.unshift({
36552                     tag : 'i',
36553                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36554                     tooltip : 'This field is required'
36555                 });
36556             } else {
36557                 label.cn.push({
36558                     tag : 'i',
36559                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36560                     tooltip : 'This field is required'
36561                 });
36562             }
36563         }
36564         var items = {
36565             tag : 'div',
36566             cls : 'roo-radio-set-items'
36567         };
36568         
36569         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36570         
36571         if (align === 'left' && this.fieldLabel.length) {
36572             
36573             items = {
36574                 cls : "roo-radio-set-right", 
36575                 cn: [
36576                     items
36577                 ]
36578             };
36579             
36580             if(this.labelWidth > 12){
36581                 label.style = "width: " + this.labelWidth + 'px';
36582             }
36583             
36584             if(this.labelWidth < 13 && this.labelmd == 0){
36585                 this.labelmd = this.labelWidth;
36586             }
36587             
36588             if(this.labellg > 0){
36589                 label.cls += ' col-lg-' + this.labellg;
36590                 items.cls += ' col-lg-' + (12 - this.labellg);
36591             }
36592             
36593             if(this.labelmd > 0){
36594                 label.cls += ' col-md-' + this.labelmd;
36595                 items.cls += ' col-md-' + (12 - this.labelmd);
36596             }
36597             
36598             if(this.labelsm > 0){
36599                 label.cls += ' col-sm-' + this.labelsm;
36600                 items.cls += ' col-sm-' + (12 - this.labelsm);
36601             }
36602             
36603             if(this.labelxs > 0){
36604                 label.cls += ' col-xs-' + this.labelxs;
36605                 items.cls += ' col-xs-' + (12 - this.labelxs);
36606             }
36607         }
36608         
36609         var cfg = {
36610             tag : 'div',
36611             cls : 'roo-radio-set',
36612             cn : [
36613                 {
36614                     tag : 'input',
36615                     cls : 'roo-radio-set-input',
36616                     type : 'hidden',
36617                     name : this.name,
36618                     value : this.value ? this.value :  ''
36619                 },
36620                 label,
36621                 items
36622             ]
36623         };
36624         
36625         if(this.weight.length){
36626             cfg.cls += ' roo-radio-' + this.weight;
36627         }
36628         
36629         if(this.inline) {
36630             cfg.cls += ' roo-radio-set-inline';
36631         }
36632         
36633         var settings=this;
36634         ['xs','sm','md','lg'].map(function(size){
36635             if (settings[size]) {
36636                 cfg.cls += ' col-' + size + '-' + settings[size];
36637             }
36638         });
36639         
36640         return cfg;
36641         
36642     },
36643
36644     initEvents : function()
36645     {
36646         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36647         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36648         
36649         if(!this.fieldLabel.length){
36650             this.labelEl.hide();
36651         }
36652         
36653         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36654         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36655         
36656         this.indicator = this.indicatorEl();
36657         
36658         if(this.indicator){
36659             this.indicator.addClass('invisible');
36660         }
36661         
36662         this.originalValue = this.getValue();
36663         
36664     },
36665     
36666     inputEl: function ()
36667     {
36668         return this.el.select('.roo-radio-set-input', true).first();
36669     },
36670     
36671     getChildContainer : function()
36672     {
36673         return this.itemsEl;
36674     },
36675     
36676     register : function(item)
36677     {
36678         this.radioes.push(item);
36679         
36680     },
36681     
36682     validate : function()
36683     {   
36684         if(this.getVisibilityEl().hasClass('hidden')){
36685             return true;
36686         }
36687         
36688         var valid = false;
36689         
36690         Roo.each(this.radioes, function(i){
36691             if(!i.checked){
36692                 return;
36693             }
36694             
36695             valid = true;
36696             return false;
36697         });
36698         
36699         if(this.allowBlank) {
36700             return true;
36701         }
36702         
36703         if(this.disabled || valid){
36704             this.markValid();
36705             return true;
36706         }
36707         
36708         this.markInvalid();
36709         return false;
36710         
36711     },
36712     
36713     markValid : function()
36714     {
36715         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36716             this.indicatorEl().removeClass('visible');
36717             this.indicatorEl().addClass('invisible');
36718         }
36719         
36720         
36721         if (Roo.bootstrap.version == 3) {
36722             this.el.removeClass([this.invalidClass, this.validClass]);
36723             this.el.addClass(this.validClass);
36724         } else {
36725             this.el.removeClass(['is-invalid','is-valid']);
36726             this.el.addClass(['is-valid']);
36727         }
36728         this.fireEvent('valid', this);
36729     },
36730     
36731     markInvalid : function(msg)
36732     {
36733         if(this.allowBlank || this.disabled){
36734             return;
36735         }
36736         
36737         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36738             this.indicatorEl().removeClass('invisible');
36739             this.indicatorEl().addClass('visible');
36740         }
36741         if (Roo.bootstrap.version == 3) {
36742             this.el.removeClass([this.invalidClass, this.validClass]);
36743             this.el.addClass(this.invalidClass);
36744         } else {
36745             this.el.removeClass(['is-invalid','is-valid']);
36746             this.el.addClass(['is-invalid']);
36747         }
36748         
36749         this.fireEvent('invalid', this, msg);
36750         
36751     },
36752     
36753     setValue : function(v, suppressEvent)
36754     {   
36755         if(this.value === v){
36756             return;
36757         }
36758         
36759         this.value = v;
36760         
36761         if(this.rendered){
36762             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36763         }
36764         
36765         Roo.each(this.radioes, function(i){
36766             i.checked = false;
36767             i.el.removeClass('checked');
36768         });
36769         
36770         Roo.each(this.radioes, function(i){
36771             
36772             if(i.value === v || i.value.toString() === v.toString()){
36773                 i.checked = true;
36774                 i.el.addClass('checked');
36775                 
36776                 if(suppressEvent !== true){
36777                     this.fireEvent('check', this, i);
36778                 }
36779                 
36780                 return false;
36781             }
36782             
36783         }, this);
36784         
36785         this.validate();
36786     },
36787     
36788     clearInvalid : function(){
36789         
36790         if(!this.el || this.preventMark){
36791             return;
36792         }
36793         
36794         this.el.removeClass([this.invalidClass]);
36795         
36796         this.fireEvent('valid', this);
36797     }
36798     
36799 });
36800
36801 Roo.apply(Roo.bootstrap.RadioSet, {
36802     
36803     groups: {},
36804     
36805     register : function(set)
36806     {
36807         this.groups[set.name] = set;
36808     },
36809     
36810     get: function(name) 
36811     {
36812         if (typeof(this.groups[name]) == 'undefined') {
36813             return false;
36814         }
36815         
36816         return this.groups[name] ;
36817     }
36818     
36819 });
36820 /*
36821  * Based on:
36822  * Ext JS Library 1.1.1
36823  * Copyright(c) 2006-2007, Ext JS, LLC.
36824  *
36825  * Originally Released Under LGPL - original licence link has changed is not relivant.
36826  *
36827  * Fork - LGPL
36828  * <script type="text/javascript">
36829  */
36830
36831
36832 /**
36833  * @class Roo.bootstrap.SplitBar
36834  * @extends Roo.util.Observable
36835  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36836  * <br><br>
36837  * Usage:
36838  * <pre><code>
36839 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36840                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36841 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36842 split.minSize = 100;
36843 split.maxSize = 600;
36844 split.animate = true;
36845 split.on('moved', splitterMoved);
36846 </code></pre>
36847  * @constructor
36848  * Create a new SplitBar
36849  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36850  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36851  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36852  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36853                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36854                         position of the SplitBar).
36855  */
36856 Roo.bootstrap.SplitBar = function(cfg){
36857     
36858     /** @private */
36859     
36860     //{
36861     //  dragElement : elm
36862     //  resizingElement: el,
36863         // optional..
36864     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36865     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36866         // existingProxy ???
36867     //}
36868     
36869     this.el = Roo.get(cfg.dragElement, true);
36870     this.el.dom.unselectable = "on";
36871     /** @private */
36872     this.resizingEl = Roo.get(cfg.resizingElement, true);
36873
36874     /**
36875      * @private
36876      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36877      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36878      * @type Number
36879      */
36880     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36881     
36882     /**
36883      * The minimum size of the resizing element. (Defaults to 0)
36884      * @type Number
36885      */
36886     this.minSize = 0;
36887     
36888     /**
36889      * The maximum size of the resizing element. (Defaults to 2000)
36890      * @type Number
36891      */
36892     this.maxSize = 2000;
36893     
36894     /**
36895      * Whether to animate the transition to the new size
36896      * @type Boolean
36897      */
36898     this.animate = false;
36899     
36900     /**
36901      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36902      * @type Boolean
36903      */
36904     this.useShim = false;
36905     
36906     /** @private */
36907     this.shim = null;
36908     
36909     if(!cfg.existingProxy){
36910         /** @private */
36911         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36912     }else{
36913         this.proxy = Roo.get(cfg.existingProxy).dom;
36914     }
36915     /** @private */
36916     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36917     
36918     /** @private */
36919     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36920     
36921     /** @private */
36922     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36923     
36924     /** @private */
36925     this.dragSpecs = {};
36926     
36927     /**
36928      * @private The adapter to use to positon and resize elements
36929      */
36930     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36931     this.adapter.init(this);
36932     
36933     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36934         /** @private */
36935         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36936         this.el.addClass("roo-splitbar-h");
36937     }else{
36938         /** @private */
36939         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36940         this.el.addClass("roo-splitbar-v");
36941     }
36942     
36943     this.addEvents({
36944         /**
36945          * @event resize
36946          * Fires when the splitter is moved (alias for {@link #event-moved})
36947          * @param {Roo.bootstrap.SplitBar} this
36948          * @param {Number} newSize the new width or height
36949          */
36950         "resize" : true,
36951         /**
36952          * @event moved
36953          * Fires when the splitter is moved
36954          * @param {Roo.bootstrap.SplitBar} this
36955          * @param {Number} newSize the new width or height
36956          */
36957         "moved" : true,
36958         /**
36959          * @event beforeresize
36960          * Fires before the splitter is dragged
36961          * @param {Roo.bootstrap.SplitBar} this
36962          */
36963         "beforeresize" : true,
36964
36965         "beforeapply" : true
36966     });
36967
36968     Roo.util.Observable.call(this);
36969 };
36970
36971 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36972     onStartProxyDrag : function(x, y){
36973         this.fireEvent("beforeresize", this);
36974         if(!this.overlay){
36975             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36976             o.unselectable();
36977             o.enableDisplayMode("block");
36978             // all splitbars share the same overlay
36979             Roo.bootstrap.SplitBar.prototype.overlay = o;
36980         }
36981         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36982         this.overlay.show();
36983         Roo.get(this.proxy).setDisplayed("block");
36984         var size = this.adapter.getElementSize(this);
36985         this.activeMinSize = this.getMinimumSize();;
36986         this.activeMaxSize = this.getMaximumSize();;
36987         var c1 = size - this.activeMinSize;
36988         var c2 = Math.max(this.activeMaxSize - size, 0);
36989         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36990             this.dd.resetConstraints();
36991             this.dd.setXConstraint(
36992                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36993                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36994             );
36995             this.dd.setYConstraint(0, 0);
36996         }else{
36997             this.dd.resetConstraints();
36998             this.dd.setXConstraint(0, 0);
36999             this.dd.setYConstraint(
37000                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37001                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37002             );
37003          }
37004         this.dragSpecs.startSize = size;
37005         this.dragSpecs.startPoint = [x, y];
37006         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37007     },
37008     
37009     /** 
37010      * @private Called after the drag operation by the DDProxy
37011      */
37012     onEndProxyDrag : function(e){
37013         Roo.get(this.proxy).setDisplayed(false);
37014         var endPoint = Roo.lib.Event.getXY(e);
37015         if(this.overlay){
37016             this.overlay.hide();
37017         }
37018         var newSize;
37019         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37020             newSize = this.dragSpecs.startSize + 
37021                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37022                     endPoint[0] - this.dragSpecs.startPoint[0] :
37023                     this.dragSpecs.startPoint[0] - endPoint[0]
37024                 );
37025         }else{
37026             newSize = this.dragSpecs.startSize + 
37027                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37028                     endPoint[1] - this.dragSpecs.startPoint[1] :
37029                     this.dragSpecs.startPoint[1] - endPoint[1]
37030                 );
37031         }
37032         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37033         if(newSize != this.dragSpecs.startSize){
37034             if(this.fireEvent('beforeapply', this, newSize) !== false){
37035                 this.adapter.setElementSize(this, newSize);
37036                 this.fireEvent("moved", this, newSize);
37037                 this.fireEvent("resize", this, newSize);
37038             }
37039         }
37040     },
37041     
37042     /**
37043      * Get the adapter this SplitBar uses
37044      * @return The adapter object
37045      */
37046     getAdapter : function(){
37047         return this.adapter;
37048     },
37049     
37050     /**
37051      * Set the adapter this SplitBar uses
37052      * @param {Object} adapter A SplitBar adapter object
37053      */
37054     setAdapter : function(adapter){
37055         this.adapter = adapter;
37056         this.adapter.init(this);
37057     },
37058     
37059     /**
37060      * Gets the minimum size for the resizing element
37061      * @return {Number} The minimum size
37062      */
37063     getMinimumSize : function(){
37064         return this.minSize;
37065     },
37066     
37067     /**
37068      * Sets the minimum size for the resizing element
37069      * @param {Number} minSize The minimum size
37070      */
37071     setMinimumSize : function(minSize){
37072         this.minSize = minSize;
37073     },
37074     
37075     /**
37076      * Gets the maximum size for the resizing element
37077      * @return {Number} The maximum size
37078      */
37079     getMaximumSize : function(){
37080         return this.maxSize;
37081     },
37082     
37083     /**
37084      * Sets the maximum size for the resizing element
37085      * @param {Number} maxSize The maximum size
37086      */
37087     setMaximumSize : function(maxSize){
37088         this.maxSize = maxSize;
37089     },
37090     
37091     /**
37092      * Sets the initialize size for the resizing element
37093      * @param {Number} size The initial size
37094      */
37095     setCurrentSize : function(size){
37096         var oldAnimate = this.animate;
37097         this.animate = false;
37098         this.adapter.setElementSize(this, size);
37099         this.animate = oldAnimate;
37100     },
37101     
37102     /**
37103      * Destroy this splitbar. 
37104      * @param {Boolean} removeEl True to remove the element
37105      */
37106     destroy : function(removeEl){
37107         if(this.shim){
37108             this.shim.remove();
37109         }
37110         this.dd.unreg();
37111         this.proxy.parentNode.removeChild(this.proxy);
37112         if(removeEl){
37113             this.el.remove();
37114         }
37115     }
37116 });
37117
37118 /**
37119  * @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.
37120  */
37121 Roo.bootstrap.SplitBar.createProxy = function(dir){
37122     var proxy = new Roo.Element(document.createElement("div"));
37123     proxy.unselectable();
37124     var cls = 'roo-splitbar-proxy';
37125     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37126     document.body.appendChild(proxy.dom);
37127     return proxy.dom;
37128 };
37129
37130 /** 
37131  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37132  * Default Adapter. It assumes the splitter and resizing element are not positioned
37133  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37134  */
37135 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37136 };
37137
37138 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37139     // do nothing for now
37140     init : function(s){
37141     
37142     },
37143     /**
37144      * Called before drag operations to get the current size of the resizing element. 
37145      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37146      */
37147      getElementSize : function(s){
37148         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37149             return s.resizingEl.getWidth();
37150         }else{
37151             return s.resizingEl.getHeight();
37152         }
37153     },
37154     
37155     /**
37156      * Called after drag operations to set the size of the resizing element.
37157      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37158      * @param {Number} newSize The new size to set
37159      * @param {Function} onComplete A function to be invoked when resizing is complete
37160      */
37161     setElementSize : function(s, newSize, onComplete){
37162         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37163             if(!s.animate){
37164                 s.resizingEl.setWidth(newSize);
37165                 if(onComplete){
37166                     onComplete(s, newSize);
37167                 }
37168             }else{
37169                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37170             }
37171         }else{
37172             
37173             if(!s.animate){
37174                 s.resizingEl.setHeight(newSize);
37175                 if(onComplete){
37176                     onComplete(s, newSize);
37177                 }
37178             }else{
37179                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37180             }
37181         }
37182     }
37183 };
37184
37185 /** 
37186  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37187  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37188  * Adapter that  moves the splitter element to align with the resized sizing element. 
37189  * Used with an absolute positioned SplitBar.
37190  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37191  * document.body, make sure you assign an id to the body element.
37192  */
37193 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37194     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37195     this.container = Roo.get(container);
37196 };
37197
37198 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37199     init : function(s){
37200         this.basic.init(s);
37201     },
37202     
37203     getElementSize : function(s){
37204         return this.basic.getElementSize(s);
37205     },
37206     
37207     setElementSize : function(s, newSize, onComplete){
37208         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37209     },
37210     
37211     moveSplitter : function(s){
37212         var yes = Roo.bootstrap.SplitBar;
37213         switch(s.placement){
37214             case yes.LEFT:
37215                 s.el.setX(s.resizingEl.getRight());
37216                 break;
37217             case yes.RIGHT:
37218                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37219                 break;
37220             case yes.TOP:
37221                 s.el.setY(s.resizingEl.getBottom());
37222                 break;
37223             case yes.BOTTOM:
37224                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37225                 break;
37226         }
37227     }
37228 };
37229
37230 /**
37231  * Orientation constant - Create a vertical SplitBar
37232  * @static
37233  * @type Number
37234  */
37235 Roo.bootstrap.SplitBar.VERTICAL = 1;
37236
37237 /**
37238  * Orientation constant - Create a horizontal SplitBar
37239  * @static
37240  * @type Number
37241  */
37242 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37243
37244 /**
37245  * Placement constant - The resizing element is to the left of the splitter element
37246  * @static
37247  * @type Number
37248  */
37249 Roo.bootstrap.SplitBar.LEFT = 1;
37250
37251 /**
37252  * Placement constant - The resizing element is to the right of the splitter element
37253  * @static
37254  * @type Number
37255  */
37256 Roo.bootstrap.SplitBar.RIGHT = 2;
37257
37258 /**
37259  * Placement constant - The resizing element is positioned above the splitter element
37260  * @static
37261  * @type Number
37262  */
37263 Roo.bootstrap.SplitBar.TOP = 3;
37264
37265 /**
37266  * Placement constant - The resizing element is positioned under splitter element
37267  * @static
37268  * @type Number
37269  */
37270 Roo.bootstrap.SplitBar.BOTTOM = 4;
37271 Roo.namespace("Roo.bootstrap.layout");/*
37272  * Based on:
37273  * Ext JS Library 1.1.1
37274  * Copyright(c) 2006-2007, Ext JS, LLC.
37275  *
37276  * Originally Released Under LGPL - original licence link has changed is not relivant.
37277  *
37278  * Fork - LGPL
37279  * <script type="text/javascript">
37280  */
37281
37282 /**
37283  * @class Roo.bootstrap.layout.Manager
37284  * @extends Roo.bootstrap.Component
37285  * Base class for layout managers.
37286  */
37287 Roo.bootstrap.layout.Manager = function(config)
37288 {
37289     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37290
37291
37292
37293
37294
37295     /** false to disable window resize monitoring @type Boolean */
37296     this.monitorWindowResize = true;
37297     this.regions = {};
37298     this.addEvents({
37299         /**
37300          * @event layout
37301          * Fires when a layout is performed.
37302          * @param {Roo.LayoutManager} this
37303          */
37304         "layout" : true,
37305         /**
37306          * @event regionresized
37307          * Fires when the user resizes a region.
37308          * @param {Roo.LayoutRegion} region The resized region
37309          * @param {Number} newSize The new size (width for east/west, height for north/south)
37310          */
37311         "regionresized" : true,
37312         /**
37313          * @event regioncollapsed
37314          * Fires when a region is collapsed.
37315          * @param {Roo.LayoutRegion} region The collapsed region
37316          */
37317         "regioncollapsed" : true,
37318         /**
37319          * @event regionexpanded
37320          * Fires when a region is expanded.
37321          * @param {Roo.LayoutRegion} region The expanded region
37322          */
37323         "regionexpanded" : true
37324     });
37325     this.updating = false;
37326
37327     if (config.el) {
37328         this.el = Roo.get(config.el);
37329         this.initEvents();
37330     }
37331
37332 };
37333
37334 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37335
37336
37337     regions : null,
37338
37339     monitorWindowResize : true,
37340
37341
37342     updating : false,
37343
37344
37345     onRender : function(ct, position)
37346     {
37347         if(!this.el){
37348             this.el = Roo.get(ct);
37349             this.initEvents();
37350         }
37351         //this.fireEvent('render',this);
37352     },
37353
37354
37355     initEvents: function()
37356     {
37357
37358
37359         // ie scrollbar fix
37360         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37361             document.body.scroll = "no";
37362         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37363             this.el.position('relative');
37364         }
37365         this.id = this.el.id;
37366         this.el.addClass("roo-layout-container");
37367         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37368         if(this.el.dom != document.body ) {
37369             this.el.on('resize', this.layout,this);
37370             this.el.on('show', this.layout,this);
37371         }
37372
37373     },
37374
37375     /**
37376      * Returns true if this layout is currently being updated
37377      * @return {Boolean}
37378      */
37379     isUpdating : function(){
37380         return this.updating;
37381     },
37382
37383     /**
37384      * Suspend the LayoutManager from doing auto-layouts while
37385      * making multiple add or remove calls
37386      */
37387     beginUpdate : function(){
37388         this.updating = true;
37389     },
37390
37391     /**
37392      * Restore auto-layouts and optionally disable the manager from performing a layout
37393      * @param {Boolean} noLayout true to disable a layout update
37394      */
37395     endUpdate : function(noLayout){
37396         this.updating = false;
37397         if(!noLayout){
37398             this.layout();
37399         }
37400     },
37401
37402     layout: function(){
37403         // abstract...
37404     },
37405
37406     onRegionResized : function(region, newSize){
37407         this.fireEvent("regionresized", region, newSize);
37408         this.layout();
37409     },
37410
37411     onRegionCollapsed : function(region){
37412         this.fireEvent("regioncollapsed", region);
37413     },
37414
37415     onRegionExpanded : function(region){
37416         this.fireEvent("regionexpanded", region);
37417     },
37418
37419     /**
37420      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37421      * performs box-model adjustments.
37422      * @return {Object} The size as an object {width: (the width), height: (the height)}
37423      */
37424     getViewSize : function()
37425     {
37426         var size;
37427         if(this.el.dom != document.body){
37428             size = this.el.getSize();
37429         }else{
37430             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37431         }
37432         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37433         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37434         return size;
37435     },
37436
37437     /**
37438      * Returns the Element this layout is bound to.
37439      * @return {Roo.Element}
37440      */
37441     getEl : function(){
37442         return this.el;
37443     },
37444
37445     /**
37446      * Returns the specified region.
37447      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37448      * @return {Roo.LayoutRegion}
37449      */
37450     getRegion : function(target){
37451         return this.regions[target.toLowerCase()];
37452     },
37453
37454     onWindowResize : function(){
37455         if(this.monitorWindowResize){
37456             this.layout();
37457         }
37458     }
37459 });
37460 /*
37461  * Based on:
37462  * Ext JS Library 1.1.1
37463  * Copyright(c) 2006-2007, Ext JS, LLC.
37464  *
37465  * Originally Released Under LGPL - original licence link has changed is not relivant.
37466  *
37467  * Fork - LGPL
37468  * <script type="text/javascript">
37469  */
37470 /**
37471  * @class Roo.bootstrap.layout.Border
37472  * @extends Roo.bootstrap.layout.Manager
37473  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37474  * please see: examples/bootstrap/nested.html<br><br>
37475  
37476 <b>The container the layout is rendered into can be either the body element or any other element.
37477 If it is not the body element, the container needs to either be an absolute positioned element,
37478 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37479 the container size if it is not the body element.</b>
37480
37481 * @constructor
37482 * Create a new Border
37483 * @param {Object} config Configuration options
37484  */
37485 Roo.bootstrap.layout.Border = function(config){
37486     config = config || {};
37487     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37488     
37489     
37490     
37491     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37492         if(config[region]){
37493             config[region].region = region;
37494             this.addRegion(config[region]);
37495         }
37496     },this);
37497     
37498 };
37499
37500 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37501
37502 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37503     
37504     parent : false, // this might point to a 'nest' or a ???
37505     
37506     /**
37507      * Creates and adds a new region if it doesn't already exist.
37508      * @param {String} target The target region key (north, south, east, west or center).
37509      * @param {Object} config The regions config object
37510      * @return {BorderLayoutRegion} The new region
37511      */
37512     addRegion : function(config)
37513     {
37514         if(!this.regions[config.region]){
37515             var r = this.factory(config);
37516             this.bindRegion(r);
37517         }
37518         return this.regions[config.region];
37519     },
37520
37521     // private (kinda)
37522     bindRegion : function(r){
37523         this.regions[r.config.region] = r;
37524         
37525         r.on("visibilitychange",    this.layout, this);
37526         r.on("paneladded",          this.layout, this);
37527         r.on("panelremoved",        this.layout, this);
37528         r.on("invalidated",         this.layout, this);
37529         r.on("resized",             this.onRegionResized, this);
37530         r.on("collapsed",           this.onRegionCollapsed, this);
37531         r.on("expanded",            this.onRegionExpanded, this);
37532     },
37533
37534     /**
37535      * Performs a layout update.
37536      */
37537     layout : function()
37538     {
37539         if(this.updating) {
37540             return;
37541         }
37542         
37543         // render all the rebions if they have not been done alreayd?
37544         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37545             if(this.regions[region] && !this.regions[region].bodyEl){
37546                 this.regions[region].onRender(this.el)
37547             }
37548         },this);
37549         
37550         var size = this.getViewSize();
37551         var w = size.width;
37552         var h = size.height;
37553         var centerW = w;
37554         var centerH = h;
37555         var centerY = 0;
37556         var centerX = 0;
37557         //var x = 0, y = 0;
37558
37559         var rs = this.regions;
37560         var north = rs["north"];
37561         var south = rs["south"]; 
37562         var west = rs["west"];
37563         var east = rs["east"];
37564         var center = rs["center"];
37565         //if(this.hideOnLayout){ // not supported anymore
37566             //c.el.setStyle("display", "none");
37567         //}
37568         if(north && north.isVisible()){
37569             var b = north.getBox();
37570             var m = north.getMargins();
37571             b.width = w - (m.left+m.right);
37572             b.x = m.left;
37573             b.y = m.top;
37574             centerY = b.height + b.y + m.bottom;
37575             centerH -= centerY;
37576             north.updateBox(this.safeBox(b));
37577         }
37578         if(south && south.isVisible()){
37579             var b = south.getBox();
37580             var m = south.getMargins();
37581             b.width = w - (m.left+m.right);
37582             b.x = m.left;
37583             var totalHeight = (b.height + m.top + m.bottom);
37584             b.y = h - totalHeight + m.top;
37585             centerH -= totalHeight;
37586             south.updateBox(this.safeBox(b));
37587         }
37588         if(west && west.isVisible()){
37589             var b = west.getBox();
37590             var m = west.getMargins();
37591             b.height = centerH - (m.top+m.bottom);
37592             b.x = m.left;
37593             b.y = centerY + m.top;
37594             var totalWidth = (b.width + m.left + m.right);
37595             centerX += totalWidth;
37596             centerW -= totalWidth;
37597             west.updateBox(this.safeBox(b));
37598         }
37599         if(east && east.isVisible()){
37600             var b = east.getBox();
37601             var m = east.getMargins();
37602             b.height = centerH - (m.top+m.bottom);
37603             var totalWidth = (b.width + m.left + m.right);
37604             b.x = w - totalWidth + m.left;
37605             b.y = centerY + m.top;
37606             centerW -= totalWidth;
37607             east.updateBox(this.safeBox(b));
37608         }
37609         if(center){
37610             var m = center.getMargins();
37611             var centerBox = {
37612                 x: centerX + m.left,
37613                 y: centerY + m.top,
37614                 width: centerW - (m.left+m.right),
37615                 height: centerH - (m.top+m.bottom)
37616             };
37617             //if(this.hideOnLayout){
37618                 //center.el.setStyle("display", "block");
37619             //}
37620             center.updateBox(this.safeBox(centerBox));
37621         }
37622         this.el.repaint();
37623         this.fireEvent("layout", this);
37624     },
37625
37626     // private
37627     safeBox : function(box){
37628         box.width = Math.max(0, box.width);
37629         box.height = Math.max(0, box.height);
37630         return box;
37631     },
37632
37633     /**
37634      * Adds a ContentPanel (or subclass) to this layout.
37635      * @param {String} target The target region key (north, south, east, west or center).
37636      * @param {Roo.ContentPanel} panel The panel to add
37637      * @return {Roo.ContentPanel} The added panel
37638      */
37639     add : function(target, panel){
37640          
37641         target = target.toLowerCase();
37642         return this.regions[target].add(panel);
37643     },
37644
37645     /**
37646      * Remove a ContentPanel (or subclass) to this layout.
37647      * @param {String} target The target region key (north, south, east, west or center).
37648      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37649      * @return {Roo.ContentPanel} The removed panel
37650      */
37651     remove : function(target, panel){
37652         target = target.toLowerCase();
37653         return this.regions[target].remove(panel);
37654     },
37655
37656     /**
37657      * Searches all regions for a panel with the specified id
37658      * @param {String} panelId
37659      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37660      */
37661     findPanel : function(panelId){
37662         var rs = this.regions;
37663         for(var target in rs){
37664             if(typeof rs[target] != "function"){
37665                 var p = rs[target].getPanel(panelId);
37666                 if(p){
37667                     return p;
37668                 }
37669             }
37670         }
37671         return null;
37672     },
37673
37674     /**
37675      * Searches all regions for a panel with the specified id and activates (shows) it.
37676      * @param {String/ContentPanel} panelId The panels id or the panel itself
37677      * @return {Roo.ContentPanel} The shown panel or null
37678      */
37679     showPanel : function(panelId) {
37680       var rs = this.regions;
37681       for(var target in rs){
37682          var r = rs[target];
37683          if(typeof r != "function"){
37684             if(r.hasPanel(panelId)){
37685                return r.showPanel(panelId);
37686             }
37687          }
37688       }
37689       return null;
37690    },
37691
37692    /**
37693      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37694      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37695      */
37696    /*
37697     restoreState : function(provider){
37698         if(!provider){
37699             provider = Roo.state.Manager;
37700         }
37701         var sm = new Roo.LayoutStateManager();
37702         sm.init(this, provider);
37703     },
37704 */
37705  
37706  
37707     /**
37708      * Adds a xtype elements to the layout.
37709      * <pre><code>
37710
37711 layout.addxtype({
37712        xtype : 'ContentPanel',
37713        region: 'west',
37714        items: [ .... ]
37715    }
37716 );
37717
37718 layout.addxtype({
37719         xtype : 'NestedLayoutPanel',
37720         region: 'west',
37721         layout: {
37722            center: { },
37723            west: { }   
37724         },
37725         items : [ ... list of content panels or nested layout panels.. ]
37726    }
37727 );
37728 </code></pre>
37729      * @param {Object} cfg Xtype definition of item to add.
37730      */
37731     addxtype : function(cfg)
37732     {
37733         // basically accepts a pannel...
37734         // can accept a layout region..!?!?
37735         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37736         
37737         
37738         // theory?  children can only be panels??
37739         
37740         //if (!cfg.xtype.match(/Panel$/)) {
37741         //    return false;
37742         //}
37743         var ret = false;
37744         
37745         if (typeof(cfg.region) == 'undefined') {
37746             Roo.log("Failed to add Panel, region was not set");
37747             Roo.log(cfg);
37748             return false;
37749         }
37750         var region = cfg.region;
37751         delete cfg.region;
37752         
37753           
37754         var xitems = [];
37755         if (cfg.items) {
37756             xitems = cfg.items;
37757             delete cfg.items;
37758         }
37759         var nb = false;
37760         
37761         if ( region == 'center') {
37762             Roo.log("Center: " + cfg.title);
37763         }
37764         
37765         
37766         switch(cfg.xtype) 
37767         {
37768             case 'Content':  // ContentPanel (el, cfg)
37769             case 'Scroll':  // ContentPanel (el, cfg)
37770             case 'View': 
37771                 cfg.autoCreate = cfg.autoCreate || true;
37772                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37773                 //} else {
37774                 //    var el = this.el.createChild();
37775                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37776                 //}
37777                 
37778                 this.add(region, ret);
37779                 break;
37780             
37781             /*
37782             case 'TreePanel': // our new panel!
37783                 cfg.el = this.el.createChild();
37784                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37785                 this.add(region, ret);
37786                 break;
37787             */
37788             
37789             case 'Nest': 
37790                 // create a new Layout (which is  a Border Layout...
37791                 
37792                 var clayout = cfg.layout;
37793                 clayout.el  = this.el.createChild();
37794                 clayout.items   = clayout.items  || [];
37795                 
37796                 delete cfg.layout;
37797                 
37798                 // replace this exitems with the clayout ones..
37799                 xitems = clayout.items;
37800                  
37801                 // force background off if it's in center...
37802                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37803                     cfg.background = false;
37804                 }
37805                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37806                 
37807                 
37808                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37809                 //console.log('adding nested layout panel '  + cfg.toSource());
37810                 this.add(region, ret);
37811                 nb = {}; /// find first...
37812                 break;
37813             
37814             case 'Grid':
37815                 
37816                 // needs grid and region
37817                 
37818                 //var el = this.getRegion(region).el.createChild();
37819                 /*
37820                  *var el = this.el.createChild();
37821                 // create the grid first...
37822                 cfg.grid.container = el;
37823                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37824                 */
37825                 
37826                 if (region == 'center' && this.active ) {
37827                     cfg.background = false;
37828                 }
37829                 
37830                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37831                 
37832                 this.add(region, ret);
37833                 /*
37834                 if (cfg.background) {
37835                     // render grid on panel activation (if panel background)
37836                     ret.on('activate', function(gp) {
37837                         if (!gp.grid.rendered) {
37838                     //        gp.grid.render(el);
37839                         }
37840                     });
37841                 } else {
37842                   //  cfg.grid.render(el);
37843                 }
37844                 */
37845                 break;
37846            
37847            
37848             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37849                 // it was the old xcomponent building that caused this before.
37850                 // espeically if border is the top element in the tree.
37851                 ret = this;
37852                 break; 
37853                 
37854                     
37855                 
37856                 
37857                 
37858             default:
37859                 /*
37860                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37861                     
37862                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37863                     this.add(region, ret);
37864                 } else {
37865                 */
37866                     Roo.log(cfg);
37867                     throw "Can not add '" + cfg.xtype + "' to Border";
37868                     return null;
37869              
37870                                 
37871              
37872         }
37873         this.beginUpdate();
37874         // add children..
37875         var region = '';
37876         var abn = {};
37877         Roo.each(xitems, function(i)  {
37878             region = nb && i.region ? i.region : false;
37879             
37880             var add = ret.addxtype(i);
37881            
37882             if (region) {
37883                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37884                 if (!i.background) {
37885                     abn[region] = nb[region] ;
37886                 }
37887             }
37888             
37889         });
37890         this.endUpdate();
37891
37892         // make the last non-background panel active..
37893         //if (nb) { Roo.log(abn); }
37894         if (nb) {
37895             
37896             for(var r in abn) {
37897                 region = this.getRegion(r);
37898                 if (region) {
37899                     // tried using nb[r], but it does not work..
37900                      
37901                     region.showPanel(abn[r]);
37902                    
37903                 }
37904             }
37905         }
37906         return ret;
37907         
37908     },
37909     
37910     
37911 // private
37912     factory : function(cfg)
37913     {
37914         
37915         var validRegions = Roo.bootstrap.layout.Border.regions;
37916
37917         var target = cfg.region;
37918         cfg.mgr = this;
37919         
37920         var r = Roo.bootstrap.layout;
37921         Roo.log(target);
37922         switch(target){
37923             case "north":
37924                 return new r.North(cfg);
37925             case "south":
37926                 return new r.South(cfg);
37927             case "east":
37928                 return new r.East(cfg);
37929             case "west":
37930                 return new r.West(cfg);
37931             case "center":
37932                 return new r.Center(cfg);
37933         }
37934         throw 'Layout region "'+target+'" not supported.';
37935     }
37936     
37937     
37938 });
37939  /*
37940  * Based on:
37941  * Ext JS Library 1.1.1
37942  * Copyright(c) 2006-2007, Ext JS, LLC.
37943  *
37944  * Originally Released Under LGPL - original licence link has changed is not relivant.
37945  *
37946  * Fork - LGPL
37947  * <script type="text/javascript">
37948  */
37949  
37950 /**
37951  * @class Roo.bootstrap.layout.Basic
37952  * @extends Roo.util.Observable
37953  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37954  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37955  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37956  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37957  * @cfg {string}   region  the region that it inhabits..
37958  * @cfg {bool}   skipConfig skip config?
37959  * 
37960
37961  */
37962 Roo.bootstrap.layout.Basic = function(config){
37963     
37964     this.mgr = config.mgr;
37965     
37966     this.position = config.region;
37967     
37968     var skipConfig = config.skipConfig;
37969     
37970     this.events = {
37971         /**
37972          * @scope Roo.BasicLayoutRegion
37973          */
37974         
37975         /**
37976          * @event beforeremove
37977          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37978          * @param {Roo.LayoutRegion} this
37979          * @param {Roo.ContentPanel} panel The panel
37980          * @param {Object} e The cancel event object
37981          */
37982         "beforeremove" : true,
37983         /**
37984          * @event invalidated
37985          * Fires when the layout for this region is changed.
37986          * @param {Roo.LayoutRegion} this
37987          */
37988         "invalidated" : true,
37989         /**
37990          * @event visibilitychange
37991          * Fires when this region is shown or hidden 
37992          * @param {Roo.LayoutRegion} this
37993          * @param {Boolean} visibility true or false
37994          */
37995         "visibilitychange" : true,
37996         /**
37997          * @event paneladded
37998          * Fires when a panel is added. 
37999          * @param {Roo.LayoutRegion} this
38000          * @param {Roo.ContentPanel} panel The panel
38001          */
38002         "paneladded" : true,
38003         /**
38004          * @event panelremoved
38005          * Fires when a panel is removed. 
38006          * @param {Roo.LayoutRegion} this
38007          * @param {Roo.ContentPanel} panel The panel
38008          */
38009         "panelremoved" : true,
38010         /**
38011          * @event beforecollapse
38012          * Fires when this region before collapse.
38013          * @param {Roo.LayoutRegion} this
38014          */
38015         "beforecollapse" : true,
38016         /**
38017          * @event collapsed
38018          * Fires when this region is collapsed.
38019          * @param {Roo.LayoutRegion} this
38020          */
38021         "collapsed" : true,
38022         /**
38023          * @event expanded
38024          * Fires when this region is expanded.
38025          * @param {Roo.LayoutRegion} this
38026          */
38027         "expanded" : true,
38028         /**
38029          * @event slideshow
38030          * Fires when this region is slid into view.
38031          * @param {Roo.LayoutRegion} this
38032          */
38033         "slideshow" : true,
38034         /**
38035          * @event slidehide
38036          * Fires when this region slides out of view. 
38037          * @param {Roo.LayoutRegion} this
38038          */
38039         "slidehide" : true,
38040         /**
38041          * @event panelactivated
38042          * Fires when a panel is activated. 
38043          * @param {Roo.LayoutRegion} this
38044          * @param {Roo.ContentPanel} panel The activated panel
38045          */
38046         "panelactivated" : true,
38047         /**
38048          * @event resized
38049          * Fires when the user resizes this region. 
38050          * @param {Roo.LayoutRegion} this
38051          * @param {Number} newSize The new size (width for east/west, height for north/south)
38052          */
38053         "resized" : true
38054     };
38055     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38056     this.panels = new Roo.util.MixedCollection();
38057     this.panels.getKey = this.getPanelId.createDelegate(this);
38058     this.box = null;
38059     this.activePanel = null;
38060     // ensure listeners are added...
38061     
38062     if (config.listeners || config.events) {
38063         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38064             listeners : config.listeners || {},
38065             events : config.events || {}
38066         });
38067     }
38068     
38069     if(skipConfig !== true){
38070         this.applyConfig(config);
38071     }
38072 };
38073
38074 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38075 {
38076     getPanelId : function(p){
38077         return p.getId();
38078     },
38079     
38080     applyConfig : function(config){
38081         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38082         this.config = config;
38083         
38084     },
38085     
38086     /**
38087      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38088      * the width, for horizontal (north, south) the height.
38089      * @param {Number} newSize The new width or height
38090      */
38091     resizeTo : function(newSize){
38092         var el = this.el ? this.el :
38093                  (this.activePanel ? this.activePanel.getEl() : null);
38094         if(el){
38095             switch(this.position){
38096                 case "east":
38097                 case "west":
38098                     el.setWidth(newSize);
38099                     this.fireEvent("resized", this, newSize);
38100                 break;
38101                 case "north":
38102                 case "south":
38103                     el.setHeight(newSize);
38104                     this.fireEvent("resized", this, newSize);
38105                 break;                
38106             }
38107         }
38108     },
38109     
38110     getBox : function(){
38111         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38112     },
38113     
38114     getMargins : function(){
38115         return this.margins;
38116     },
38117     
38118     updateBox : function(box){
38119         this.box = box;
38120         var el = this.activePanel.getEl();
38121         el.dom.style.left = box.x + "px";
38122         el.dom.style.top = box.y + "px";
38123         this.activePanel.setSize(box.width, box.height);
38124     },
38125     
38126     /**
38127      * Returns the container element for this region.
38128      * @return {Roo.Element}
38129      */
38130     getEl : function(){
38131         return this.activePanel;
38132     },
38133     
38134     /**
38135      * Returns true if this region is currently visible.
38136      * @return {Boolean}
38137      */
38138     isVisible : function(){
38139         return this.activePanel ? true : false;
38140     },
38141     
38142     setActivePanel : function(panel){
38143         panel = this.getPanel(panel);
38144         if(this.activePanel && this.activePanel != panel){
38145             this.activePanel.setActiveState(false);
38146             this.activePanel.getEl().setLeftTop(-10000,-10000);
38147         }
38148         this.activePanel = panel;
38149         panel.setActiveState(true);
38150         if(this.box){
38151             panel.setSize(this.box.width, this.box.height);
38152         }
38153         this.fireEvent("panelactivated", this, panel);
38154         this.fireEvent("invalidated");
38155     },
38156     
38157     /**
38158      * Show the specified panel.
38159      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38160      * @return {Roo.ContentPanel} The shown panel or null
38161      */
38162     showPanel : function(panel){
38163         panel = this.getPanel(panel);
38164         if(panel){
38165             this.setActivePanel(panel);
38166         }
38167         return panel;
38168     },
38169     
38170     /**
38171      * Get the active panel for this region.
38172      * @return {Roo.ContentPanel} The active panel or null
38173      */
38174     getActivePanel : function(){
38175         return this.activePanel;
38176     },
38177     
38178     /**
38179      * Add the passed ContentPanel(s)
38180      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38181      * @return {Roo.ContentPanel} The panel added (if only one was added)
38182      */
38183     add : function(panel){
38184         if(arguments.length > 1){
38185             for(var i = 0, len = arguments.length; i < len; i++) {
38186                 this.add(arguments[i]);
38187             }
38188             return null;
38189         }
38190         if(this.hasPanel(panel)){
38191             this.showPanel(panel);
38192             return panel;
38193         }
38194         var el = panel.getEl();
38195         if(el.dom.parentNode != this.mgr.el.dom){
38196             this.mgr.el.dom.appendChild(el.dom);
38197         }
38198         if(panel.setRegion){
38199             panel.setRegion(this);
38200         }
38201         this.panels.add(panel);
38202         el.setStyle("position", "absolute");
38203         if(!panel.background){
38204             this.setActivePanel(panel);
38205             if(this.config.initialSize && this.panels.getCount()==1){
38206                 this.resizeTo(this.config.initialSize);
38207             }
38208         }
38209         this.fireEvent("paneladded", this, panel);
38210         return panel;
38211     },
38212     
38213     /**
38214      * Returns true if the panel is in this region.
38215      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38216      * @return {Boolean}
38217      */
38218     hasPanel : function(panel){
38219         if(typeof panel == "object"){ // must be panel obj
38220             panel = panel.getId();
38221         }
38222         return this.getPanel(panel) ? true : false;
38223     },
38224     
38225     /**
38226      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38227      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38228      * @param {Boolean} preservePanel Overrides the config preservePanel option
38229      * @return {Roo.ContentPanel} The panel that was removed
38230      */
38231     remove : function(panel, preservePanel){
38232         panel = this.getPanel(panel);
38233         if(!panel){
38234             return null;
38235         }
38236         var e = {};
38237         this.fireEvent("beforeremove", this, panel, e);
38238         if(e.cancel === true){
38239             return null;
38240         }
38241         var panelId = panel.getId();
38242         this.panels.removeKey(panelId);
38243         return panel;
38244     },
38245     
38246     /**
38247      * Returns the panel specified or null if it's not in this region.
38248      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38249      * @return {Roo.ContentPanel}
38250      */
38251     getPanel : function(id){
38252         if(typeof id == "object"){ // must be panel obj
38253             return id;
38254         }
38255         return this.panels.get(id);
38256     },
38257     
38258     /**
38259      * Returns this regions position (north/south/east/west/center).
38260      * @return {String} 
38261      */
38262     getPosition: function(){
38263         return this.position;    
38264     }
38265 });/*
38266  * Based on:
38267  * Ext JS Library 1.1.1
38268  * Copyright(c) 2006-2007, Ext JS, LLC.
38269  *
38270  * Originally Released Under LGPL - original licence link has changed is not relivant.
38271  *
38272  * Fork - LGPL
38273  * <script type="text/javascript">
38274  */
38275  
38276 /**
38277  * @class Roo.bootstrap.layout.Region
38278  * @extends Roo.bootstrap.layout.Basic
38279  * This class represents a region in a layout manager.
38280  
38281  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38282  * @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})
38283  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38284  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38285  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38286  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38287  * @cfg {String}    title           The title for the region (overrides panel titles)
38288  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38289  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38290  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38291  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38292  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38293  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38294  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38295  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38296  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38297  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38298
38299  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38300  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38301  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38302  * @cfg {Number}    width           For East/West panels
38303  * @cfg {Number}    height          For North/South panels
38304  * @cfg {Boolean}   split           To show the splitter
38305  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38306  * 
38307  * @cfg {string}   cls             Extra CSS classes to add to region
38308  * 
38309  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38310  * @cfg {string}   region  the region that it inhabits..
38311  *
38312
38313  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38314  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38315
38316  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38317  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38318  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38319  */
38320 Roo.bootstrap.layout.Region = function(config)
38321 {
38322     this.applyConfig(config);
38323
38324     var mgr = config.mgr;
38325     var pos = config.region;
38326     config.skipConfig = true;
38327     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38328     
38329     if (mgr.el) {
38330         this.onRender(mgr.el);   
38331     }
38332      
38333     this.visible = true;
38334     this.collapsed = false;
38335     this.unrendered_panels = [];
38336 };
38337
38338 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38339
38340     position: '', // set by wrapper (eg. north/south etc..)
38341     unrendered_panels : null,  // unrendered panels.
38342     
38343     tabPosition : false,
38344     
38345     mgr: false, // points to 'Border'
38346     
38347     
38348     createBody : function(){
38349         /** This region's body element 
38350         * @type Roo.Element */
38351         this.bodyEl = this.el.createChild({
38352                 tag: "div",
38353                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38354         });
38355     },
38356
38357     onRender: function(ctr, pos)
38358     {
38359         var dh = Roo.DomHelper;
38360         /** This region's container element 
38361         * @type Roo.Element */
38362         this.el = dh.append(ctr.dom, {
38363                 tag: "div",
38364                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38365             }, true);
38366         /** This region's title element 
38367         * @type Roo.Element */
38368     
38369         this.titleEl = dh.append(this.el.dom,  {
38370                 tag: "div",
38371                 unselectable: "on",
38372                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38373                 children:[
38374                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38375                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38376                 ]
38377             }, true);
38378         
38379         this.titleEl.enableDisplayMode();
38380         /** This region's title text element 
38381         * @type HTMLElement */
38382         this.titleTextEl = this.titleEl.dom.firstChild;
38383         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38384         /*
38385         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38386         this.closeBtn.enableDisplayMode();
38387         this.closeBtn.on("click", this.closeClicked, this);
38388         this.closeBtn.hide();
38389     */
38390         this.createBody(this.config);
38391         if(this.config.hideWhenEmpty){
38392             this.hide();
38393             this.on("paneladded", this.validateVisibility, this);
38394             this.on("panelremoved", this.validateVisibility, this);
38395         }
38396         if(this.autoScroll){
38397             this.bodyEl.setStyle("overflow", "auto");
38398         }else{
38399             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38400         }
38401         //if(c.titlebar !== false){
38402             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38403                 this.titleEl.hide();
38404             }else{
38405                 this.titleEl.show();
38406                 if(this.config.title){
38407                     this.titleTextEl.innerHTML = this.config.title;
38408                 }
38409             }
38410         //}
38411         if(this.config.collapsed){
38412             this.collapse(true);
38413         }
38414         if(this.config.hidden){
38415             this.hide();
38416         }
38417         
38418         if (this.unrendered_panels && this.unrendered_panels.length) {
38419             for (var i =0;i< this.unrendered_panels.length; i++) {
38420                 this.add(this.unrendered_panels[i]);
38421             }
38422             this.unrendered_panels = null;
38423             
38424         }
38425         
38426     },
38427     
38428     applyConfig : function(c)
38429     {
38430         /*
38431          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38432             var dh = Roo.DomHelper;
38433             if(c.titlebar !== false){
38434                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38435                 this.collapseBtn.on("click", this.collapse, this);
38436                 this.collapseBtn.enableDisplayMode();
38437                 /*
38438                 if(c.showPin === true || this.showPin){
38439                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38440                     this.stickBtn.enableDisplayMode();
38441                     this.stickBtn.on("click", this.expand, this);
38442                     this.stickBtn.hide();
38443                 }
38444                 
38445             }
38446             */
38447             /** This region's collapsed element
38448             * @type Roo.Element */
38449             /*
38450              *
38451             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38452                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38453             ]}, true);
38454             
38455             if(c.floatable !== false){
38456                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38457                this.collapsedEl.on("click", this.collapseClick, this);
38458             }
38459
38460             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38461                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38462                    id: "message", unselectable: "on", style:{"float":"left"}});
38463                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38464              }
38465             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38466             this.expandBtn.on("click", this.expand, this);
38467             
38468         }
38469         
38470         if(this.collapseBtn){
38471             this.collapseBtn.setVisible(c.collapsible == true);
38472         }
38473         
38474         this.cmargins = c.cmargins || this.cmargins ||
38475                          (this.position == "west" || this.position == "east" ?
38476                              {top: 0, left: 2, right:2, bottom: 0} :
38477                              {top: 2, left: 0, right:0, bottom: 2});
38478         */
38479         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38480         
38481         
38482         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38483         
38484         this.autoScroll = c.autoScroll || false;
38485         
38486         
38487        
38488         
38489         this.duration = c.duration || .30;
38490         this.slideDuration = c.slideDuration || .45;
38491         this.config = c;
38492        
38493     },
38494     /**
38495      * Returns true if this region is currently visible.
38496      * @return {Boolean}
38497      */
38498     isVisible : function(){
38499         return this.visible;
38500     },
38501
38502     /**
38503      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38504      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38505      */
38506     //setCollapsedTitle : function(title){
38507     //    title = title || "&#160;";
38508      //   if(this.collapsedTitleTextEl){
38509       //      this.collapsedTitleTextEl.innerHTML = title;
38510        // }
38511     //},
38512
38513     getBox : function(){
38514         var b;
38515       //  if(!this.collapsed){
38516             b = this.el.getBox(false, true);
38517        // }else{
38518           //  b = this.collapsedEl.getBox(false, true);
38519         //}
38520         return b;
38521     },
38522
38523     getMargins : function(){
38524         return this.margins;
38525         //return this.collapsed ? this.cmargins : this.margins;
38526     },
38527 /*
38528     highlight : function(){
38529         this.el.addClass("x-layout-panel-dragover");
38530     },
38531
38532     unhighlight : function(){
38533         this.el.removeClass("x-layout-panel-dragover");
38534     },
38535 */
38536     updateBox : function(box)
38537     {
38538         if (!this.bodyEl) {
38539             return; // not rendered yet..
38540         }
38541         
38542         this.box = box;
38543         if(!this.collapsed){
38544             this.el.dom.style.left = box.x + "px";
38545             this.el.dom.style.top = box.y + "px";
38546             this.updateBody(box.width, box.height);
38547         }else{
38548             this.collapsedEl.dom.style.left = box.x + "px";
38549             this.collapsedEl.dom.style.top = box.y + "px";
38550             this.collapsedEl.setSize(box.width, box.height);
38551         }
38552         if(this.tabs){
38553             this.tabs.autoSizeTabs();
38554         }
38555     },
38556
38557     updateBody : function(w, h)
38558     {
38559         if(w !== null){
38560             this.el.setWidth(w);
38561             w -= this.el.getBorderWidth("rl");
38562             if(this.config.adjustments){
38563                 w += this.config.adjustments[0];
38564             }
38565         }
38566         if(h !== null && h > 0){
38567             this.el.setHeight(h);
38568             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38569             h -= this.el.getBorderWidth("tb");
38570             if(this.config.adjustments){
38571                 h += this.config.adjustments[1];
38572             }
38573             this.bodyEl.setHeight(h);
38574             if(this.tabs){
38575                 h = this.tabs.syncHeight(h);
38576             }
38577         }
38578         if(this.panelSize){
38579             w = w !== null ? w : this.panelSize.width;
38580             h = h !== null ? h : this.panelSize.height;
38581         }
38582         if(this.activePanel){
38583             var el = this.activePanel.getEl();
38584             w = w !== null ? w : el.getWidth();
38585             h = h !== null ? h : el.getHeight();
38586             this.panelSize = {width: w, height: h};
38587             this.activePanel.setSize(w, h);
38588         }
38589         if(Roo.isIE && this.tabs){
38590             this.tabs.el.repaint();
38591         }
38592     },
38593
38594     /**
38595      * Returns the container element for this region.
38596      * @return {Roo.Element}
38597      */
38598     getEl : function(){
38599         return this.el;
38600     },
38601
38602     /**
38603      * Hides this region.
38604      */
38605     hide : function(){
38606         //if(!this.collapsed){
38607             this.el.dom.style.left = "-2000px";
38608             this.el.hide();
38609         //}else{
38610          //   this.collapsedEl.dom.style.left = "-2000px";
38611          //   this.collapsedEl.hide();
38612        // }
38613         this.visible = false;
38614         this.fireEvent("visibilitychange", this, false);
38615     },
38616
38617     /**
38618      * Shows this region if it was previously hidden.
38619      */
38620     show : function(){
38621         //if(!this.collapsed){
38622             this.el.show();
38623         //}else{
38624         //    this.collapsedEl.show();
38625        // }
38626         this.visible = true;
38627         this.fireEvent("visibilitychange", this, true);
38628     },
38629 /*
38630     closeClicked : function(){
38631         if(this.activePanel){
38632             this.remove(this.activePanel);
38633         }
38634     },
38635
38636     collapseClick : function(e){
38637         if(this.isSlid){
38638            e.stopPropagation();
38639            this.slideIn();
38640         }else{
38641            e.stopPropagation();
38642            this.slideOut();
38643         }
38644     },
38645 */
38646     /**
38647      * Collapses this region.
38648      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38649      */
38650     /*
38651     collapse : function(skipAnim, skipCheck = false){
38652         if(this.collapsed) {
38653             return;
38654         }
38655         
38656         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38657             
38658             this.collapsed = true;
38659             if(this.split){
38660                 this.split.el.hide();
38661             }
38662             if(this.config.animate && skipAnim !== true){
38663                 this.fireEvent("invalidated", this);
38664                 this.animateCollapse();
38665             }else{
38666                 this.el.setLocation(-20000,-20000);
38667                 this.el.hide();
38668                 this.collapsedEl.show();
38669                 this.fireEvent("collapsed", this);
38670                 this.fireEvent("invalidated", this);
38671             }
38672         }
38673         
38674     },
38675 */
38676     animateCollapse : function(){
38677         // overridden
38678     },
38679
38680     /**
38681      * Expands this region if it was previously collapsed.
38682      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38683      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38684      */
38685     /*
38686     expand : function(e, skipAnim){
38687         if(e) {
38688             e.stopPropagation();
38689         }
38690         if(!this.collapsed || this.el.hasActiveFx()) {
38691             return;
38692         }
38693         if(this.isSlid){
38694             this.afterSlideIn();
38695             skipAnim = true;
38696         }
38697         this.collapsed = false;
38698         if(this.config.animate && skipAnim !== true){
38699             this.animateExpand();
38700         }else{
38701             this.el.show();
38702             if(this.split){
38703                 this.split.el.show();
38704             }
38705             this.collapsedEl.setLocation(-2000,-2000);
38706             this.collapsedEl.hide();
38707             this.fireEvent("invalidated", this);
38708             this.fireEvent("expanded", this);
38709         }
38710     },
38711 */
38712     animateExpand : function(){
38713         // overridden
38714     },
38715
38716     initTabs : function()
38717     {
38718         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38719         
38720         var ts = new Roo.bootstrap.panel.Tabs({
38721             el: this.bodyEl.dom,
38722             region : this,
38723             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38724             disableTooltips: this.config.disableTabTips,
38725             toolbar : this.config.toolbar
38726         });
38727         
38728         if(this.config.hideTabs){
38729             ts.stripWrap.setDisplayed(false);
38730         }
38731         this.tabs = ts;
38732         ts.resizeTabs = this.config.resizeTabs === true;
38733         ts.minTabWidth = this.config.minTabWidth || 40;
38734         ts.maxTabWidth = this.config.maxTabWidth || 250;
38735         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38736         ts.monitorResize = false;
38737         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38738         ts.bodyEl.addClass('roo-layout-tabs-body');
38739         this.panels.each(this.initPanelAsTab, this);
38740     },
38741
38742     initPanelAsTab : function(panel){
38743         var ti = this.tabs.addTab(
38744             panel.getEl().id,
38745             panel.getTitle(),
38746             null,
38747             this.config.closeOnTab && panel.isClosable(),
38748             panel.tpl
38749         );
38750         if(panel.tabTip !== undefined){
38751             ti.setTooltip(panel.tabTip);
38752         }
38753         ti.on("activate", function(){
38754               this.setActivePanel(panel);
38755         }, this);
38756         
38757         if(this.config.closeOnTab){
38758             ti.on("beforeclose", function(t, e){
38759                 e.cancel = true;
38760                 this.remove(panel);
38761             }, this);
38762         }
38763         
38764         panel.tabItem = ti;
38765         
38766         return ti;
38767     },
38768
38769     updatePanelTitle : function(panel, title)
38770     {
38771         if(this.activePanel == panel){
38772             this.updateTitle(title);
38773         }
38774         if(this.tabs){
38775             var ti = this.tabs.getTab(panel.getEl().id);
38776             ti.setText(title);
38777             if(panel.tabTip !== undefined){
38778                 ti.setTooltip(panel.tabTip);
38779             }
38780         }
38781     },
38782
38783     updateTitle : function(title){
38784         if(this.titleTextEl && !this.config.title){
38785             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38786         }
38787     },
38788
38789     setActivePanel : function(panel)
38790     {
38791         panel = this.getPanel(panel);
38792         if(this.activePanel && this.activePanel != panel){
38793             if(this.activePanel.setActiveState(false) === false){
38794                 return;
38795             }
38796         }
38797         this.activePanel = panel;
38798         panel.setActiveState(true);
38799         if(this.panelSize){
38800             panel.setSize(this.panelSize.width, this.panelSize.height);
38801         }
38802         if(this.closeBtn){
38803             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38804         }
38805         this.updateTitle(panel.getTitle());
38806         if(this.tabs){
38807             this.fireEvent("invalidated", this);
38808         }
38809         this.fireEvent("panelactivated", this, panel);
38810     },
38811
38812     /**
38813      * Shows the specified panel.
38814      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38815      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38816      */
38817     showPanel : function(panel)
38818     {
38819         panel = this.getPanel(panel);
38820         if(panel){
38821             if(this.tabs){
38822                 var tab = this.tabs.getTab(panel.getEl().id);
38823                 if(tab.isHidden()){
38824                     this.tabs.unhideTab(tab.id);
38825                 }
38826                 tab.activate();
38827             }else{
38828                 this.setActivePanel(panel);
38829             }
38830         }
38831         return panel;
38832     },
38833
38834     /**
38835      * Get the active panel for this region.
38836      * @return {Roo.ContentPanel} The active panel or null
38837      */
38838     getActivePanel : function(){
38839         return this.activePanel;
38840     },
38841
38842     validateVisibility : function(){
38843         if(this.panels.getCount() < 1){
38844             this.updateTitle("&#160;");
38845             this.closeBtn.hide();
38846             this.hide();
38847         }else{
38848             if(!this.isVisible()){
38849                 this.show();
38850             }
38851         }
38852     },
38853
38854     /**
38855      * Adds the passed ContentPanel(s) to this region.
38856      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38857      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38858      */
38859     add : function(panel)
38860     {
38861         if(arguments.length > 1){
38862             for(var i = 0, len = arguments.length; i < len; i++) {
38863                 this.add(arguments[i]);
38864             }
38865             return null;
38866         }
38867         
38868         // if we have not been rendered yet, then we can not really do much of this..
38869         if (!this.bodyEl) {
38870             this.unrendered_panels.push(panel);
38871             return panel;
38872         }
38873         
38874         
38875         
38876         
38877         if(this.hasPanel(panel)){
38878             this.showPanel(panel);
38879             return panel;
38880         }
38881         panel.setRegion(this);
38882         this.panels.add(panel);
38883        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38884             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38885             // and hide them... ???
38886             this.bodyEl.dom.appendChild(panel.getEl().dom);
38887             if(panel.background !== true){
38888                 this.setActivePanel(panel);
38889             }
38890             this.fireEvent("paneladded", this, panel);
38891             return panel;
38892         }
38893         */
38894         if(!this.tabs){
38895             this.initTabs();
38896         }else{
38897             this.initPanelAsTab(panel);
38898         }
38899         
38900         
38901         if(panel.background !== true){
38902             this.tabs.activate(panel.getEl().id);
38903         }
38904         this.fireEvent("paneladded", this, panel);
38905         return panel;
38906     },
38907
38908     /**
38909      * Hides the tab for the specified panel.
38910      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38911      */
38912     hidePanel : function(panel){
38913         if(this.tabs && (panel = this.getPanel(panel))){
38914             this.tabs.hideTab(panel.getEl().id);
38915         }
38916     },
38917
38918     /**
38919      * Unhides the tab for a previously hidden panel.
38920      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38921      */
38922     unhidePanel : function(panel){
38923         if(this.tabs && (panel = this.getPanel(panel))){
38924             this.tabs.unhideTab(panel.getEl().id);
38925         }
38926     },
38927
38928     clearPanels : function(){
38929         while(this.panels.getCount() > 0){
38930              this.remove(this.panels.first());
38931         }
38932     },
38933
38934     /**
38935      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38936      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38937      * @param {Boolean} preservePanel Overrides the config preservePanel option
38938      * @return {Roo.ContentPanel} The panel that was removed
38939      */
38940     remove : function(panel, preservePanel)
38941     {
38942         panel = this.getPanel(panel);
38943         if(!panel){
38944             return null;
38945         }
38946         var e = {};
38947         this.fireEvent("beforeremove", this, panel, e);
38948         if(e.cancel === true){
38949             return null;
38950         }
38951         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38952         var panelId = panel.getId();
38953         this.panels.removeKey(panelId);
38954         if(preservePanel){
38955             document.body.appendChild(panel.getEl().dom);
38956         }
38957         if(this.tabs){
38958             this.tabs.removeTab(panel.getEl().id);
38959         }else if (!preservePanel){
38960             this.bodyEl.dom.removeChild(panel.getEl().dom);
38961         }
38962         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38963             var p = this.panels.first();
38964             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38965             tempEl.appendChild(p.getEl().dom);
38966             this.bodyEl.update("");
38967             this.bodyEl.dom.appendChild(p.getEl().dom);
38968             tempEl = null;
38969             this.updateTitle(p.getTitle());
38970             this.tabs = null;
38971             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38972             this.setActivePanel(p);
38973         }
38974         panel.setRegion(null);
38975         if(this.activePanel == panel){
38976             this.activePanel = null;
38977         }
38978         if(this.config.autoDestroy !== false && preservePanel !== true){
38979             try{panel.destroy();}catch(e){}
38980         }
38981         this.fireEvent("panelremoved", this, panel);
38982         return panel;
38983     },
38984
38985     /**
38986      * Returns the TabPanel component used by this region
38987      * @return {Roo.TabPanel}
38988      */
38989     getTabs : function(){
38990         return this.tabs;
38991     },
38992
38993     createTool : function(parentEl, className){
38994         var btn = Roo.DomHelper.append(parentEl, {
38995             tag: "div",
38996             cls: "x-layout-tools-button",
38997             children: [ {
38998                 tag: "div",
38999                 cls: "roo-layout-tools-button-inner " + className,
39000                 html: "&#160;"
39001             }]
39002         }, true);
39003         btn.addClassOnOver("roo-layout-tools-button-over");
39004         return btn;
39005     }
39006 });/*
39007  * Based on:
39008  * Ext JS Library 1.1.1
39009  * Copyright(c) 2006-2007, Ext JS, LLC.
39010  *
39011  * Originally Released Under LGPL - original licence link has changed is not relivant.
39012  *
39013  * Fork - LGPL
39014  * <script type="text/javascript">
39015  */
39016  
39017
39018
39019 /**
39020  * @class Roo.SplitLayoutRegion
39021  * @extends Roo.LayoutRegion
39022  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39023  */
39024 Roo.bootstrap.layout.Split = function(config){
39025     this.cursor = config.cursor;
39026     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39027 };
39028
39029 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39030 {
39031     splitTip : "Drag to resize.",
39032     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39033     useSplitTips : false,
39034
39035     applyConfig : function(config){
39036         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39037     },
39038     
39039     onRender : function(ctr,pos) {
39040         
39041         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39042         if(!this.config.split){
39043             return;
39044         }
39045         if(!this.split){
39046             
39047             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39048                             tag: "div",
39049                             id: this.el.id + "-split",
39050                             cls: "roo-layout-split roo-layout-split-"+this.position,
39051                             html: "&#160;"
39052             });
39053             /** The SplitBar for this region 
39054             * @type Roo.SplitBar */
39055             // does not exist yet...
39056             Roo.log([this.position, this.orientation]);
39057             
39058             this.split = new Roo.bootstrap.SplitBar({
39059                 dragElement : splitEl,
39060                 resizingElement: this.el,
39061                 orientation : this.orientation
39062             });
39063             
39064             this.split.on("moved", this.onSplitMove, this);
39065             this.split.useShim = this.config.useShim === true;
39066             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39067             if(this.useSplitTips){
39068                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39069             }
39070             //if(config.collapsible){
39071             //    this.split.el.on("dblclick", this.collapse,  this);
39072             //}
39073         }
39074         if(typeof this.config.minSize != "undefined"){
39075             this.split.minSize = this.config.minSize;
39076         }
39077         if(typeof this.config.maxSize != "undefined"){
39078             this.split.maxSize = this.config.maxSize;
39079         }
39080         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39081             this.hideSplitter();
39082         }
39083         
39084     },
39085
39086     getHMaxSize : function(){
39087          var cmax = this.config.maxSize || 10000;
39088          var center = this.mgr.getRegion("center");
39089          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39090     },
39091
39092     getVMaxSize : function(){
39093          var cmax = this.config.maxSize || 10000;
39094          var center = this.mgr.getRegion("center");
39095          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39096     },
39097
39098     onSplitMove : function(split, newSize){
39099         this.fireEvent("resized", this, newSize);
39100     },
39101     
39102     /** 
39103      * Returns the {@link Roo.SplitBar} for this region.
39104      * @return {Roo.SplitBar}
39105      */
39106     getSplitBar : function(){
39107         return this.split;
39108     },
39109     
39110     hide : function(){
39111         this.hideSplitter();
39112         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39113     },
39114
39115     hideSplitter : function(){
39116         if(this.split){
39117             this.split.el.setLocation(-2000,-2000);
39118             this.split.el.hide();
39119         }
39120     },
39121
39122     show : function(){
39123         if(this.split){
39124             this.split.el.show();
39125         }
39126         Roo.bootstrap.layout.Split.superclass.show.call(this);
39127     },
39128     
39129     beforeSlide: function(){
39130         if(Roo.isGecko){// firefox overflow auto bug workaround
39131             this.bodyEl.clip();
39132             if(this.tabs) {
39133                 this.tabs.bodyEl.clip();
39134             }
39135             if(this.activePanel){
39136                 this.activePanel.getEl().clip();
39137                 
39138                 if(this.activePanel.beforeSlide){
39139                     this.activePanel.beforeSlide();
39140                 }
39141             }
39142         }
39143     },
39144     
39145     afterSlide : function(){
39146         if(Roo.isGecko){// firefox overflow auto bug workaround
39147             this.bodyEl.unclip();
39148             if(this.tabs) {
39149                 this.tabs.bodyEl.unclip();
39150             }
39151             if(this.activePanel){
39152                 this.activePanel.getEl().unclip();
39153                 if(this.activePanel.afterSlide){
39154                     this.activePanel.afterSlide();
39155                 }
39156             }
39157         }
39158     },
39159
39160     initAutoHide : function(){
39161         if(this.autoHide !== false){
39162             if(!this.autoHideHd){
39163                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39164                 this.autoHideHd = {
39165                     "mouseout": function(e){
39166                         if(!e.within(this.el, true)){
39167                             st.delay(500);
39168                         }
39169                     },
39170                     "mouseover" : function(e){
39171                         st.cancel();
39172                     },
39173                     scope : this
39174                 };
39175             }
39176             this.el.on(this.autoHideHd);
39177         }
39178     },
39179
39180     clearAutoHide : function(){
39181         if(this.autoHide !== false){
39182             this.el.un("mouseout", this.autoHideHd.mouseout);
39183             this.el.un("mouseover", this.autoHideHd.mouseover);
39184         }
39185     },
39186
39187     clearMonitor : function(){
39188         Roo.get(document).un("click", this.slideInIf, this);
39189     },
39190
39191     // these names are backwards but not changed for compat
39192     slideOut : function(){
39193         if(this.isSlid || this.el.hasActiveFx()){
39194             return;
39195         }
39196         this.isSlid = true;
39197         if(this.collapseBtn){
39198             this.collapseBtn.hide();
39199         }
39200         this.closeBtnState = this.closeBtn.getStyle('display');
39201         this.closeBtn.hide();
39202         if(this.stickBtn){
39203             this.stickBtn.show();
39204         }
39205         this.el.show();
39206         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39207         this.beforeSlide();
39208         this.el.setStyle("z-index", 10001);
39209         this.el.slideIn(this.getSlideAnchor(), {
39210             callback: function(){
39211                 this.afterSlide();
39212                 this.initAutoHide();
39213                 Roo.get(document).on("click", this.slideInIf, this);
39214                 this.fireEvent("slideshow", this);
39215             },
39216             scope: this,
39217             block: true
39218         });
39219     },
39220
39221     afterSlideIn : function(){
39222         this.clearAutoHide();
39223         this.isSlid = false;
39224         this.clearMonitor();
39225         this.el.setStyle("z-index", "");
39226         if(this.collapseBtn){
39227             this.collapseBtn.show();
39228         }
39229         this.closeBtn.setStyle('display', this.closeBtnState);
39230         if(this.stickBtn){
39231             this.stickBtn.hide();
39232         }
39233         this.fireEvent("slidehide", this);
39234     },
39235
39236     slideIn : function(cb){
39237         if(!this.isSlid || this.el.hasActiveFx()){
39238             Roo.callback(cb);
39239             return;
39240         }
39241         this.isSlid = false;
39242         this.beforeSlide();
39243         this.el.slideOut(this.getSlideAnchor(), {
39244             callback: function(){
39245                 this.el.setLeftTop(-10000, -10000);
39246                 this.afterSlide();
39247                 this.afterSlideIn();
39248                 Roo.callback(cb);
39249             },
39250             scope: this,
39251             block: true
39252         });
39253     },
39254     
39255     slideInIf : function(e){
39256         if(!e.within(this.el)){
39257             this.slideIn();
39258         }
39259     },
39260
39261     animateCollapse : function(){
39262         this.beforeSlide();
39263         this.el.setStyle("z-index", 20000);
39264         var anchor = this.getSlideAnchor();
39265         this.el.slideOut(anchor, {
39266             callback : function(){
39267                 this.el.setStyle("z-index", "");
39268                 this.collapsedEl.slideIn(anchor, {duration:.3});
39269                 this.afterSlide();
39270                 this.el.setLocation(-10000,-10000);
39271                 this.el.hide();
39272                 this.fireEvent("collapsed", this);
39273             },
39274             scope: this,
39275             block: true
39276         });
39277     },
39278
39279     animateExpand : function(){
39280         this.beforeSlide();
39281         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39282         this.el.setStyle("z-index", 20000);
39283         this.collapsedEl.hide({
39284             duration:.1
39285         });
39286         this.el.slideIn(this.getSlideAnchor(), {
39287             callback : function(){
39288                 this.el.setStyle("z-index", "");
39289                 this.afterSlide();
39290                 if(this.split){
39291                     this.split.el.show();
39292                 }
39293                 this.fireEvent("invalidated", this);
39294                 this.fireEvent("expanded", this);
39295             },
39296             scope: this,
39297             block: true
39298         });
39299     },
39300
39301     anchors : {
39302         "west" : "left",
39303         "east" : "right",
39304         "north" : "top",
39305         "south" : "bottom"
39306     },
39307
39308     sanchors : {
39309         "west" : "l",
39310         "east" : "r",
39311         "north" : "t",
39312         "south" : "b"
39313     },
39314
39315     canchors : {
39316         "west" : "tl-tr",
39317         "east" : "tr-tl",
39318         "north" : "tl-bl",
39319         "south" : "bl-tl"
39320     },
39321
39322     getAnchor : function(){
39323         return this.anchors[this.position];
39324     },
39325
39326     getCollapseAnchor : function(){
39327         return this.canchors[this.position];
39328     },
39329
39330     getSlideAnchor : function(){
39331         return this.sanchors[this.position];
39332     },
39333
39334     getAlignAdj : function(){
39335         var cm = this.cmargins;
39336         switch(this.position){
39337             case "west":
39338                 return [0, 0];
39339             break;
39340             case "east":
39341                 return [0, 0];
39342             break;
39343             case "north":
39344                 return [0, 0];
39345             break;
39346             case "south":
39347                 return [0, 0];
39348             break;
39349         }
39350     },
39351
39352     getExpandAdj : function(){
39353         var c = this.collapsedEl, cm = this.cmargins;
39354         switch(this.position){
39355             case "west":
39356                 return [-(cm.right+c.getWidth()+cm.left), 0];
39357             break;
39358             case "east":
39359                 return [cm.right+c.getWidth()+cm.left, 0];
39360             break;
39361             case "north":
39362                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39363             break;
39364             case "south":
39365                 return [0, cm.top+cm.bottom+c.getHeight()];
39366             break;
39367         }
39368     }
39369 });/*
39370  * Based on:
39371  * Ext JS Library 1.1.1
39372  * Copyright(c) 2006-2007, Ext JS, LLC.
39373  *
39374  * Originally Released Under LGPL - original licence link has changed is not relivant.
39375  *
39376  * Fork - LGPL
39377  * <script type="text/javascript">
39378  */
39379 /*
39380  * These classes are private internal classes
39381  */
39382 Roo.bootstrap.layout.Center = function(config){
39383     config.region = "center";
39384     Roo.bootstrap.layout.Region.call(this, config);
39385     this.visible = true;
39386     this.minWidth = config.minWidth || 20;
39387     this.minHeight = config.minHeight || 20;
39388 };
39389
39390 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39391     hide : function(){
39392         // center panel can't be hidden
39393     },
39394     
39395     show : function(){
39396         // center panel can't be hidden
39397     },
39398     
39399     getMinWidth: function(){
39400         return this.minWidth;
39401     },
39402     
39403     getMinHeight: function(){
39404         return this.minHeight;
39405     }
39406 });
39407
39408
39409
39410
39411  
39412
39413
39414
39415
39416
39417
39418 Roo.bootstrap.layout.North = function(config)
39419 {
39420     config.region = 'north';
39421     config.cursor = 'n-resize';
39422     
39423     Roo.bootstrap.layout.Split.call(this, config);
39424     
39425     
39426     if(this.split){
39427         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39428         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39429         this.split.el.addClass("roo-layout-split-v");
39430     }
39431     //var size = config.initialSize || config.height;
39432     //if(this.el && typeof size != "undefined"){
39433     //    this.el.setHeight(size);
39434     //}
39435 };
39436 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39437 {
39438     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39439      
39440      
39441     onRender : function(ctr, pos)
39442     {
39443         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39444         var size = this.config.initialSize || this.config.height;
39445         if(this.el && typeof size != "undefined"){
39446             this.el.setHeight(size);
39447         }
39448     
39449     },
39450     
39451     getBox : function(){
39452         if(this.collapsed){
39453             return this.collapsedEl.getBox();
39454         }
39455         var box = this.el.getBox();
39456         if(this.split){
39457             box.height += this.split.el.getHeight();
39458         }
39459         return box;
39460     },
39461     
39462     updateBox : function(box){
39463         if(this.split && !this.collapsed){
39464             box.height -= this.split.el.getHeight();
39465             this.split.el.setLeft(box.x);
39466             this.split.el.setTop(box.y+box.height);
39467             this.split.el.setWidth(box.width);
39468         }
39469         if(this.collapsed){
39470             this.updateBody(box.width, null);
39471         }
39472         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39473     }
39474 });
39475
39476
39477
39478
39479
39480 Roo.bootstrap.layout.South = function(config){
39481     config.region = 'south';
39482     config.cursor = 's-resize';
39483     Roo.bootstrap.layout.Split.call(this, config);
39484     if(this.split){
39485         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39486         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39487         this.split.el.addClass("roo-layout-split-v");
39488     }
39489     
39490 };
39491
39492 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39493     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39494     
39495     onRender : function(ctr, pos)
39496     {
39497         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39498         var size = this.config.initialSize || this.config.height;
39499         if(this.el && typeof size != "undefined"){
39500             this.el.setHeight(size);
39501         }
39502     
39503     },
39504     
39505     getBox : function(){
39506         if(this.collapsed){
39507             return this.collapsedEl.getBox();
39508         }
39509         var box = this.el.getBox();
39510         if(this.split){
39511             var sh = this.split.el.getHeight();
39512             box.height += sh;
39513             box.y -= sh;
39514         }
39515         return box;
39516     },
39517     
39518     updateBox : function(box){
39519         if(this.split && !this.collapsed){
39520             var sh = this.split.el.getHeight();
39521             box.height -= sh;
39522             box.y += sh;
39523             this.split.el.setLeft(box.x);
39524             this.split.el.setTop(box.y-sh);
39525             this.split.el.setWidth(box.width);
39526         }
39527         if(this.collapsed){
39528             this.updateBody(box.width, null);
39529         }
39530         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39531     }
39532 });
39533
39534 Roo.bootstrap.layout.East = function(config){
39535     config.region = "east";
39536     config.cursor = "e-resize";
39537     Roo.bootstrap.layout.Split.call(this, config);
39538     if(this.split){
39539         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39540         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39541         this.split.el.addClass("roo-layout-split-h");
39542     }
39543     
39544 };
39545 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39546     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39547     
39548     onRender : function(ctr, pos)
39549     {
39550         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39551         var size = this.config.initialSize || this.config.width;
39552         if(this.el && typeof size != "undefined"){
39553             this.el.setWidth(size);
39554         }
39555     
39556     },
39557     
39558     getBox : function(){
39559         if(this.collapsed){
39560             return this.collapsedEl.getBox();
39561         }
39562         var box = this.el.getBox();
39563         if(this.split){
39564             var sw = this.split.el.getWidth();
39565             box.width += sw;
39566             box.x -= sw;
39567         }
39568         return box;
39569     },
39570
39571     updateBox : function(box){
39572         if(this.split && !this.collapsed){
39573             var sw = this.split.el.getWidth();
39574             box.width -= sw;
39575             this.split.el.setLeft(box.x);
39576             this.split.el.setTop(box.y);
39577             this.split.el.setHeight(box.height);
39578             box.x += sw;
39579         }
39580         if(this.collapsed){
39581             this.updateBody(null, box.height);
39582         }
39583         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39584     }
39585 });
39586
39587 Roo.bootstrap.layout.West = function(config){
39588     config.region = "west";
39589     config.cursor = "w-resize";
39590     
39591     Roo.bootstrap.layout.Split.call(this, config);
39592     if(this.split){
39593         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39594         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39595         this.split.el.addClass("roo-layout-split-h");
39596     }
39597     
39598 };
39599 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39600     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39601     
39602     onRender: function(ctr, pos)
39603     {
39604         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39605         var size = this.config.initialSize || this.config.width;
39606         if(typeof size != "undefined"){
39607             this.el.setWidth(size);
39608         }
39609     },
39610     
39611     getBox : function(){
39612         if(this.collapsed){
39613             return this.collapsedEl.getBox();
39614         }
39615         var box = this.el.getBox();
39616         if (box.width == 0) {
39617             box.width = this.config.width; // kludge?
39618         }
39619         if(this.split){
39620             box.width += this.split.el.getWidth();
39621         }
39622         return box;
39623     },
39624     
39625     updateBox : function(box){
39626         if(this.split && !this.collapsed){
39627             var sw = this.split.el.getWidth();
39628             box.width -= sw;
39629             this.split.el.setLeft(box.x+box.width);
39630             this.split.el.setTop(box.y);
39631             this.split.el.setHeight(box.height);
39632         }
39633         if(this.collapsed){
39634             this.updateBody(null, box.height);
39635         }
39636         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39637     }
39638 });Roo.namespace("Roo.bootstrap.panel");/*
39639  * Based on:
39640  * Ext JS Library 1.1.1
39641  * Copyright(c) 2006-2007, Ext JS, LLC.
39642  *
39643  * Originally Released Under LGPL - original licence link has changed is not relivant.
39644  *
39645  * Fork - LGPL
39646  * <script type="text/javascript">
39647  */
39648 /**
39649  * @class Roo.ContentPanel
39650  * @extends Roo.util.Observable
39651  * A basic ContentPanel element.
39652  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39653  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39654  * @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
39655  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39656  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39657  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39658  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39659  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39660  * @cfg {String} title          The title for this panel
39661  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39662  * @cfg {String} url            Calls {@link #setUrl} with this value
39663  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39664  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39665  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39666  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39667  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39668  * @cfg {Boolean} badges render the badges
39669  * @cfg {String} cls  extra classes to use  
39670  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39671
39672  * @constructor
39673  * Create a new ContentPanel.
39674  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39675  * @param {String/Object} config A string to set only the title or a config object
39676  * @param {String} content (optional) Set the HTML content for this panel
39677  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39678  */
39679 Roo.bootstrap.panel.Content = function( config){
39680     
39681     this.tpl = config.tpl || false;
39682     
39683     var el = config.el;
39684     var content = config.content;
39685
39686     if(config.autoCreate){ // xtype is available if this is called from factory
39687         el = Roo.id();
39688     }
39689     this.el = Roo.get(el);
39690     if(!this.el && config && config.autoCreate){
39691         if(typeof config.autoCreate == "object"){
39692             if(!config.autoCreate.id){
39693                 config.autoCreate.id = config.id||el;
39694             }
39695             this.el = Roo.DomHelper.append(document.body,
39696                         config.autoCreate, true);
39697         }else{
39698             var elcfg =  {
39699                 tag: "div",
39700                 cls: (config.cls || '') +
39701                     (config.background ? ' bg-' + config.background : '') +
39702                     " roo-layout-inactive-content",
39703                 id: config.id||el
39704             };
39705             if (config.iframe) {
39706                 elcfg.cn = [
39707                     {
39708                         tag : 'iframe',
39709                         style : 'border: 0px',
39710                         src : 'about:blank'
39711                     }
39712                 ];
39713             }
39714               
39715             if (config.html) {
39716                 elcfg.html = config.html;
39717                 
39718             }
39719                         
39720             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39721             if (config.iframe) {
39722                 this.iframeEl = this.el.select('iframe',true).first();
39723             }
39724             
39725         }
39726     } 
39727     this.closable = false;
39728     this.loaded = false;
39729     this.active = false;
39730    
39731       
39732     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39733         
39734         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39735         
39736         this.wrapEl = this.el; //this.el.wrap();
39737         var ti = [];
39738         if (config.toolbar.items) {
39739             ti = config.toolbar.items ;
39740             delete config.toolbar.items ;
39741         }
39742         
39743         var nitems = [];
39744         this.toolbar.render(this.wrapEl, 'before');
39745         for(var i =0;i < ti.length;i++) {
39746           //  Roo.log(['add child', items[i]]);
39747             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39748         }
39749         this.toolbar.items = nitems;
39750         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39751         delete config.toolbar;
39752         
39753     }
39754     /*
39755     // xtype created footer. - not sure if will work as we normally have to render first..
39756     if (this.footer && !this.footer.el && this.footer.xtype) {
39757         if (!this.wrapEl) {
39758             this.wrapEl = this.el.wrap();
39759         }
39760     
39761         this.footer.container = this.wrapEl.createChild();
39762          
39763         this.footer = Roo.factory(this.footer, Roo);
39764         
39765     }
39766     */
39767     
39768      if(typeof config == "string"){
39769         this.title = config;
39770     }else{
39771         Roo.apply(this, config);
39772     }
39773     
39774     if(this.resizeEl){
39775         this.resizeEl = Roo.get(this.resizeEl, true);
39776     }else{
39777         this.resizeEl = this.el;
39778     }
39779     // handle view.xtype
39780     
39781  
39782     
39783     
39784     this.addEvents({
39785         /**
39786          * @event activate
39787          * Fires when this panel is activated. 
39788          * @param {Roo.ContentPanel} this
39789          */
39790         "activate" : true,
39791         /**
39792          * @event deactivate
39793          * Fires when this panel is activated. 
39794          * @param {Roo.ContentPanel} this
39795          */
39796         "deactivate" : true,
39797
39798         /**
39799          * @event resize
39800          * Fires when this panel is resized if fitToFrame is true.
39801          * @param {Roo.ContentPanel} this
39802          * @param {Number} width The width after any component adjustments
39803          * @param {Number} height The height after any component adjustments
39804          */
39805         "resize" : true,
39806         
39807          /**
39808          * @event render
39809          * Fires when this tab is created
39810          * @param {Roo.ContentPanel} this
39811          */
39812         "render" : true
39813         
39814         
39815         
39816     });
39817     
39818
39819     
39820     
39821     if(this.autoScroll && !this.iframe){
39822         this.resizeEl.setStyle("overflow", "auto");
39823     } else {
39824         // fix randome scrolling
39825         //this.el.on('scroll', function() {
39826         //    Roo.log('fix random scolling');
39827         //    this.scrollTo('top',0); 
39828         //});
39829     }
39830     content = content || this.content;
39831     if(content){
39832         this.setContent(content);
39833     }
39834     if(config && config.url){
39835         this.setUrl(this.url, this.params, this.loadOnce);
39836     }
39837     
39838     
39839     
39840     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39841     
39842     if (this.view && typeof(this.view.xtype) != 'undefined') {
39843         this.view.el = this.el.appendChild(document.createElement("div"));
39844         this.view = Roo.factory(this.view); 
39845         this.view.render  &&  this.view.render(false, '');  
39846     }
39847     
39848     
39849     this.fireEvent('render', this);
39850 };
39851
39852 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39853     
39854     cls : '',
39855     background : '',
39856     
39857     tabTip : '',
39858     
39859     iframe : false,
39860     iframeEl : false,
39861     
39862     setRegion : function(region){
39863         this.region = region;
39864         this.setActiveClass(region && !this.background);
39865     },
39866     
39867     
39868     setActiveClass: function(state)
39869     {
39870         if(state){
39871            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39872            this.el.setStyle('position','relative');
39873         }else{
39874            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39875            this.el.setStyle('position', 'absolute');
39876         } 
39877     },
39878     
39879     /**
39880      * Returns the toolbar for this Panel if one was configured. 
39881      * @return {Roo.Toolbar} 
39882      */
39883     getToolbar : function(){
39884         return this.toolbar;
39885     },
39886     
39887     setActiveState : function(active)
39888     {
39889         this.active = active;
39890         this.setActiveClass(active);
39891         if(!active){
39892             if(this.fireEvent("deactivate", this) === false){
39893                 return false;
39894             }
39895             return true;
39896         }
39897         this.fireEvent("activate", this);
39898         return true;
39899     },
39900     /**
39901      * Updates this panel's element (not for iframe)
39902      * @param {String} content The new content
39903      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39904     */
39905     setContent : function(content, loadScripts){
39906         if (this.iframe) {
39907             return;
39908         }
39909         
39910         this.el.update(content, loadScripts);
39911     },
39912
39913     ignoreResize : function(w, h){
39914         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39915             return true;
39916         }else{
39917             this.lastSize = {width: w, height: h};
39918             return false;
39919         }
39920     },
39921     /**
39922      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39923      * @return {Roo.UpdateManager} The UpdateManager
39924      */
39925     getUpdateManager : function(){
39926         if (this.iframe) {
39927             return false;
39928         }
39929         return this.el.getUpdateManager();
39930     },
39931      /**
39932      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39933      * Does not work with IFRAME contents
39934      * @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:
39935 <pre><code>
39936 panel.load({
39937     url: "your-url.php",
39938     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39939     callback: yourFunction,
39940     scope: yourObject, //(optional scope)
39941     discardUrl: false,
39942     nocache: false,
39943     text: "Loading...",
39944     timeout: 30,
39945     scripts: false
39946 });
39947 </code></pre>
39948      
39949      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39950      * 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.
39951      * @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}
39952      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39953      * @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.
39954      * @return {Roo.ContentPanel} this
39955      */
39956     load : function(){
39957         
39958         if (this.iframe) {
39959             return this;
39960         }
39961         
39962         var um = this.el.getUpdateManager();
39963         um.update.apply(um, arguments);
39964         return this;
39965     },
39966
39967
39968     /**
39969      * 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.
39970      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39971      * @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)
39972      * @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)
39973      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39974      */
39975     setUrl : function(url, params, loadOnce){
39976         if (this.iframe) {
39977             this.iframeEl.dom.src = url;
39978             return false;
39979         }
39980         
39981         if(this.refreshDelegate){
39982             this.removeListener("activate", this.refreshDelegate);
39983         }
39984         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39985         this.on("activate", this.refreshDelegate);
39986         return this.el.getUpdateManager();
39987     },
39988     
39989     _handleRefresh : function(url, params, loadOnce){
39990         if(!loadOnce || !this.loaded){
39991             var updater = this.el.getUpdateManager();
39992             updater.update(url, params, this._setLoaded.createDelegate(this));
39993         }
39994     },
39995     
39996     _setLoaded : function(){
39997         this.loaded = true;
39998     }, 
39999     
40000     /**
40001      * Returns this panel's id
40002      * @return {String} 
40003      */
40004     getId : function(){
40005         return this.el.id;
40006     },
40007     
40008     /** 
40009      * Returns this panel's element - used by regiosn to add.
40010      * @return {Roo.Element} 
40011      */
40012     getEl : function(){
40013         return this.wrapEl || this.el;
40014     },
40015     
40016    
40017     
40018     adjustForComponents : function(width, height)
40019     {
40020         //Roo.log('adjustForComponents ');
40021         if(this.resizeEl != this.el){
40022             width -= this.el.getFrameWidth('lr');
40023             height -= this.el.getFrameWidth('tb');
40024         }
40025         if(this.toolbar){
40026             var te = this.toolbar.getEl();
40027             te.setWidth(width);
40028             height -= te.getHeight();
40029         }
40030         if(this.footer){
40031             var te = this.footer.getEl();
40032             te.setWidth(width);
40033             height -= te.getHeight();
40034         }
40035         
40036         
40037         if(this.adjustments){
40038             width += this.adjustments[0];
40039             height += this.adjustments[1];
40040         }
40041         return {"width": width, "height": height};
40042     },
40043     
40044     setSize : function(width, height){
40045         if(this.fitToFrame && !this.ignoreResize(width, height)){
40046             if(this.fitContainer && this.resizeEl != this.el){
40047                 this.el.setSize(width, height);
40048             }
40049             var size = this.adjustForComponents(width, height);
40050             if (this.iframe) {
40051                 this.iframeEl.setSize(width,height);
40052             }
40053             
40054             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40055             this.fireEvent('resize', this, size.width, size.height);
40056             
40057             
40058         }
40059     },
40060     
40061     /**
40062      * Returns this panel's title
40063      * @return {String} 
40064      */
40065     getTitle : function(){
40066         
40067         if (typeof(this.title) != 'object') {
40068             return this.title;
40069         }
40070         
40071         var t = '';
40072         for (var k in this.title) {
40073             if (!this.title.hasOwnProperty(k)) {
40074                 continue;
40075             }
40076             
40077             if (k.indexOf('-') >= 0) {
40078                 var s = k.split('-');
40079                 for (var i = 0; i<s.length; i++) {
40080                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40081                 }
40082             } else {
40083                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40084             }
40085         }
40086         return t;
40087     },
40088     
40089     /**
40090      * Set this panel's title
40091      * @param {String} title
40092      */
40093     setTitle : function(title){
40094         this.title = title;
40095         if(this.region){
40096             this.region.updatePanelTitle(this, title);
40097         }
40098     },
40099     
40100     /**
40101      * Returns true is this panel was configured to be closable
40102      * @return {Boolean} 
40103      */
40104     isClosable : function(){
40105         return this.closable;
40106     },
40107     
40108     beforeSlide : function(){
40109         this.el.clip();
40110         this.resizeEl.clip();
40111     },
40112     
40113     afterSlide : function(){
40114         this.el.unclip();
40115         this.resizeEl.unclip();
40116     },
40117     
40118     /**
40119      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40120      *   Will fail silently if the {@link #setUrl} method has not been called.
40121      *   This does not activate the panel, just updates its content.
40122      */
40123     refresh : function(){
40124         if(this.refreshDelegate){
40125            this.loaded = false;
40126            this.refreshDelegate();
40127         }
40128     },
40129     
40130     /**
40131      * Destroys this panel
40132      */
40133     destroy : function(){
40134         this.el.removeAllListeners();
40135         var tempEl = document.createElement("span");
40136         tempEl.appendChild(this.el.dom);
40137         tempEl.innerHTML = "";
40138         this.el.remove();
40139         this.el = null;
40140     },
40141     
40142     /**
40143      * form - if the content panel contains a form - this is a reference to it.
40144      * @type {Roo.form.Form}
40145      */
40146     form : false,
40147     /**
40148      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40149      *    This contains a reference to it.
40150      * @type {Roo.View}
40151      */
40152     view : false,
40153     
40154       /**
40155      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40156      * <pre><code>
40157
40158 layout.addxtype({
40159        xtype : 'Form',
40160        items: [ .... ]
40161    }
40162 );
40163
40164 </code></pre>
40165      * @param {Object} cfg Xtype definition of item to add.
40166      */
40167     
40168     
40169     getChildContainer: function () {
40170         return this.getEl();
40171     }
40172     
40173     
40174     /*
40175         var  ret = new Roo.factory(cfg);
40176         return ret;
40177         
40178         
40179         // add form..
40180         if (cfg.xtype.match(/^Form$/)) {
40181             
40182             var el;
40183             //if (this.footer) {
40184             //    el = this.footer.container.insertSibling(false, 'before');
40185             //} else {
40186                 el = this.el.createChild();
40187             //}
40188
40189             this.form = new  Roo.form.Form(cfg);
40190             
40191             
40192             if ( this.form.allItems.length) {
40193                 this.form.render(el.dom);
40194             }
40195             return this.form;
40196         }
40197         // should only have one of theses..
40198         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40199             // views.. should not be just added - used named prop 'view''
40200             
40201             cfg.el = this.el.appendChild(document.createElement("div"));
40202             // factory?
40203             
40204             var ret = new Roo.factory(cfg);
40205              
40206              ret.render && ret.render(false, ''); // render blank..
40207             this.view = ret;
40208             return ret;
40209         }
40210         return false;
40211     }
40212     \*/
40213 });
40214  
40215 /**
40216  * @class Roo.bootstrap.panel.Grid
40217  * @extends Roo.bootstrap.panel.Content
40218  * @constructor
40219  * Create a new GridPanel.
40220  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40221  * @param {Object} config A the config object
40222   
40223  */
40224
40225
40226
40227 Roo.bootstrap.panel.Grid = function(config)
40228 {
40229     
40230       
40231     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40232         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40233
40234     config.el = this.wrapper;
40235     //this.el = this.wrapper;
40236     
40237       if (config.container) {
40238         // ctor'ed from a Border/panel.grid
40239         
40240         
40241         this.wrapper.setStyle("overflow", "hidden");
40242         this.wrapper.addClass('roo-grid-container');
40243
40244     }
40245     
40246     
40247     if(config.toolbar){
40248         var tool_el = this.wrapper.createChild();    
40249         this.toolbar = Roo.factory(config.toolbar);
40250         var ti = [];
40251         if (config.toolbar.items) {
40252             ti = config.toolbar.items ;
40253             delete config.toolbar.items ;
40254         }
40255         
40256         var nitems = [];
40257         this.toolbar.render(tool_el);
40258         for(var i =0;i < ti.length;i++) {
40259           //  Roo.log(['add child', items[i]]);
40260             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40261         }
40262         this.toolbar.items = nitems;
40263         
40264         delete config.toolbar;
40265     }
40266     
40267     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40268     config.grid.scrollBody = true;;
40269     config.grid.monitorWindowResize = false; // turn off autosizing
40270     config.grid.autoHeight = false;
40271     config.grid.autoWidth = false;
40272     
40273     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40274     
40275     if (config.background) {
40276         // render grid on panel activation (if panel background)
40277         this.on('activate', function(gp) {
40278             if (!gp.grid.rendered) {
40279                 gp.grid.render(this.wrapper);
40280                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40281             }
40282         });
40283             
40284     } else {
40285         this.grid.render(this.wrapper);
40286         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40287
40288     }
40289     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40290     // ??? needed ??? config.el = this.wrapper;
40291     
40292     
40293     
40294   
40295     // xtype created footer. - not sure if will work as we normally have to render first..
40296     if (this.footer && !this.footer.el && this.footer.xtype) {
40297         
40298         var ctr = this.grid.getView().getFooterPanel(true);
40299         this.footer.dataSource = this.grid.dataSource;
40300         this.footer = Roo.factory(this.footer, Roo);
40301         this.footer.render(ctr);
40302         
40303     }
40304     
40305     
40306     
40307     
40308      
40309 };
40310
40311 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40312     getId : function(){
40313         return this.grid.id;
40314     },
40315     
40316     /**
40317      * Returns the grid for this panel
40318      * @return {Roo.bootstrap.Table} 
40319      */
40320     getGrid : function(){
40321         return this.grid;    
40322     },
40323     
40324     setSize : function(width, height){
40325         if(!this.ignoreResize(width, height)){
40326             var grid = this.grid;
40327             var size = this.adjustForComponents(width, height);
40328             // tfoot is not a footer?
40329           
40330             
40331             var gridel = grid.getGridEl();
40332             gridel.setSize(size.width, size.height);
40333             
40334             var tbd = grid.getGridEl().select('tbody', true).first();
40335             var thd = grid.getGridEl().select('thead',true).first();
40336             var tbf= grid.getGridEl().select('tfoot', true).first();
40337
40338             if (tbf) {
40339                 size.height -= tbf.getHeight();
40340             }
40341             if (thd) {
40342                 size.height -= thd.getHeight();
40343             }
40344             
40345             tbd.setSize(size.width, size.height );
40346             // this is for the account management tab -seems to work there.
40347             var thd = grid.getGridEl().select('thead',true).first();
40348             //if (tbd) {
40349             //    tbd.setSize(size.width, size.height - thd.getHeight());
40350             //}
40351              
40352             grid.autoSize();
40353         }
40354     },
40355      
40356     
40357     
40358     beforeSlide : function(){
40359         this.grid.getView().scroller.clip();
40360     },
40361     
40362     afterSlide : function(){
40363         this.grid.getView().scroller.unclip();
40364     },
40365     
40366     destroy : function(){
40367         this.grid.destroy();
40368         delete this.grid;
40369         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40370     }
40371 });
40372
40373 /**
40374  * @class Roo.bootstrap.panel.Nest
40375  * @extends Roo.bootstrap.panel.Content
40376  * @constructor
40377  * Create a new Panel, that can contain a layout.Border.
40378  * 
40379  * 
40380  * @param {Roo.BorderLayout} layout The layout for this panel
40381  * @param {String/Object} config A string to set only the title or a config object
40382  */
40383 Roo.bootstrap.panel.Nest = function(config)
40384 {
40385     // construct with only one argument..
40386     /* FIXME - implement nicer consturctors
40387     if (layout.layout) {
40388         config = layout;
40389         layout = config.layout;
40390         delete config.layout;
40391     }
40392     if (layout.xtype && !layout.getEl) {
40393         // then layout needs constructing..
40394         layout = Roo.factory(layout, Roo);
40395     }
40396     */
40397     
40398     config.el =  config.layout.getEl();
40399     
40400     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40401     
40402     config.layout.monitorWindowResize = false; // turn off autosizing
40403     this.layout = config.layout;
40404     this.layout.getEl().addClass("roo-layout-nested-layout");
40405     this.layout.parent = this;
40406     
40407     
40408     
40409     
40410 };
40411
40412 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40413
40414     setSize : function(width, height){
40415         if(!this.ignoreResize(width, height)){
40416             var size = this.adjustForComponents(width, height);
40417             var el = this.layout.getEl();
40418             if (size.height < 1) {
40419                 el.setWidth(size.width);   
40420             } else {
40421                 el.setSize(size.width, size.height);
40422             }
40423             var touch = el.dom.offsetWidth;
40424             this.layout.layout();
40425             // ie requires a double layout on the first pass
40426             if(Roo.isIE && !this.initialized){
40427                 this.initialized = true;
40428                 this.layout.layout();
40429             }
40430         }
40431     },
40432     
40433     // activate all subpanels if not currently active..
40434     
40435     setActiveState : function(active){
40436         this.active = active;
40437         this.setActiveClass(active);
40438         
40439         if(!active){
40440             this.fireEvent("deactivate", this);
40441             return;
40442         }
40443         
40444         this.fireEvent("activate", this);
40445         // not sure if this should happen before or after..
40446         if (!this.layout) {
40447             return; // should not happen..
40448         }
40449         var reg = false;
40450         for (var r in this.layout.regions) {
40451             reg = this.layout.getRegion(r);
40452             if (reg.getActivePanel()) {
40453                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40454                 reg.setActivePanel(reg.getActivePanel());
40455                 continue;
40456             }
40457             if (!reg.panels.length) {
40458                 continue;
40459             }
40460             reg.showPanel(reg.getPanel(0));
40461         }
40462         
40463         
40464         
40465         
40466     },
40467     
40468     /**
40469      * Returns the nested BorderLayout for this panel
40470      * @return {Roo.BorderLayout} 
40471      */
40472     getLayout : function(){
40473         return this.layout;
40474     },
40475     
40476      /**
40477      * Adds a xtype elements to the layout of the nested panel
40478      * <pre><code>
40479
40480 panel.addxtype({
40481        xtype : 'ContentPanel',
40482        region: 'west',
40483        items: [ .... ]
40484    }
40485 );
40486
40487 panel.addxtype({
40488         xtype : 'NestedLayoutPanel',
40489         region: 'west',
40490         layout: {
40491            center: { },
40492            west: { }   
40493         },
40494         items : [ ... list of content panels or nested layout panels.. ]
40495    }
40496 );
40497 </code></pre>
40498      * @param {Object} cfg Xtype definition of item to add.
40499      */
40500     addxtype : function(cfg) {
40501         return this.layout.addxtype(cfg);
40502     
40503     }
40504 });/*
40505  * Based on:
40506  * Ext JS Library 1.1.1
40507  * Copyright(c) 2006-2007, Ext JS, LLC.
40508  *
40509  * Originally Released Under LGPL - original licence link has changed is not relivant.
40510  *
40511  * Fork - LGPL
40512  * <script type="text/javascript">
40513  */
40514 /**
40515  * @class Roo.TabPanel
40516  * @extends Roo.util.Observable
40517  * A lightweight tab container.
40518  * <br><br>
40519  * Usage:
40520  * <pre><code>
40521 // basic tabs 1, built from existing content
40522 var tabs = new Roo.TabPanel("tabs1");
40523 tabs.addTab("script", "View Script");
40524 tabs.addTab("markup", "View Markup");
40525 tabs.activate("script");
40526
40527 // more advanced tabs, built from javascript
40528 var jtabs = new Roo.TabPanel("jtabs");
40529 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40530
40531 // set up the UpdateManager
40532 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40533 var updater = tab2.getUpdateManager();
40534 updater.setDefaultUrl("ajax1.htm");
40535 tab2.on('activate', updater.refresh, updater, true);
40536
40537 // Use setUrl for Ajax loading
40538 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40539 tab3.setUrl("ajax2.htm", null, true);
40540
40541 // Disabled tab
40542 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40543 tab4.disable();
40544
40545 jtabs.activate("jtabs-1");
40546  * </code></pre>
40547  * @constructor
40548  * Create a new TabPanel.
40549  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40550  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40551  */
40552 Roo.bootstrap.panel.Tabs = function(config){
40553     /**
40554     * The container element for this TabPanel.
40555     * @type Roo.Element
40556     */
40557     this.el = Roo.get(config.el);
40558     delete config.el;
40559     if(config){
40560         if(typeof config == "boolean"){
40561             this.tabPosition = config ? "bottom" : "top";
40562         }else{
40563             Roo.apply(this, config);
40564         }
40565     }
40566     
40567     if(this.tabPosition == "bottom"){
40568         // if tabs are at the bottom = create the body first.
40569         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40570         this.el.addClass("roo-tabs-bottom");
40571     }
40572     // next create the tabs holders
40573     
40574     if (this.tabPosition == "west"){
40575         
40576         var reg = this.region; // fake it..
40577         while (reg) {
40578             if (!reg.mgr.parent) {
40579                 break;
40580             }
40581             reg = reg.mgr.parent.region;
40582         }
40583         Roo.log("got nest?");
40584         Roo.log(reg);
40585         if (reg.mgr.getRegion('west')) {
40586             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40587             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40588             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40589             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40590             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40591         
40592             
40593         }
40594         
40595         
40596     } else {
40597      
40598         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40599         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40600         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40601         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40602     }
40603     
40604     
40605     if(Roo.isIE){
40606         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40607     }
40608     
40609     // finally - if tabs are at the top, then create the body last..
40610     if(this.tabPosition != "bottom"){
40611         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40612          * @type Roo.Element
40613          */
40614         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40615         this.el.addClass("roo-tabs-top");
40616     }
40617     this.items = [];
40618
40619     this.bodyEl.setStyle("position", "relative");
40620
40621     this.active = null;
40622     this.activateDelegate = this.activate.createDelegate(this);
40623
40624     this.addEvents({
40625         /**
40626          * @event tabchange
40627          * Fires when the active tab changes
40628          * @param {Roo.TabPanel} this
40629          * @param {Roo.TabPanelItem} activePanel The new active tab
40630          */
40631         "tabchange": true,
40632         /**
40633          * @event beforetabchange
40634          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40635          * @param {Roo.TabPanel} this
40636          * @param {Object} e Set cancel to true on this object to cancel the tab change
40637          * @param {Roo.TabPanelItem} tab The tab being changed to
40638          */
40639         "beforetabchange" : true
40640     });
40641
40642     Roo.EventManager.onWindowResize(this.onResize, this);
40643     this.cpad = this.el.getPadding("lr");
40644     this.hiddenCount = 0;
40645
40646
40647     // toolbar on the tabbar support...
40648     if (this.toolbar) {
40649         alert("no toolbar support yet");
40650         this.toolbar  = false;
40651         /*
40652         var tcfg = this.toolbar;
40653         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40654         this.toolbar = new Roo.Toolbar(tcfg);
40655         if (Roo.isSafari) {
40656             var tbl = tcfg.container.child('table', true);
40657             tbl.setAttribute('width', '100%');
40658         }
40659         */
40660         
40661     }
40662    
40663
40664
40665     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40666 };
40667
40668 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40669     /*
40670      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40671      */
40672     tabPosition : "top",
40673     /*
40674      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40675      */
40676     currentTabWidth : 0,
40677     /*
40678      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40679      */
40680     minTabWidth : 40,
40681     /*
40682      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40683      */
40684     maxTabWidth : 250,
40685     /*
40686      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40687      */
40688     preferredTabWidth : 175,
40689     /*
40690      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40691      */
40692     resizeTabs : false,
40693     /*
40694      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40695      */
40696     monitorResize : true,
40697     /*
40698      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40699      */
40700     toolbar : false,  // set by caller..
40701     
40702     region : false, /// set by caller
40703     
40704     disableTooltips : true, // not used yet...
40705
40706     /**
40707      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40708      * @param {String} id The id of the div to use <b>or create</b>
40709      * @param {String} text The text for the tab
40710      * @param {String} content (optional) Content to put in the TabPanelItem body
40711      * @param {Boolean} closable (optional) True to create a close icon on the tab
40712      * @return {Roo.TabPanelItem} The created TabPanelItem
40713      */
40714     addTab : function(id, text, content, closable, tpl)
40715     {
40716         var item = new Roo.bootstrap.panel.TabItem({
40717             panel: this,
40718             id : id,
40719             text : text,
40720             closable : closable,
40721             tpl : tpl
40722         });
40723         this.addTabItem(item);
40724         if(content){
40725             item.setContent(content);
40726         }
40727         return item;
40728     },
40729
40730     /**
40731      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40732      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40733      * @return {Roo.TabPanelItem}
40734      */
40735     getTab : function(id){
40736         return this.items[id];
40737     },
40738
40739     /**
40740      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40741      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40742      */
40743     hideTab : function(id){
40744         var t = this.items[id];
40745         if(!t.isHidden()){
40746            t.setHidden(true);
40747            this.hiddenCount++;
40748            this.autoSizeTabs();
40749         }
40750     },
40751
40752     /**
40753      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40754      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40755      */
40756     unhideTab : function(id){
40757         var t = this.items[id];
40758         if(t.isHidden()){
40759            t.setHidden(false);
40760            this.hiddenCount--;
40761            this.autoSizeTabs();
40762         }
40763     },
40764
40765     /**
40766      * Adds an existing {@link Roo.TabPanelItem}.
40767      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40768      */
40769     addTabItem : function(item)
40770     {
40771         this.items[item.id] = item;
40772         this.items.push(item);
40773         this.autoSizeTabs();
40774       //  if(this.resizeTabs){
40775     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40776   //         this.autoSizeTabs();
40777 //        }else{
40778 //            item.autoSize();
40779        // }
40780     },
40781
40782     /**
40783      * Removes a {@link Roo.TabPanelItem}.
40784      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40785      */
40786     removeTab : function(id){
40787         var items = this.items;
40788         var tab = items[id];
40789         if(!tab) { return; }
40790         var index = items.indexOf(tab);
40791         if(this.active == tab && items.length > 1){
40792             var newTab = this.getNextAvailable(index);
40793             if(newTab) {
40794                 newTab.activate();
40795             }
40796         }
40797         this.stripEl.dom.removeChild(tab.pnode.dom);
40798         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40799             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40800         }
40801         items.splice(index, 1);
40802         delete this.items[tab.id];
40803         tab.fireEvent("close", tab);
40804         tab.purgeListeners();
40805         this.autoSizeTabs();
40806     },
40807
40808     getNextAvailable : function(start){
40809         var items = this.items;
40810         var index = start;
40811         // look for a next tab that will slide over to
40812         // replace the one being removed
40813         while(index < items.length){
40814             var item = items[++index];
40815             if(item && !item.isHidden()){
40816                 return item;
40817             }
40818         }
40819         // if one isn't found select the previous tab (on the left)
40820         index = start;
40821         while(index >= 0){
40822             var item = items[--index];
40823             if(item && !item.isHidden()){
40824                 return item;
40825             }
40826         }
40827         return null;
40828     },
40829
40830     /**
40831      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40832      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40833      */
40834     disableTab : function(id){
40835         var tab = this.items[id];
40836         if(tab && this.active != tab){
40837             tab.disable();
40838         }
40839     },
40840
40841     /**
40842      * Enables a {@link Roo.TabPanelItem} that is disabled.
40843      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40844      */
40845     enableTab : function(id){
40846         var tab = this.items[id];
40847         tab.enable();
40848     },
40849
40850     /**
40851      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40852      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40853      * @return {Roo.TabPanelItem} The TabPanelItem.
40854      */
40855     activate : function(id)
40856     {
40857         //Roo.log('activite:'  + id);
40858         
40859         var tab = this.items[id];
40860         if(!tab){
40861             return null;
40862         }
40863         if(tab == this.active || tab.disabled){
40864             return tab;
40865         }
40866         var e = {};
40867         this.fireEvent("beforetabchange", this, e, tab);
40868         if(e.cancel !== true && !tab.disabled){
40869             if(this.active){
40870                 this.active.hide();
40871             }
40872             this.active = this.items[id];
40873             this.active.show();
40874             this.fireEvent("tabchange", this, this.active);
40875         }
40876         return tab;
40877     },
40878
40879     /**
40880      * Gets the active {@link Roo.TabPanelItem}.
40881      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40882      */
40883     getActiveTab : function(){
40884         return this.active;
40885     },
40886
40887     /**
40888      * Updates the tab body element to fit the height of the container element
40889      * for overflow scrolling
40890      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40891      */
40892     syncHeight : function(targetHeight){
40893         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40894         var bm = this.bodyEl.getMargins();
40895         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40896         this.bodyEl.setHeight(newHeight);
40897         return newHeight;
40898     },
40899
40900     onResize : function(){
40901         if(this.monitorResize){
40902             this.autoSizeTabs();
40903         }
40904     },
40905
40906     /**
40907      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40908      */
40909     beginUpdate : function(){
40910         this.updating = true;
40911     },
40912
40913     /**
40914      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40915      */
40916     endUpdate : function(){
40917         this.updating = false;
40918         this.autoSizeTabs();
40919     },
40920
40921     /**
40922      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40923      */
40924     autoSizeTabs : function()
40925     {
40926         var count = this.items.length;
40927         var vcount = count - this.hiddenCount;
40928         
40929         if (vcount < 2) {
40930             this.stripEl.hide();
40931         } else {
40932             this.stripEl.show();
40933         }
40934         
40935         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40936             return;
40937         }
40938         
40939         
40940         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40941         var availWidth = Math.floor(w / vcount);
40942         var b = this.stripBody;
40943         if(b.getWidth() > w){
40944             var tabs = this.items;
40945             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40946             if(availWidth < this.minTabWidth){
40947                 /*if(!this.sleft){    // incomplete scrolling code
40948                     this.createScrollButtons();
40949                 }
40950                 this.showScroll();
40951                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40952             }
40953         }else{
40954             if(this.currentTabWidth < this.preferredTabWidth){
40955                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40956             }
40957         }
40958     },
40959
40960     /**
40961      * Returns the number of tabs in this TabPanel.
40962      * @return {Number}
40963      */
40964      getCount : function(){
40965          return this.items.length;
40966      },
40967
40968     /**
40969      * Resizes all the tabs to the passed width
40970      * @param {Number} The new width
40971      */
40972     setTabWidth : function(width){
40973         this.currentTabWidth = width;
40974         for(var i = 0, len = this.items.length; i < len; i++) {
40975                 if(!this.items[i].isHidden()) {
40976                 this.items[i].setWidth(width);
40977             }
40978         }
40979     },
40980
40981     /**
40982      * Destroys this TabPanel
40983      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40984      */
40985     destroy : function(removeEl){
40986         Roo.EventManager.removeResizeListener(this.onResize, this);
40987         for(var i = 0, len = this.items.length; i < len; i++){
40988             this.items[i].purgeListeners();
40989         }
40990         if(removeEl === true){
40991             this.el.update("");
40992             this.el.remove();
40993         }
40994     },
40995     
40996     createStrip : function(container)
40997     {
40998         var strip = document.createElement("nav");
40999         strip.className = Roo.bootstrap.version == 4 ?
41000             "navbar-light bg-light" : 
41001             "navbar navbar-default"; //"x-tabs-wrap";
41002         container.appendChild(strip);
41003         return strip;
41004     },
41005     
41006     createStripList : function(strip)
41007     {
41008         // div wrapper for retard IE
41009         // returns the "tr" element.
41010         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41011         //'<div class="x-tabs-strip-wrap">'+
41012           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41013           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41014         return strip.firstChild; //.firstChild.firstChild.firstChild;
41015     },
41016     createBody : function(container)
41017     {
41018         var body = document.createElement("div");
41019         Roo.id(body, "tab-body");
41020         //Roo.fly(body).addClass("x-tabs-body");
41021         Roo.fly(body).addClass("tab-content");
41022         container.appendChild(body);
41023         return body;
41024     },
41025     createItemBody :function(bodyEl, id){
41026         var body = Roo.getDom(id);
41027         if(!body){
41028             body = document.createElement("div");
41029             body.id = id;
41030         }
41031         //Roo.fly(body).addClass("x-tabs-item-body");
41032         Roo.fly(body).addClass("tab-pane");
41033          bodyEl.insertBefore(body, bodyEl.firstChild);
41034         return body;
41035     },
41036     /** @private */
41037     createStripElements :  function(stripEl, text, closable, tpl)
41038     {
41039         var td = document.createElement("li"); // was td..
41040         td.className = 'nav-item';
41041         
41042         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41043         
41044         
41045         stripEl.appendChild(td);
41046         /*if(closable){
41047             td.className = "x-tabs-closable";
41048             if(!this.closeTpl){
41049                 this.closeTpl = new Roo.Template(
41050                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41051                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41052                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41053                 );
41054             }
41055             var el = this.closeTpl.overwrite(td, {"text": text});
41056             var close = el.getElementsByTagName("div")[0];
41057             var inner = el.getElementsByTagName("em")[0];
41058             return {"el": el, "close": close, "inner": inner};
41059         } else {
41060         */
41061         // not sure what this is..
41062 //            if(!this.tabTpl){
41063                 //this.tabTpl = new Roo.Template(
41064                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41065                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41066                 //);
41067 //                this.tabTpl = new Roo.Template(
41068 //                   '<a href="#">' +
41069 //                   '<span unselectable="on"' +
41070 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41071 //                            ' >{text}</span></a>'
41072 //                );
41073 //                
41074 //            }
41075
41076
41077             var template = tpl || this.tabTpl || false;
41078             
41079             if(!template){
41080                 template =  new Roo.Template(
41081                         Roo.bootstrap.version == 4 ? 
41082                             (
41083                                 '<a class="nav-link" href="#" unselectable="on"' +
41084                                      (this.disableTooltips ? '' : ' title="{text}"') +
41085                                      ' >{text}</a>'
41086                             ) : (
41087                                 '<a class="nav-link" href="#">' +
41088                                 '<span unselectable="on"' +
41089                                          (this.disableTooltips ? '' : ' title="{text}"') +
41090                                     ' >{text}</span></a>'
41091                             )
41092                 );
41093             }
41094             
41095             switch (typeof(template)) {
41096                 case 'object' :
41097                     break;
41098                 case 'string' :
41099                     template = new Roo.Template(template);
41100                     break;
41101                 default :
41102                     break;
41103             }
41104             
41105             var el = template.overwrite(td, {"text": text});
41106             
41107             var inner = el.getElementsByTagName("span")[0];
41108             
41109             return {"el": el, "inner": inner};
41110             
41111     }
41112         
41113     
41114 });
41115
41116 /**
41117  * @class Roo.TabPanelItem
41118  * @extends Roo.util.Observable
41119  * Represents an individual item (tab plus body) in a TabPanel.
41120  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41121  * @param {String} id The id of this TabPanelItem
41122  * @param {String} text The text for the tab of this TabPanelItem
41123  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41124  */
41125 Roo.bootstrap.panel.TabItem = function(config){
41126     /**
41127      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41128      * @type Roo.TabPanel
41129      */
41130     this.tabPanel = config.panel;
41131     /**
41132      * The id for this TabPanelItem
41133      * @type String
41134      */
41135     this.id = config.id;
41136     /** @private */
41137     this.disabled = false;
41138     /** @private */
41139     this.text = config.text;
41140     /** @private */
41141     this.loaded = false;
41142     this.closable = config.closable;
41143
41144     /**
41145      * The body element for this TabPanelItem.
41146      * @type Roo.Element
41147      */
41148     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41149     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41150     this.bodyEl.setStyle("display", "block");
41151     this.bodyEl.setStyle("zoom", "1");
41152     //this.hideAction();
41153
41154     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41155     /** @private */
41156     this.el = Roo.get(els.el);
41157     this.inner = Roo.get(els.inner, true);
41158      this.textEl = Roo.bootstrap.version == 4 ?
41159         this.el : Roo.get(this.el.dom.firstChild, true);
41160
41161     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41162     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41163
41164     
41165 //    this.el.on("mousedown", this.onTabMouseDown, this);
41166     this.el.on("click", this.onTabClick, this);
41167     /** @private */
41168     if(config.closable){
41169         var c = Roo.get(els.close, true);
41170         c.dom.title = this.closeText;
41171         c.addClassOnOver("close-over");
41172         c.on("click", this.closeClick, this);
41173      }
41174
41175     this.addEvents({
41176          /**
41177          * @event activate
41178          * Fires when this tab becomes the active tab.
41179          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41180          * @param {Roo.TabPanelItem} this
41181          */
41182         "activate": true,
41183         /**
41184          * @event beforeclose
41185          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41186          * @param {Roo.TabPanelItem} this
41187          * @param {Object} e Set cancel to true on this object to cancel the close.
41188          */
41189         "beforeclose": true,
41190         /**
41191          * @event close
41192          * Fires when this tab is closed.
41193          * @param {Roo.TabPanelItem} this
41194          */
41195          "close": true,
41196         /**
41197          * @event deactivate
41198          * Fires when this tab is no longer the active tab.
41199          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41200          * @param {Roo.TabPanelItem} this
41201          */
41202          "deactivate" : true
41203     });
41204     this.hidden = false;
41205
41206     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41207 };
41208
41209 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41210            {
41211     purgeListeners : function(){
41212        Roo.util.Observable.prototype.purgeListeners.call(this);
41213        this.el.removeAllListeners();
41214     },
41215     /**
41216      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41217      */
41218     show : function(){
41219         this.status_node.addClass("active");
41220         this.showAction();
41221         if(Roo.isOpera){
41222             this.tabPanel.stripWrap.repaint();
41223         }
41224         this.fireEvent("activate", this.tabPanel, this);
41225     },
41226
41227     /**
41228      * Returns true if this tab is the active tab.
41229      * @return {Boolean}
41230      */
41231     isActive : function(){
41232         return this.tabPanel.getActiveTab() == this;
41233     },
41234
41235     /**
41236      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41237      */
41238     hide : function(){
41239         this.status_node.removeClass("active");
41240         this.hideAction();
41241         this.fireEvent("deactivate", this.tabPanel, this);
41242     },
41243
41244     hideAction : function(){
41245         this.bodyEl.hide();
41246         this.bodyEl.setStyle("position", "absolute");
41247         this.bodyEl.setLeft("-20000px");
41248         this.bodyEl.setTop("-20000px");
41249     },
41250
41251     showAction : function(){
41252         this.bodyEl.setStyle("position", "relative");
41253         this.bodyEl.setTop("");
41254         this.bodyEl.setLeft("");
41255         this.bodyEl.show();
41256     },
41257
41258     /**
41259      * Set the tooltip for the tab.
41260      * @param {String} tooltip The tab's tooltip
41261      */
41262     setTooltip : function(text){
41263         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41264             this.textEl.dom.qtip = text;
41265             this.textEl.dom.removeAttribute('title');
41266         }else{
41267             this.textEl.dom.title = text;
41268         }
41269     },
41270
41271     onTabClick : function(e){
41272         e.preventDefault();
41273         this.tabPanel.activate(this.id);
41274     },
41275
41276     onTabMouseDown : function(e){
41277         e.preventDefault();
41278         this.tabPanel.activate(this.id);
41279     },
41280 /*
41281     getWidth : function(){
41282         return this.inner.getWidth();
41283     },
41284
41285     setWidth : function(width){
41286         var iwidth = width - this.linode.getPadding("lr");
41287         this.inner.setWidth(iwidth);
41288         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41289         this.linode.setWidth(width);
41290     },
41291 */
41292     /**
41293      * Show or hide the tab
41294      * @param {Boolean} hidden True to hide or false to show.
41295      */
41296     setHidden : function(hidden){
41297         this.hidden = hidden;
41298         this.linode.setStyle("display", hidden ? "none" : "");
41299     },
41300
41301     /**
41302      * Returns true if this tab is "hidden"
41303      * @return {Boolean}
41304      */
41305     isHidden : function(){
41306         return this.hidden;
41307     },
41308
41309     /**
41310      * Returns the text for this tab
41311      * @return {String}
41312      */
41313     getText : function(){
41314         return this.text;
41315     },
41316     /*
41317     autoSize : function(){
41318         //this.el.beginMeasure();
41319         this.textEl.setWidth(1);
41320         /*
41321          *  #2804 [new] Tabs in Roojs
41322          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41323          */
41324         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41325         //this.el.endMeasure();
41326     //},
41327
41328     /**
41329      * Sets the text for the tab (Note: this also sets the tooltip text)
41330      * @param {String} text The tab's text and tooltip
41331      */
41332     setText : function(text){
41333         this.text = text;
41334         this.textEl.update(text);
41335         this.setTooltip(text);
41336         //if(!this.tabPanel.resizeTabs){
41337         //    this.autoSize();
41338         //}
41339     },
41340     /**
41341      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41342      */
41343     activate : function(){
41344         this.tabPanel.activate(this.id);
41345     },
41346
41347     /**
41348      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41349      */
41350     disable : function(){
41351         if(this.tabPanel.active != this){
41352             this.disabled = true;
41353             this.status_node.addClass("disabled");
41354         }
41355     },
41356
41357     /**
41358      * Enables this TabPanelItem if it was previously disabled.
41359      */
41360     enable : function(){
41361         this.disabled = false;
41362         this.status_node.removeClass("disabled");
41363     },
41364
41365     /**
41366      * Sets the content for this TabPanelItem.
41367      * @param {String} content The content
41368      * @param {Boolean} loadScripts true to look for and load scripts
41369      */
41370     setContent : function(content, loadScripts){
41371         this.bodyEl.update(content, loadScripts);
41372     },
41373
41374     /**
41375      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41376      * @return {Roo.UpdateManager} The UpdateManager
41377      */
41378     getUpdateManager : function(){
41379         return this.bodyEl.getUpdateManager();
41380     },
41381
41382     /**
41383      * Set a URL to be used to load the content for this TabPanelItem.
41384      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41385      * @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)
41386      * @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)
41387      * @return {Roo.UpdateManager} The UpdateManager
41388      */
41389     setUrl : function(url, params, loadOnce){
41390         if(this.refreshDelegate){
41391             this.un('activate', this.refreshDelegate);
41392         }
41393         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41394         this.on("activate", this.refreshDelegate);
41395         return this.bodyEl.getUpdateManager();
41396     },
41397
41398     /** @private */
41399     _handleRefresh : function(url, params, loadOnce){
41400         if(!loadOnce || !this.loaded){
41401             var updater = this.bodyEl.getUpdateManager();
41402             updater.update(url, params, this._setLoaded.createDelegate(this));
41403         }
41404     },
41405
41406     /**
41407      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41408      *   Will fail silently if the setUrl method has not been called.
41409      *   This does not activate the panel, just updates its content.
41410      */
41411     refresh : function(){
41412         if(this.refreshDelegate){
41413            this.loaded = false;
41414            this.refreshDelegate();
41415         }
41416     },
41417
41418     /** @private */
41419     _setLoaded : function(){
41420         this.loaded = true;
41421     },
41422
41423     /** @private */
41424     closeClick : function(e){
41425         var o = {};
41426         e.stopEvent();
41427         this.fireEvent("beforeclose", this, o);
41428         if(o.cancel !== true){
41429             this.tabPanel.removeTab(this.id);
41430         }
41431     },
41432     /**
41433      * The text displayed in the tooltip for the close icon.
41434      * @type String
41435      */
41436     closeText : "Close this tab"
41437 });
41438 /**
41439 *    This script refer to:
41440 *    Title: International Telephone Input
41441 *    Author: Jack O'Connor
41442 *    Code version:  v12.1.12
41443 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41444 **/
41445
41446 Roo.bootstrap.PhoneInputData = function() {
41447     var d = [
41448       [
41449         "Afghanistan (‫افغانستان‬‎)",
41450         "af",
41451         "93"
41452       ],
41453       [
41454         "Albania (Shqipëri)",
41455         "al",
41456         "355"
41457       ],
41458       [
41459         "Algeria (‫الجزائر‬‎)",
41460         "dz",
41461         "213"
41462       ],
41463       [
41464         "American Samoa",
41465         "as",
41466         "1684"
41467       ],
41468       [
41469         "Andorra",
41470         "ad",
41471         "376"
41472       ],
41473       [
41474         "Angola",
41475         "ao",
41476         "244"
41477       ],
41478       [
41479         "Anguilla",
41480         "ai",
41481         "1264"
41482       ],
41483       [
41484         "Antigua and Barbuda",
41485         "ag",
41486         "1268"
41487       ],
41488       [
41489         "Argentina",
41490         "ar",
41491         "54"
41492       ],
41493       [
41494         "Armenia (Հայաստան)",
41495         "am",
41496         "374"
41497       ],
41498       [
41499         "Aruba",
41500         "aw",
41501         "297"
41502       ],
41503       [
41504         "Australia",
41505         "au",
41506         "61",
41507         0
41508       ],
41509       [
41510         "Austria (Österreich)",
41511         "at",
41512         "43"
41513       ],
41514       [
41515         "Azerbaijan (Azərbaycan)",
41516         "az",
41517         "994"
41518       ],
41519       [
41520         "Bahamas",
41521         "bs",
41522         "1242"
41523       ],
41524       [
41525         "Bahrain (‫البحرين‬‎)",
41526         "bh",
41527         "973"
41528       ],
41529       [
41530         "Bangladesh (বাংলাদেশ)",
41531         "bd",
41532         "880"
41533       ],
41534       [
41535         "Barbados",
41536         "bb",
41537         "1246"
41538       ],
41539       [
41540         "Belarus (Беларусь)",
41541         "by",
41542         "375"
41543       ],
41544       [
41545         "Belgium (België)",
41546         "be",
41547         "32"
41548       ],
41549       [
41550         "Belize",
41551         "bz",
41552         "501"
41553       ],
41554       [
41555         "Benin (Bénin)",
41556         "bj",
41557         "229"
41558       ],
41559       [
41560         "Bermuda",
41561         "bm",
41562         "1441"
41563       ],
41564       [
41565         "Bhutan (འབྲུག)",
41566         "bt",
41567         "975"
41568       ],
41569       [
41570         "Bolivia",
41571         "bo",
41572         "591"
41573       ],
41574       [
41575         "Bosnia and Herzegovina (Босна и Херцеговина)",
41576         "ba",
41577         "387"
41578       ],
41579       [
41580         "Botswana",
41581         "bw",
41582         "267"
41583       ],
41584       [
41585         "Brazil (Brasil)",
41586         "br",
41587         "55"
41588       ],
41589       [
41590         "British Indian Ocean Territory",
41591         "io",
41592         "246"
41593       ],
41594       [
41595         "British Virgin Islands",
41596         "vg",
41597         "1284"
41598       ],
41599       [
41600         "Brunei",
41601         "bn",
41602         "673"
41603       ],
41604       [
41605         "Bulgaria (България)",
41606         "bg",
41607         "359"
41608       ],
41609       [
41610         "Burkina Faso",
41611         "bf",
41612         "226"
41613       ],
41614       [
41615         "Burundi (Uburundi)",
41616         "bi",
41617         "257"
41618       ],
41619       [
41620         "Cambodia (កម្ពុជា)",
41621         "kh",
41622         "855"
41623       ],
41624       [
41625         "Cameroon (Cameroun)",
41626         "cm",
41627         "237"
41628       ],
41629       [
41630         "Canada",
41631         "ca",
41632         "1",
41633         1,
41634         ["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"]
41635       ],
41636       [
41637         "Cape Verde (Kabu Verdi)",
41638         "cv",
41639         "238"
41640       ],
41641       [
41642         "Caribbean Netherlands",
41643         "bq",
41644         "599",
41645         1
41646       ],
41647       [
41648         "Cayman Islands",
41649         "ky",
41650         "1345"
41651       ],
41652       [
41653         "Central African Republic (République centrafricaine)",
41654         "cf",
41655         "236"
41656       ],
41657       [
41658         "Chad (Tchad)",
41659         "td",
41660         "235"
41661       ],
41662       [
41663         "Chile",
41664         "cl",
41665         "56"
41666       ],
41667       [
41668         "China (中国)",
41669         "cn",
41670         "86"
41671       ],
41672       [
41673         "Christmas Island",
41674         "cx",
41675         "61",
41676         2
41677       ],
41678       [
41679         "Cocos (Keeling) Islands",
41680         "cc",
41681         "61",
41682         1
41683       ],
41684       [
41685         "Colombia",
41686         "co",
41687         "57"
41688       ],
41689       [
41690         "Comoros (‫جزر القمر‬‎)",
41691         "km",
41692         "269"
41693       ],
41694       [
41695         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41696         "cd",
41697         "243"
41698       ],
41699       [
41700         "Congo (Republic) (Congo-Brazzaville)",
41701         "cg",
41702         "242"
41703       ],
41704       [
41705         "Cook Islands",
41706         "ck",
41707         "682"
41708       ],
41709       [
41710         "Costa Rica",
41711         "cr",
41712         "506"
41713       ],
41714       [
41715         "Côte d’Ivoire",
41716         "ci",
41717         "225"
41718       ],
41719       [
41720         "Croatia (Hrvatska)",
41721         "hr",
41722         "385"
41723       ],
41724       [
41725         "Cuba",
41726         "cu",
41727         "53"
41728       ],
41729       [
41730         "Curaçao",
41731         "cw",
41732         "599",
41733         0
41734       ],
41735       [
41736         "Cyprus (Κύπρος)",
41737         "cy",
41738         "357"
41739       ],
41740       [
41741         "Czech Republic (Česká republika)",
41742         "cz",
41743         "420"
41744       ],
41745       [
41746         "Denmark (Danmark)",
41747         "dk",
41748         "45"
41749       ],
41750       [
41751         "Djibouti",
41752         "dj",
41753         "253"
41754       ],
41755       [
41756         "Dominica",
41757         "dm",
41758         "1767"
41759       ],
41760       [
41761         "Dominican Republic (República Dominicana)",
41762         "do",
41763         "1",
41764         2,
41765         ["809", "829", "849"]
41766       ],
41767       [
41768         "Ecuador",
41769         "ec",
41770         "593"
41771       ],
41772       [
41773         "Egypt (‫مصر‬‎)",
41774         "eg",
41775         "20"
41776       ],
41777       [
41778         "El Salvador",
41779         "sv",
41780         "503"
41781       ],
41782       [
41783         "Equatorial Guinea (Guinea Ecuatorial)",
41784         "gq",
41785         "240"
41786       ],
41787       [
41788         "Eritrea",
41789         "er",
41790         "291"
41791       ],
41792       [
41793         "Estonia (Eesti)",
41794         "ee",
41795         "372"
41796       ],
41797       [
41798         "Ethiopia",
41799         "et",
41800         "251"
41801       ],
41802       [
41803         "Falkland Islands (Islas Malvinas)",
41804         "fk",
41805         "500"
41806       ],
41807       [
41808         "Faroe Islands (Føroyar)",
41809         "fo",
41810         "298"
41811       ],
41812       [
41813         "Fiji",
41814         "fj",
41815         "679"
41816       ],
41817       [
41818         "Finland (Suomi)",
41819         "fi",
41820         "358",
41821         0
41822       ],
41823       [
41824         "France",
41825         "fr",
41826         "33"
41827       ],
41828       [
41829         "French Guiana (Guyane française)",
41830         "gf",
41831         "594"
41832       ],
41833       [
41834         "French Polynesia (Polynésie française)",
41835         "pf",
41836         "689"
41837       ],
41838       [
41839         "Gabon",
41840         "ga",
41841         "241"
41842       ],
41843       [
41844         "Gambia",
41845         "gm",
41846         "220"
41847       ],
41848       [
41849         "Georgia (საქართველო)",
41850         "ge",
41851         "995"
41852       ],
41853       [
41854         "Germany (Deutschland)",
41855         "de",
41856         "49"
41857       ],
41858       [
41859         "Ghana (Gaana)",
41860         "gh",
41861         "233"
41862       ],
41863       [
41864         "Gibraltar",
41865         "gi",
41866         "350"
41867       ],
41868       [
41869         "Greece (Ελλάδα)",
41870         "gr",
41871         "30"
41872       ],
41873       [
41874         "Greenland (Kalaallit Nunaat)",
41875         "gl",
41876         "299"
41877       ],
41878       [
41879         "Grenada",
41880         "gd",
41881         "1473"
41882       ],
41883       [
41884         "Guadeloupe",
41885         "gp",
41886         "590",
41887         0
41888       ],
41889       [
41890         "Guam",
41891         "gu",
41892         "1671"
41893       ],
41894       [
41895         "Guatemala",
41896         "gt",
41897         "502"
41898       ],
41899       [
41900         "Guernsey",
41901         "gg",
41902         "44",
41903         1
41904       ],
41905       [
41906         "Guinea (Guinée)",
41907         "gn",
41908         "224"
41909       ],
41910       [
41911         "Guinea-Bissau (Guiné Bissau)",
41912         "gw",
41913         "245"
41914       ],
41915       [
41916         "Guyana",
41917         "gy",
41918         "592"
41919       ],
41920       [
41921         "Haiti",
41922         "ht",
41923         "509"
41924       ],
41925       [
41926         "Honduras",
41927         "hn",
41928         "504"
41929       ],
41930       [
41931         "Hong Kong (香港)",
41932         "hk",
41933         "852"
41934       ],
41935       [
41936         "Hungary (Magyarország)",
41937         "hu",
41938         "36"
41939       ],
41940       [
41941         "Iceland (Ísland)",
41942         "is",
41943         "354"
41944       ],
41945       [
41946         "India (भारत)",
41947         "in",
41948         "91"
41949       ],
41950       [
41951         "Indonesia",
41952         "id",
41953         "62"
41954       ],
41955       [
41956         "Iran (‫ایران‬‎)",
41957         "ir",
41958         "98"
41959       ],
41960       [
41961         "Iraq (‫العراق‬‎)",
41962         "iq",
41963         "964"
41964       ],
41965       [
41966         "Ireland",
41967         "ie",
41968         "353"
41969       ],
41970       [
41971         "Isle of Man",
41972         "im",
41973         "44",
41974         2
41975       ],
41976       [
41977         "Israel (‫ישראל‬‎)",
41978         "il",
41979         "972"
41980       ],
41981       [
41982         "Italy (Italia)",
41983         "it",
41984         "39",
41985         0
41986       ],
41987       [
41988         "Jamaica",
41989         "jm",
41990         "1876"
41991       ],
41992       [
41993         "Japan (日本)",
41994         "jp",
41995         "81"
41996       ],
41997       [
41998         "Jersey",
41999         "je",
42000         "44",
42001         3
42002       ],
42003       [
42004         "Jordan (‫الأردن‬‎)",
42005         "jo",
42006         "962"
42007       ],
42008       [
42009         "Kazakhstan (Казахстан)",
42010         "kz",
42011         "7",
42012         1
42013       ],
42014       [
42015         "Kenya",
42016         "ke",
42017         "254"
42018       ],
42019       [
42020         "Kiribati",
42021         "ki",
42022         "686"
42023       ],
42024       [
42025         "Kosovo",
42026         "xk",
42027         "383"
42028       ],
42029       [
42030         "Kuwait (‫الكويت‬‎)",
42031         "kw",
42032         "965"
42033       ],
42034       [
42035         "Kyrgyzstan (Кыргызстан)",
42036         "kg",
42037         "996"
42038       ],
42039       [
42040         "Laos (ລາວ)",
42041         "la",
42042         "856"
42043       ],
42044       [
42045         "Latvia (Latvija)",
42046         "lv",
42047         "371"
42048       ],
42049       [
42050         "Lebanon (‫لبنان‬‎)",
42051         "lb",
42052         "961"
42053       ],
42054       [
42055         "Lesotho",
42056         "ls",
42057         "266"
42058       ],
42059       [
42060         "Liberia",
42061         "lr",
42062         "231"
42063       ],
42064       [
42065         "Libya (‫ليبيا‬‎)",
42066         "ly",
42067         "218"
42068       ],
42069       [
42070         "Liechtenstein",
42071         "li",
42072         "423"
42073       ],
42074       [
42075         "Lithuania (Lietuva)",
42076         "lt",
42077         "370"
42078       ],
42079       [
42080         "Luxembourg",
42081         "lu",
42082         "352"
42083       ],
42084       [
42085         "Macau (澳門)",
42086         "mo",
42087         "853"
42088       ],
42089       [
42090         "Macedonia (FYROM) (Македонија)",
42091         "mk",
42092         "389"
42093       ],
42094       [
42095         "Madagascar (Madagasikara)",
42096         "mg",
42097         "261"
42098       ],
42099       [
42100         "Malawi",
42101         "mw",
42102         "265"
42103       ],
42104       [
42105         "Malaysia",
42106         "my",
42107         "60"
42108       ],
42109       [
42110         "Maldives",
42111         "mv",
42112         "960"
42113       ],
42114       [
42115         "Mali",
42116         "ml",
42117         "223"
42118       ],
42119       [
42120         "Malta",
42121         "mt",
42122         "356"
42123       ],
42124       [
42125         "Marshall Islands",
42126         "mh",
42127         "692"
42128       ],
42129       [
42130         "Martinique",
42131         "mq",
42132         "596"
42133       ],
42134       [
42135         "Mauritania (‫موريتانيا‬‎)",
42136         "mr",
42137         "222"
42138       ],
42139       [
42140         "Mauritius (Moris)",
42141         "mu",
42142         "230"
42143       ],
42144       [
42145         "Mayotte",
42146         "yt",
42147         "262",
42148         1
42149       ],
42150       [
42151         "Mexico (México)",
42152         "mx",
42153         "52"
42154       ],
42155       [
42156         "Micronesia",
42157         "fm",
42158         "691"
42159       ],
42160       [
42161         "Moldova (Republica Moldova)",
42162         "md",
42163         "373"
42164       ],
42165       [
42166         "Monaco",
42167         "mc",
42168         "377"
42169       ],
42170       [
42171         "Mongolia (Монгол)",
42172         "mn",
42173         "976"
42174       ],
42175       [
42176         "Montenegro (Crna Gora)",
42177         "me",
42178         "382"
42179       ],
42180       [
42181         "Montserrat",
42182         "ms",
42183         "1664"
42184       ],
42185       [
42186         "Morocco (‫المغرب‬‎)",
42187         "ma",
42188         "212",
42189         0
42190       ],
42191       [
42192         "Mozambique (Moçambique)",
42193         "mz",
42194         "258"
42195       ],
42196       [
42197         "Myanmar (Burma) (မြန်မာ)",
42198         "mm",
42199         "95"
42200       ],
42201       [
42202         "Namibia (Namibië)",
42203         "na",
42204         "264"
42205       ],
42206       [
42207         "Nauru",
42208         "nr",
42209         "674"
42210       ],
42211       [
42212         "Nepal (नेपाल)",
42213         "np",
42214         "977"
42215       ],
42216       [
42217         "Netherlands (Nederland)",
42218         "nl",
42219         "31"
42220       ],
42221       [
42222         "New Caledonia (Nouvelle-Calédonie)",
42223         "nc",
42224         "687"
42225       ],
42226       [
42227         "New Zealand",
42228         "nz",
42229         "64"
42230       ],
42231       [
42232         "Nicaragua",
42233         "ni",
42234         "505"
42235       ],
42236       [
42237         "Niger (Nijar)",
42238         "ne",
42239         "227"
42240       ],
42241       [
42242         "Nigeria",
42243         "ng",
42244         "234"
42245       ],
42246       [
42247         "Niue",
42248         "nu",
42249         "683"
42250       ],
42251       [
42252         "Norfolk Island",
42253         "nf",
42254         "672"
42255       ],
42256       [
42257         "North Korea (조선 민주주의 인민 공화국)",
42258         "kp",
42259         "850"
42260       ],
42261       [
42262         "Northern Mariana Islands",
42263         "mp",
42264         "1670"
42265       ],
42266       [
42267         "Norway (Norge)",
42268         "no",
42269         "47",
42270         0
42271       ],
42272       [
42273         "Oman (‫عُمان‬‎)",
42274         "om",
42275         "968"
42276       ],
42277       [
42278         "Pakistan (‫پاکستان‬‎)",
42279         "pk",
42280         "92"
42281       ],
42282       [
42283         "Palau",
42284         "pw",
42285         "680"
42286       ],
42287       [
42288         "Palestine (‫فلسطين‬‎)",
42289         "ps",
42290         "970"
42291       ],
42292       [
42293         "Panama (Panamá)",
42294         "pa",
42295         "507"
42296       ],
42297       [
42298         "Papua New Guinea",
42299         "pg",
42300         "675"
42301       ],
42302       [
42303         "Paraguay",
42304         "py",
42305         "595"
42306       ],
42307       [
42308         "Peru (Perú)",
42309         "pe",
42310         "51"
42311       ],
42312       [
42313         "Philippines",
42314         "ph",
42315         "63"
42316       ],
42317       [
42318         "Poland (Polska)",
42319         "pl",
42320         "48"
42321       ],
42322       [
42323         "Portugal",
42324         "pt",
42325         "351"
42326       ],
42327       [
42328         "Puerto Rico",
42329         "pr",
42330         "1",
42331         3,
42332         ["787", "939"]
42333       ],
42334       [
42335         "Qatar (‫قطر‬‎)",
42336         "qa",
42337         "974"
42338       ],
42339       [
42340         "Réunion (La Réunion)",
42341         "re",
42342         "262",
42343         0
42344       ],
42345       [
42346         "Romania (România)",
42347         "ro",
42348         "40"
42349       ],
42350       [
42351         "Russia (Россия)",
42352         "ru",
42353         "7",
42354         0
42355       ],
42356       [
42357         "Rwanda",
42358         "rw",
42359         "250"
42360       ],
42361       [
42362         "Saint Barthélemy",
42363         "bl",
42364         "590",
42365         1
42366       ],
42367       [
42368         "Saint Helena",
42369         "sh",
42370         "290"
42371       ],
42372       [
42373         "Saint Kitts and Nevis",
42374         "kn",
42375         "1869"
42376       ],
42377       [
42378         "Saint Lucia",
42379         "lc",
42380         "1758"
42381       ],
42382       [
42383         "Saint Martin (Saint-Martin (partie française))",
42384         "mf",
42385         "590",
42386         2
42387       ],
42388       [
42389         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42390         "pm",
42391         "508"
42392       ],
42393       [
42394         "Saint Vincent and the Grenadines",
42395         "vc",
42396         "1784"
42397       ],
42398       [
42399         "Samoa",
42400         "ws",
42401         "685"
42402       ],
42403       [
42404         "San Marino",
42405         "sm",
42406         "378"
42407       ],
42408       [
42409         "São Tomé and Príncipe (São Tomé e Príncipe)",
42410         "st",
42411         "239"
42412       ],
42413       [
42414         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42415         "sa",
42416         "966"
42417       ],
42418       [
42419         "Senegal (Sénégal)",
42420         "sn",
42421         "221"
42422       ],
42423       [
42424         "Serbia (Србија)",
42425         "rs",
42426         "381"
42427       ],
42428       [
42429         "Seychelles",
42430         "sc",
42431         "248"
42432       ],
42433       [
42434         "Sierra Leone",
42435         "sl",
42436         "232"
42437       ],
42438       [
42439         "Singapore",
42440         "sg",
42441         "65"
42442       ],
42443       [
42444         "Sint Maarten",
42445         "sx",
42446         "1721"
42447       ],
42448       [
42449         "Slovakia (Slovensko)",
42450         "sk",
42451         "421"
42452       ],
42453       [
42454         "Slovenia (Slovenija)",
42455         "si",
42456         "386"
42457       ],
42458       [
42459         "Solomon Islands",
42460         "sb",
42461         "677"
42462       ],
42463       [
42464         "Somalia (Soomaaliya)",
42465         "so",
42466         "252"
42467       ],
42468       [
42469         "South Africa",
42470         "za",
42471         "27"
42472       ],
42473       [
42474         "South Korea (대한민국)",
42475         "kr",
42476         "82"
42477       ],
42478       [
42479         "South Sudan (‫جنوب السودان‬‎)",
42480         "ss",
42481         "211"
42482       ],
42483       [
42484         "Spain (España)",
42485         "es",
42486         "34"
42487       ],
42488       [
42489         "Sri Lanka (ශ්‍රී ලංකාව)",
42490         "lk",
42491         "94"
42492       ],
42493       [
42494         "Sudan (‫السودان‬‎)",
42495         "sd",
42496         "249"
42497       ],
42498       [
42499         "Suriname",
42500         "sr",
42501         "597"
42502       ],
42503       [
42504         "Svalbard and Jan Mayen",
42505         "sj",
42506         "47",
42507         1
42508       ],
42509       [
42510         "Swaziland",
42511         "sz",
42512         "268"
42513       ],
42514       [
42515         "Sweden (Sverige)",
42516         "se",
42517         "46"
42518       ],
42519       [
42520         "Switzerland (Schweiz)",
42521         "ch",
42522         "41"
42523       ],
42524       [
42525         "Syria (‫سوريا‬‎)",
42526         "sy",
42527         "963"
42528       ],
42529       [
42530         "Taiwan (台灣)",
42531         "tw",
42532         "886"
42533       ],
42534       [
42535         "Tajikistan",
42536         "tj",
42537         "992"
42538       ],
42539       [
42540         "Tanzania",
42541         "tz",
42542         "255"
42543       ],
42544       [
42545         "Thailand (ไทย)",
42546         "th",
42547         "66"
42548       ],
42549       [
42550         "Timor-Leste",
42551         "tl",
42552         "670"
42553       ],
42554       [
42555         "Togo",
42556         "tg",
42557         "228"
42558       ],
42559       [
42560         "Tokelau",
42561         "tk",
42562         "690"
42563       ],
42564       [
42565         "Tonga",
42566         "to",
42567         "676"
42568       ],
42569       [
42570         "Trinidad and Tobago",
42571         "tt",
42572         "1868"
42573       ],
42574       [
42575         "Tunisia (‫تونس‬‎)",
42576         "tn",
42577         "216"
42578       ],
42579       [
42580         "Turkey (Türkiye)",
42581         "tr",
42582         "90"
42583       ],
42584       [
42585         "Turkmenistan",
42586         "tm",
42587         "993"
42588       ],
42589       [
42590         "Turks and Caicos Islands",
42591         "tc",
42592         "1649"
42593       ],
42594       [
42595         "Tuvalu",
42596         "tv",
42597         "688"
42598       ],
42599       [
42600         "U.S. Virgin Islands",
42601         "vi",
42602         "1340"
42603       ],
42604       [
42605         "Uganda",
42606         "ug",
42607         "256"
42608       ],
42609       [
42610         "Ukraine (Україна)",
42611         "ua",
42612         "380"
42613       ],
42614       [
42615         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42616         "ae",
42617         "971"
42618       ],
42619       [
42620         "United Kingdom",
42621         "gb",
42622         "44",
42623         0
42624       ],
42625       [
42626         "United States",
42627         "us",
42628         "1",
42629         0
42630       ],
42631       [
42632         "Uruguay",
42633         "uy",
42634         "598"
42635       ],
42636       [
42637         "Uzbekistan (Oʻzbekiston)",
42638         "uz",
42639         "998"
42640       ],
42641       [
42642         "Vanuatu",
42643         "vu",
42644         "678"
42645       ],
42646       [
42647         "Vatican City (Città del Vaticano)",
42648         "va",
42649         "39",
42650         1
42651       ],
42652       [
42653         "Venezuela",
42654         "ve",
42655         "58"
42656       ],
42657       [
42658         "Vietnam (Việt Nam)",
42659         "vn",
42660         "84"
42661       ],
42662       [
42663         "Wallis and Futuna (Wallis-et-Futuna)",
42664         "wf",
42665         "681"
42666       ],
42667       [
42668         "Western Sahara (‫الصحراء الغربية‬‎)",
42669         "eh",
42670         "212",
42671         1
42672       ],
42673       [
42674         "Yemen (‫اليمن‬‎)",
42675         "ye",
42676         "967"
42677       ],
42678       [
42679         "Zambia",
42680         "zm",
42681         "260"
42682       ],
42683       [
42684         "Zimbabwe",
42685         "zw",
42686         "263"
42687       ],
42688       [
42689         "Åland Islands",
42690         "ax",
42691         "358",
42692         1
42693       ]
42694   ];
42695   
42696   return d;
42697 }/**
42698 *    This script refer to:
42699 *    Title: International Telephone Input
42700 *    Author: Jack O'Connor
42701 *    Code version:  v12.1.12
42702 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42703 **/
42704
42705 /**
42706  * @class Roo.bootstrap.PhoneInput
42707  * @extends Roo.bootstrap.TriggerField
42708  * An input with International dial-code selection
42709  
42710  * @cfg {String} defaultDialCode default '+852'
42711  * @cfg {Array} preferedCountries default []
42712   
42713  * @constructor
42714  * Create a new PhoneInput.
42715  * @param {Object} config Configuration options
42716  */
42717
42718 Roo.bootstrap.PhoneInput = function(config) {
42719     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42720 };
42721
42722 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42723         
42724         listWidth: undefined,
42725         
42726         selectedClass: 'active',
42727         
42728         invalidClass : "has-warning",
42729         
42730         validClass: 'has-success',
42731         
42732         allowed: '0123456789',
42733         
42734         max_length: 15,
42735         
42736         /**
42737          * @cfg {String} defaultDialCode The default dial code when initializing the input
42738          */
42739         defaultDialCode: '+852',
42740         
42741         /**
42742          * @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
42743          */
42744         preferedCountries: false,
42745         
42746         getAutoCreate : function()
42747         {
42748             var data = Roo.bootstrap.PhoneInputData();
42749             var align = this.labelAlign || this.parentLabelAlign();
42750             var id = Roo.id();
42751             
42752             this.allCountries = [];
42753             this.dialCodeMapping = [];
42754             
42755             for (var i = 0; i < data.length; i++) {
42756               var c = data[i];
42757               this.allCountries[i] = {
42758                 name: c[0],
42759                 iso2: c[1],
42760                 dialCode: c[2],
42761                 priority: c[3] || 0,
42762                 areaCodes: c[4] || null
42763               };
42764               this.dialCodeMapping[c[2]] = {
42765                   name: c[0],
42766                   iso2: c[1],
42767                   priority: c[3] || 0,
42768                   areaCodes: c[4] || null
42769               };
42770             }
42771             
42772             var cfg = {
42773                 cls: 'form-group',
42774                 cn: []
42775             };
42776             
42777             var input =  {
42778                 tag: 'input',
42779                 id : id,
42780                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42781                 maxlength: this.max_length,
42782                 cls : 'form-control tel-input',
42783                 autocomplete: 'new-password'
42784             };
42785             
42786             var hiddenInput = {
42787                 tag: 'input',
42788                 type: 'hidden',
42789                 cls: 'hidden-tel-input'
42790             };
42791             
42792             if (this.name) {
42793                 hiddenInput.name = this.name;
42794             }
42795             
42796             if (this.disabled) {
42797                 input.disabled = true;
42798             }
42799             
42800             var flag_container = {
42801                 tag: 'div',
42802                 cls: 'flag-box',
42803                 cn: [
42804                     {
42805                         tag: 'div',
42806                         cls: 'flag'
42807                     },
42808                     {
42809                         tag: 'div',
42810                         cls: 'caret'
42811                     }
42812                 ]
42813             };
42814             
42815             var box = {
42816                 tag: 'div',
42817                 cls: this.hasFeedback ? 'has-feedback' : '',
42818                 cn: [
42819                     hiddenInput,
42820                     input,
42821                     {
42822                         tag: 'input',
42823                         cls: 'dial-code-holder',
42824                         disabled: true
42825                     }
42826                 ]
42827             };
42828             
42829             var container = {
42830                 cls: 'roo-select2-container input-group',
42831                 cn: [
42832                     flag_container,
42833                     box
42834                 ]
42835             };
42836             
42837             if (this.fieldLabel.length) {
42838                 var indicator = {
42839                     tag: 'i',
42840                     tooltip: 'This field is required'
42841                 };
42842                 
42843                 var label = {
42844                     tag: 'label',
42845                     'for':  id,
42846                     cls: 'control-label',
42847                     cn: []
42848                 };
42849                 
42850                 var label_text = {
42851                     tag: 'span',
42852                     html: this.fieldLabel
42853                 };
42854                 
42855                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42856                 label.cn = [
42857                     indicator,
42858                     label_text
42859                 ];
42860                 
42861                 if(this.indicatorpos == 'right') {
42862                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42863                     label.cn = [
42864                         label_text,
42865                         indicator
42866                     ];
42867                 }
42868                 
42869                 if(align == 'left') {
42870                     container = {
42871                         tag: 'div',
42872                         cn: [
42873                             container
42874                         ]
42875                     };
42876                     
42877                     if(this.labelWidth > 12){
42878                         label.style = "width: " + this.labelWidth + 'px';
42879                     }
42880                     if(this.labelWidth < 13 && this.labelmd == 0){
42881                         this.labelmd = this.labelWidth;
42882                     }
42883                     if(this.labellg > 0){
42884                         label.cls += ' col-lg-' + this.labellg;
42885                         input.cls += ' col-lg-' + (12 - this.labellg);
42886                     }
42887                     if(this.labelmd > 0){
42888                         label.cls += ' col-md-' + this.labelmd;
42889                         container.cls += ' col-md-' + (12 - this.labelmd);
42890                     }
42891                     if(this.labelsm > 0){
42892                         label.cls += ' col-sm-' + this.labelsm;
42893                         container.cls += ' col-sm-' + (12 - this.labelsm);
42894                     }
42895                     if(this.labelxs > 0){
42896                         label.cls += ' col-xs-' + this.labelxs;
42897                         container.cls += ' col-xs-' + (12 - this.labelxs);
42898                     }
42899                 }
42900             }
42901             
42902             cfg.cn = [
42903                 label,
42904                 container
42905             ];
42906             
42907             var settings = this;
42908             
42909             ['xs','sm','md','lg'].map(function(size){
42910                 if (settings[size]) {
42911                     cfg.cls += ' col-' + size + '-' + settings[size];
42912                 }
42913             });
42914             
42915             this.store = new Roo.data.Store({
42916                 proxy : new Roo.data.MemoryProxy({}),
42917                 reader : new Roo.data.JsonReader({
42918                     fields : [
42919                         {
42920                             'name' : 'name',
42921                             'type' : 'string'
42922                         },
42923                         {
42924                             'name' : 'iso2',
42925                             'type' : 'string'
42926                         },
42927                         {
42928                             'name' : 'dialCode',
42929                             'type' : 'string'
42930                         },
42931                         {
42932                             'name' : 'priority',
42933                             'type' : 'string'
42934                         },
42935                         {
42936                             'name' : 'areaCodes',
42937                             'type' : 'string'
42938                         }
42939                     ]
42940                 })
42941             });
42942             
42943             if(!this.preferedCountries) {
42944                 this.preferedCountries = [
42945                     'hk',
42946                     'gb',
42947                     'us'
42948                 ];
42949             }
42950             
42951             var p = this.preferedCountries.reverse();
42952             
42953             if(p) {
42954                 for (var i = 0; i < p.length; i++) {
42955                     for (var j = 0; j < this.allCountries.length; j++) {
42956                         if(this.allCountries[j].iso2 == p[i]) {
42957                             var t = this.allCountries[j];
42958                             this.allCountries.splice(j,1);
42959                             this.allCountries.unshift(t);
42960                         }
42961                     } 
42962                 }
42963             }
42964             
42965             this.store.proxy.data = {
42966                 success: true,
42967                 data: this.allCountries
42968             };
42969             
42970             return cfg;
42971         },
42972         
42973         initEvents : function()
42974         {
42975             this.createList();
42976             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42977             
42978             this.indicator = this.indicatorEl();
42979             this.flag = this.flagEl();
42980             this.dialCodeHolder = this.dialCodeHolderEl();
42981             
42982             this.trigger = this.el.select('div.flag-box',true).first();
42983             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42984             
42985             var _this = this;
42986             
42987             (function(){
42988                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42989                 _this.list.setWidth(lw);
42990             }).defer(100);
42991             
42992             this.list.on('mouseover', this.onViewOver, this);
42993             this.list.on('mousemove', this.onViewMove, this);
42994             this.inputEl().on("keyup", this.onKeyUp, this);
42995             this.inputEl().on("keypress", this.onKeyPress, this);
42996             
42997             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42998
42999             this.view = new Roo.View(this.list, this.tpl, {
43000                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43001             });
43002             
43003             this.view.on('click', this.onViewClick, this);
43004             this.setValue(this.defaultDialCode);
43005         },
43006         
43007         onTriggerClick : function(e)
43008         {
43009             Roo.log('trigger click');
43010             if(this.disabled){
43011                 return;
43012             }
43013             
43014             if(this.isExpanded()){
43015                 this.collapse();
43016                 this.hasFocus = false;
43017             }else {
43018                 this.store.load({});
43019                 this.hasFocus = true;
43020                 this.expand();
43021             }
43022         },
43023         
43024         isExpanded : function()
43025         {
43026             return this.list.isVisible();
43027         },
43028         
43029         collapse : function()
43030         {
43031             if(!this.isExpanded()){
43032                 return;
43033             }
43034             this.list.hide();
43035             Roo.get(document).un('mousedown', this.collapseIf, this);
43036             Roo.get(document).un('mousewheel', this.collapseIf, this);
43037             this.fireEvent('collapse', this);
43038             this.validate();
43039         },
43040         
43041         expand : function()
43042         {
43043             Roo.log('expand');
43044
43045             if(this.isExpanded() || !this.hasFocus){
43046                 return;
43047             }
43048             
43049             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43050             this.list.setWidth(lw);
43051             
43052             this.list.show();
43053             this.restrictHeight();
43054             
43055             Roo.get(document).on('mousedown', this.collapseIf, this);
43056             Roo.get(document).on('mousewheel', this.collapseIf, this);
43057             
43058             this.fireEvent('expand', this);
43059         },
43060         
43061         restrictHeight : function()
43062         {
43063             this.list.alignTo(this.inputEl(), this.listAlign);
43064             this.list.alignTo(this.inputEl(), this.listAlign);
43065         },
43066         
43067         onViewOver : function(e, t)
43068         {
43069             if(this.inKeyMode){
43070                 return;
43071             }
43072             var item = this.view.findItemFromChild(t);
43073             
43074             if(item){
43075                 var index = this.view.indexOf(item);
43076                 this.select(index, false);
43077             }
43078         },
43079
43080         // private
43081         onViewClick : function(view, doFocus, el, e)
43082         {
43083             var index = this.view.getSelectedIndexes()[0];
43084             
43085             var r = this.store.getAt(index);
43086             
43087             if(r){
43088                 this.onSelect(r, index);
43089             }
43090             if(doFocus !== false && !this.blockFocus){
43091                 this.inputEl().focus();
43092             }
43093         },
43094         
43095         onViewMove : function(e, t)
43096         {
43097             this.inKeyMode = false;
43098         },
43099         
43100         select : function(index, scrollIntoView)
43101         {
43102             this.selectedIndex = index;
43103             this.view.select(index);
43104             if(scrollIntoView !== false){
43105                 var el = this.view.getNode(index);
43106                 if(el){
43107                     this.list.scrollChildIntoView(el, false);
43108                 }
43109             }
43110         },
43111         
43112         createList : function()
43113         {
43114             this.list = Roo.get(document.body).createChild({
43115                 tag: 'ul',
43116                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43117                 style: 'display:none'
43118             });
43119             
43120             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43121         },
43122         
43123         collapseIf : function(e)
43124         {
43125             var in_combo  = e.within(this.el);
43126             var in_list =  e.within(this.list);
43127             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43128             
43129             if (in_combo || in_list || is_list) {
43130                 return;
43131             }
43132             this.collapse();
43133         },
43134         
43135         onSelect : function(record, index)
43136         {
43137             if(this.fireEvent('beforeselect', this, record, index) !== false){
43138                 
43139                 this.setFlagClass(record.data.iso2);
43140                 this.setDialCode(record.data.dialCode);
43141                 this.hasFocus = false;
43142                 this.collapse();
43143                 this.fireEvent('select', this, record, index);
43144             }
43145         },
43146         
43147         flagEl : function()
43148         {
43149             var flag = this.el.select('div.flag',true).first();
43150             if(!flag){
43151                 return false;
43152             }
43153             return flag;
43154         },
43155         
43156         dialCodeHolderEl : function()
43157         {
43158             var d = this.el.select('input.dial-code-holder',true).first();
43159             if(!d){
43160                 return false;
43161             }
43162             return d;
43163         },
43164         
43165         setDialCode : function(v)
43166         {
43167             this.dialCodeHolder.dom.value = '+'+v;
43168         },
43169         
43170         setFlagClass : function(n)
43171         {
43172             this.flag.dom.className = 'flag '+n;
43173         },
43174         
43175         getValue : function()
43176         {
43177             var v = this.inputEl().getValue();
43178             if(this.dialCodeHolder) {
43179                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43180             }
43181             return v;
43182         },
43183         
43184         setValue : function(v)
43185         {
43186             var d = this.getDialCode(v);
43187             
43188             //invalid dial code
43189             if(v.length == 0 || !d || d.length == 0) {
43190                 if(this.rendered){
43191                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43192                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43193                 }
43194                 return;
43195             }
43196             
43197             //valid dial code
43198             this.setFlagClass(this.dialCodeMapping[d].iso2);
43199             this.setDialCode(d);
43200             this.inputEl().dom.value = v.replace('+'+d,'');
43201             this.hiddenEl().dom.value = this.getValue();
43202             
43203             this.validate();
43204         },
43205         
43206         getDialCode : function(v)
43207         {
43208             v = v ||  '';
43209             
43210             if (v.length == 0) {
43211                 return this.dialCodeHolder.dom.value;
43212             }
43213             
43214             var dialCode = "";
43215             if (v.charAt(0) != "+") {
43216                 return false;
43217             }
43218             var numericChars = "";
43219             for (var i = 1; i < v.length; i++) {
43220               var c = v.charAt(i);
43221               if (!isNaN(c)) {
43222                 numericChars += c;
43223                 if (this.dialCodeMapping[numericChars]) {
43224                   dialCode = v.substr(1, i);
43225                 }
43226                 if (numericChars.length == 4) {
43227                   break;
43228                 }
43229               }
43230             }
43231             return dialCode;
43232         },
43233         
43234         reset : function()
43235         {
43236             this.setValue(this.defaultDialCode);
43237             this.validate();
43238         },
43239         
43240         hiddenEl : function()
43241         {
43242             return this.el.select('input.hidden-tel-input',true).first();
43243         },
43244         
43245         // after setting val
43246         onKeyUp : function(e){
43247             this.setValue(this.getValue());
43248         },
43249         
43250         onKeyPress : function(e){
43251             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43252                 e.stopEvent();
43253             }
43254         }
43255         
43256 });
43257 /**
43258  * @class Roo.bootstrap.MoneyField
43259  * @extends Roo.bootstrap.ComboBox
43260  * Bootstrap MoneyField class
43261  * 
43262  * @constructor
43263  * Create a new MoneyField.
43264  * @param {Object} config Configuration options
43265  */
43266
43267 Roo.bootstrap.MoneyField = function(config) {
43268     
43269     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43270     
43271 };
43272
43273 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43274     
43275     /**
43276      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43277      */
43278     allowDecimals : true,
43279     /**
43280      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43281      */
43282     decimalSeparator : ".",
43283     /**
43284      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43285      */
43286     decimalPrecision : 0,
43287     /**
43288      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43289      */
43290     allowNegative : true,
43291     /**
43292      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43293      */
43294     allowZero: true,
43295     /**
43296      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43297      */
43298     minValue : Number.NEGATIVE_INFINITY,
43299     /**
43300      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43301      */
43302     maxValue : Number.MAX_VALUE,
43303     /**
43304      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43305      */
43306     minText : "The minimum value for this field is {0}",
43307     /**
43308      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43309      */
43310     maxText : "The maximum value for this field is {0}",
43311     /**
43312      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43313      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43314      */
43315     nanText : "{0} is not a valid number",
43316     /**
43317      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43318      */
43319     castInt : true,
43320     /**
43321      * @cfg {String} defaults currency of the MoneyField
43322      * value should be in lkey
43323      */
43324     defaultCurrency : false,
43325     /**
43326      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43327      */
43328     thousandsDelimiter : false,
43329     /**
43330      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43331      */
43332     max_length: false,
43333     
43334     inputlg : 9,
43335     inputmd : 9,
43336     inputsm : 9,
43337     inputxs : 6,
43338     
43339     store : false,
43340     
43341     getAutoCreate : function()
43342     {
43343         var align = this.labelAlign || this.parentLabelAlign();
43344         
43345         var id = Roo.id();
43346
43347         var cfg = {
43348             cls: 'form-group',
43349             cn: []
43350         };
43351
43352         var input =  {
43353             tag: 'input',
43354             id : id,
43355             cls : 'form-control roo-money-amount-input',
43356             autocomplete: 'new-password'
43357         };
43358         
43359         var hiddenInput = {
43360             tag: 'input',
43361             type: 'hidden',
43362             id: Roo.id(),
43363             cls: 'hidden-number-input'
43364         };
43365         
43366         if(this.max_length) {
43367             input.maxlength = this.max_length; 
43368         }
43369         
43370         if (this.name) {
43371             hiddenInput.name = this.name;
43372         }
43373
43374         if (this.disabled) {
43375             input.disabled = true;
43376         }
43377
43378         var clg = 12 - this.inputlg;
43379         var cmd = 12 - this.inputmd;
43380         var csm = 12 - this.inputsm;
43381         var cxs = 12 - this.inputxs;
43382         
43383         var container = {
43384             tag : 'div',
43385             cls : 'row roo-money-field',
43386             cn : [
43387                 {
43388                     tag : 'div',
43389                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43390                     cn : [
43391                         {
43392                             tag : 'div',
43393                             cls: 'roo-select2-container input-group',
43394                             cn: [
43395                                 {
43396                                     tag : 'input',
43397                                     cls : 'form-control roo-money-currency-input',
43398                                     autocomplete: 'new-password',
43399                                     readOnly : 1,
43400                                     name : this.currencyName
43401                                 },
43402                                 {
43403                                     tag :'span',
43404                                     cls : 'input-group-addon',
43405                                     cn : [
43406                                         {
43407                                             tag: 'span',
43408                                             cls: 'caret'
43409                                         }
43410                                     ]
43411                                 }
43412                             ]
43413                         }
43414                     ]
43415                 },
43416                 {
43417                     tag : 'div',
43418                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43419                     cn : [
43420                         {
43421                             tag: 'div',
43422                             cls: this.hasFeedback ? 'has-feedback' : '',
43423                             cn: [
43424                                 input
43425                             ]
43426                         }
43427                     ]
43428                 }
43429             ]
43430             
43431         };
43432         
43433         if (this.fieldLabel.length) {
43434             var indicator = {
43435                 tag: 'i',
43436                 tooltip: 'This field is required'
43437             };
43438
43439             var label = {
43440                 tag: 'label',
43441                 'for':  id,
43442                 cls: 'control-label',
43443                 cn: []
43444             };
43445
43446             var label_text = {
43447                 tag: 'span',
43448                 html: this.fieldLabel
43449             };
43450
43451             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43452             label.cn = [
43453                 indicator,
43454                 label_text
43455             ];
43456
43457             if(this.indicatorpos == 'right') {
43458                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43459                 label.cn = [
43460                     label_text,
43461                     indicator
43462                 ];
43463             }
43464
43465             if(align == 'left') {
43466                 container = {
43467                     tag: 'div',
43468                     cn: [
43469                         container
43470                     ]
43471                 };
43472
43473                 if(this.labelWidth > 12){
43474                     label.style = "width: " + this.labelWidth + 'px';
43475                 }
43476                 if(this.labelWidth < 13 && this.labelmd == 0){
43477                     this.labelmd = this.labelWidth;
43478                 }
43479                 if(this.labellg > 0){
43480                     label.cls += ' col-lg-' + this.labellg;
43481                     input.cls += ' col-lg-' + (12 - this.labellg);
43482                 }
43483                 if(this.labelmd > 0){
43484                     label.cls += ' col-md-' + this.labelmd;
43485                     container.cls += ' col-md-' + (12 - this.labelmd);
43486                 }
43487                 if(this.labelsm > 0){
43488                     label.cls += ' col-sm-' + this.labelsm;
43489                     container.cls += ' col-sm-' + (12 - this.labelsm);
43490                 }
43491                 if(this.labelxs > 0){
43492                     label.cls += ' col-xs-' + this.labelxs;
43493                     container.cls += ' col-xs-' + (12 - this.labelxs);
43494                 }
43495             }
43496         }
43497
43498         cfg.cn = [
43499             label,
43500             container,
43501             hiddenInput
43502         ];
43503         
43504         var settings = this;
43505
43506         ['xs','sm','md','lg'].map(function(size){
43507             if (settings[size]) {
43508                 cfg.cls += ' col-' + size + '-' + settings[size];
43509             }
43510         });
43511         
43512         return cfg;
43513     },
43514     
43515     initEvents : function()
43516     {
43517         this.indicator = this.indicatorEl();
43518         
43519         this.initCurrencyEvent();
43520         
43521         this.initNumberEvent();
43522     },
43523     
43524     initCurrencyEvent : function()
43525     {
43526         if (!this.store) {
43527             throw "can not find store for combo";
43528         }
43529         
43530         this.store = Roo.factory(this.store, Roo.data);
43531         this.store.parent = this;
43532         
43533         this.createList();
43534         
43535         this.triggerEl = this.el.select('.input-group-addon', true).first();
43536         
43537         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43538         
43539         var _this = this;
43540         
43541         (function(){
43542             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43543             _this.list.setWidth(lw);
43544         }).defer(100);
43545         
43546         this.list.on('mouseover', this.onViewOver, this);
43547         this.list.on('mousemove', this.onViewMove, this);
43548         this.list.on('scroll', this.onViewScroll, this);
43549         
43550         if(!this.tpl){
43551             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43552         }
43553         
43554         this.view = new Roo.View(this.list, this.tpl, {
43555             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43556         });
43557         
43558         this.view.on('click', this.onViewClick, this);
43559         
43560         this.store.on('beforeload', this.onBeforeLoad, this);
43561         this.store.on('load', this.onLoad, this);
43562         this.store.on('loadexception', this.onLoadException, this);
43563         
43564         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43565             "up" : function(e){
43566                 this.inKeyMode = true;
43567                 this.selectPrev();
43568             },
43569
43570             "down" : function(e){
43571                 if(!this.isExpanded()){
43572                     this.onTriggerClick();
43573                 }else{
43574                     this.inKeyMode = true;
43575                     this.selectNext();
43576                 }
43577             },
43578
43579             "enter" : function(e){
43580                 this.collapse();
43581                 
43582                 if(this.fireEvent("specialkey", this, e)){
43583                     this.onViewClick(false);
43584                 }
43585                 
43586                 return true;
43587             },
43588
43589             "esc" : function(e){
43590                 this.collapse();
43591             },
43592
43593             "tab" : function(e){
43594                 this.collapse();
43595                 
43596                 if(this.fireEvent("specialkey", this, e)){
43597                     this.onViewClick(false);
43598                 }
43599                 
43600                 return true;
43601             },
43602
43603             scope : this,
43604
43605             doRelay : function(foo, bar, hname){
43606                 if(hname == 'down' || this.scope.isExpanded()){
43607                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43608                 }
43609                 return true;
43610             },
43611
43612             forceKeyDown: true
43613         });
43614         
43615         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43616         
43617     },
43618     
43619     initNumberEvent : function(e)
43620     {
43621         this.inputEl().on("keydown" , this.fireKey,  this);
43622         this.inputEl().on("focus", this.onFocus,  this);
43623         this.inputEl().on("blur", this.onBlur,  this);
43624         
43625         this.inputEl().relayEvent('keyup', this);
43626         
43627         if(this.indicator){
43628             this.indicator.addClass('invisible');
43629         }
43630  
43631         this.originalValue = this.getValue();
43632         
43633         if(this.validationEvent == 'keyup'){
43634             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43635             this.inputEl().on('keyup', this.filterValidation, this);
43636         }
43637         else if(this.validationEvent !== false){
43638             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43639         }
43640         
43641         if(this.selectOnFocus){
43642             this.on("focus", this.preFocus, this);
43643             
43644         }
43645         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43646             this.inputEl().on("keypress", this.filterKeys, this);
43647         } else {
43648             this.inputEl().relayEvent('keypress', this);
43649         }
43650         
43651         var allowed = "0123456789";
43652         
43653         if(this.allowDecimals){
43654             allowed += this.decimalSeparator;
43655         }
43656         
43657         if(this.allowNegative){
43658             allowed += "-";
43659         }
43660         
43661         if(this.thousandsDelimiter) {
43662             allowed += ",";
43663         }
43664         
43665         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43666         
43667         var keyPress = function(e){
43668             
43669             var k = e.getKey();
43670             
43671             var c = e.getCharCode();
43672             
43673             if(
43674                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43675                     allowed.indexOf(String.fromCharCode(c)) === -1
43676             ){
43677                 e.stopEvent();
43678                 return;
43679             }
43680             
43681             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43682                 return;
43683             }
43684             
43685             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43686                 e.stopEvent();
43687             }
43688         };
43689         
43690         this.inputEl().on("keypress", keyPress, this);
43691         
43692     },
43693     
43694     onTriggerClick : function(e)
43695     {   
43696         if(this.disabled){
43697             return;
43698         }
43699         
43700         this.page = 0;
43701         this.loadNext = false;
43702         
43703         if(this.isExpanded()){
43704             this.collapse();
43705             return;
43706         }
43707         
43708         this.hasFocus = true;
43709         
43710         if(this.triggerAction == 'all') {
43711             this.doQuery(this.allQuery, true);
43712             return;
43713         }
43714         
43715         this.doQuery(this.getRawValue());
43716     },
43717     
43718     getCurrency : function()
43719     {   
43720         var v = this.currencyEl().getValue();
43721         
43722         return v;
43723     },
43724     
43725     restrictHeight : function()
43726     {
43727         this.list.alignTo(this.currencyEl(), this.listAlign);
43728         this.list.alignTo(this.currencyEl(), this.listAlign);
43729     },
43730     
43731     onViewClick : function(view, doFocus, el, e)
43732     {
43733         var index = this.view.getSelectedIndexes()[0];
43734         
43735         var r = this.store.getAt(index);
43736         
43737         if(r){
43738             this.onSelect(r, index);
43739         }
43740     },
43741     
43742     onSelect : function(record, index){
43743         
43744         if(this.fireEvent('beforeselect', this, record, index) !== false){
43745         
43746             this.setFromCurrencyData(index > -1 ? record.data : false);
43747             
43748             this.collapse();
43749             
43750             this.fireEvent('select', this, record, index);
43751         }
43752     },
43753     
43754     setFromCurrencyData : function(o)
43755     {
43756         var currency = '';
43757         
43758         this.lastCurrency = o;
43759         
43760         if (this.currencyField) {
43761             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43762         } else {
43763             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43764         }
43765         
43766         this.lastSelectionText = currency;
43767         
43768         //setting default currency
43769         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43770             this.setCurrency(this.defaultCurrency);
43771             return;
43772         }
43773         
43774         this.setCurrency(currency);
43775     },
43776     
43777     setFromData : function(o)
43778     {
43779         var c = {};
43780         
43781         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43782         
43783         this.setFromCurrencyData(c);
43784         
43785         var value = '';
43786         
43787         if (this.name) {
43788             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43789         } else {
43790             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43791         }
43792         
43793         this.setValue(value);
43794         
43795     },
43796     
43797     setCurrency : function(v)
43798     {   
43799         this.currencyValue = v;
43800         
43801         if(this.rendered){
43802             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43803             this.validate();
43804         }
43805     },
43806     
43807     setValue : function(v)
43808     {
43809         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43810         
43811         this.value = v;
43812         
43813         if(this.rendered){
43814             
43815             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43816             
43817             this.inputEl().dom.value = (v == '') ? '' :
43818                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43819             
43820             if(!this.allowZero && v === '0') {
43821                 this.hiddenEl().dom.value = '';
43822                 this.inputEl().dom.value = '';
43823             }
43824             
43825             this.validate();
43826         }
43827     },
43828     
43829     getRawValue : function()
43830     {
43831         var v = this.inputEl().getValue();
43832         
43833         return v;
43834     },
43835     
43836     getValue : function()
43837     {
43838         return this.fixPrecision(this.parseValue(this.getRawValue()));
43839     },
43840     
43841     parseValue : function(value)
43842     {
43843         if(this.thousandsDelimiter) {
43844             value += "";
43845             r = new RegExp(",", "g");
43846             value = value.replace(r, "");
43847         }
43848         
43849         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43850         return isNaN(value) ? '' : value;
43851         
43852     },
43853     
43854     fixPrecision : function(value)
43855     {
43856         if(this.thousandsDelimiter) {
43857             value += "";
43858             r = new RegExp(",", "g");
43859             value = value.replace(r, "");
43860         }
43861         
43862         var nan = isNaN(value);
43863         
43864         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43865             return nan ? '' : value;
43866         }
43867         return parseFloat(value).toFixed(this.decimalPrecision);
43868     },
43869     
43870     decimalPrecisionFcn : function(v)
43871     {
43872         return Math.floor(v);
43873     },
43874     
43875     validateValue : function(value)
43876     {
43877         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43878             return false;
43879         }
43880         
43881         var num = this.parseValue(value);
43882         
43883         if(isNaN(num)){
43884             this.markInvalid(String.format(this.nanText, value));
43885             return false;
43886         }
43887         
43888         if(num < this.minValue){
43889             this.markInvalid(String.format(this.minText, this.minValue));
43890             return false;
43891         }
43892         
43893         if(num > this.maxValue){
43894             this.markInvalid(String.format(this.maxText, this.maxValue));
43895             return false;
43896         }
43897         
43898         return true;
43899     },
43900     
43901     validate : function()
43902     {
43903         if(this.disabled || this.allowBlank){
43904             this.markValid();
43905             return true;
43906         }
43907         
43908         var currency = this.getCurrency();
43909         
43910         if(this.validateValue(this.getRawValue()) && currency.length){
43911             this.markValid();
43912             return true;
43913         }
43914         
43915         this.markInvalid();
43916         return false;
43917     },
43918     
43919     getName: function()
43920     {
43921         return this.name;
43922     },
43923     
43924     beforeBlur : function()
43925     {
43926         if(!this.castInt){
43927             return;
43928         }
43929         
43930         var v = this.parseValue(this.getRawValue());
43931         
43932         if(v || v == 0){
43933             this.setValue(v);
43934         }
43935     },
43936     
43937     onBlur : function()
43938     {
43939         this.beforeBlur();
43940         
43941         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43942             //this.el.removeClass(this.focusClass);
43943         }
43944         
43945         this.hasFocus = false;
43946         
43947         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43948             this.validate();
43949         }
43950         
43951         var v = this.getValue();
43952         
43953         if(String(v) !== String(this.startValue)){
43954             this.fireEvent('change', this, v, this.startValue);
43955         }
43956         
43957         this.fireEvent("blur", this);
43958     },
43959     
43960     inputEl : function()
43961     {
43962         return this.el.select('.roo-money-amount-input', true).first();
43963     },
43964     
43965     currencyEl : function()
43966     {
43967         return this.el.select('.roo-money-currency-input', true).first();
43968     },
43969     
43970     hiddenEl : function()
43971     {
43972         return this.el.select('input.hidden-number-input',true).first();
43973     }
43974     
43975 });/**
43976  * @class Roo.bootstrap.BezierSignature
43977  * @extends Roo.bootstrap.Component
43978  * Bootstrap BezierSignature class
43979  * This script refer to:
43980  *    Title: Signature Pad
43981  *    Author: szimek
43982  *    Availability: https://github.com/szimek/signature_pad
43983  *
43984  * @constructor
43985  * Create a new BezierSignature
43986  * @param {Object} config The config object
43987  */
43988
43989 Roo.bootstrap.BezierSignature = function(config){
43990     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43991     this.addEvents({
43992         "resize" : true
43993     });
43994 };
43995
43996 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43997 {
43998      
43999     curve_data: [],
44000     
44001     is_empty: true,
44002     
44003     mouse_btn_down: true,
44004     
44005     /**
44006      * @cfg {int} canvas height
44007      */
44008     canvas_height: '200px',
44009     
44010     /**
44011      * @cfg {float|function} Radius of a single dot.
44012      */ 
44013     dot_size: false,
44014     
44015     /**
44016      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44017      */
44018     min_width: 0.5,
44019     
44020     /**
44021      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44022      */
44023     max_width: 2.5,
44024     
44025     /**
44026      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44027      */
44028     throttle: 16,
44029     
44030     /**
44031      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44032      */
44033     min_distance: 5,
44034     
44035     /**
44036      * @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.
44037      */
44038     bg_color: 'rgba(0, 0, 0, 0)',
44039     
44040     /**
44041      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44042      */
44043     dot_color: 'black',
44044     
44045     /**
44046      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44047      */ 
44048     velocity_filter_weight: 0.7,
44049     
44050     /**
44051      * @cfg {function} Callback when stroke begin. 
44052      */
44053     onBegin: false,
44054     
44055     /**
44056      * @cfg {function} Callback when stroke end.
44057      */
44058     onEnd: false,
44059     
44060     getAutoCreate : function()
44061     {
44062         var cls = 'roo-signature column';
44063         
44064         if(this.cls){
44065             cls += ' ' + this.cls;
44066         }
44067         
44068         var col_sizes = [
44069             'lg',
44070             'md',
44071             'sm',
44072             'xs'
44073         ];
44074         
44075         for(var i = 0; i < col_sizes.length; i++) {
44076             if(this[col_sizes[i]]) {
44077                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44078             }
44079         }
44080         
44081         var cfg = {
44082             tag: 'div',
44083             cls: cls,
44084             cn: [
44085                 {
44086                     tag: 'div',
44087                     cls: 'roo-signature-body',
44088                     cn: [
44089                         {
44090                             tag: 'canvas',
44091                             cls: 'roo-signature-body-canvas',
44092                             height: this.canvas_height,
44093                             width: this.canvas_width
44094                         }
44095                     ]
44096                 },
44097                 {
44098                     tag: 'input',
44099                     type: 'file',
44100                     style: 'display: none'
44101                 }
44102             ]
44103         };
44104         
44105         return cfg;
44106     },
44107     
44108     initEvents: function() 
44109     {
44110         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44111         
44112         var canvas = this.canvasEl();
44113         
44114         // mouse && touch event swapping...
44115         canvas.dom.style.touchAction = 'none';
44116         canvas.dom.style.msTouchAction = 'none';
44117         
44118         this.mouse_btn_down = false;
44119         canvas.on('mousedown', this._handleMouseDown, this);
44120         canvas.on('mousemove', this._handleMouseMove, this);
44121         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44122         
44123         if (window.PointerEvent) {
44124             canvas.on('pointerdown', this._handleMouseDown, this);
44125             canvas.on('pointermove', this._handleMouseMove, this);
44126             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44127         }
44128         
44129         if ('ontouchstart' in window) {
44130             canvas.on('touchstart', this._handleTouchStart, this);
44131             canvas.on('touchmove', this._handleTouchMove, this);
44132             canvas.on('touchend', this._handleTouchEnd, this);
44133         }
44134         
44135         Roo.EventManager.onWindowResize(this.resize, this, true);
44136         
44137         // file input event
44138         this.fileEl().on('change', this.uploadImage, this);
44139         
44140         this.clear();
44141         
44142         this.resize();
44143     },
44144     
44145     resize: function(){
44146         
44147         var canvas = this.canvasEl().dom;
44148         var ctx = this.canvasElCtx();
44149         var img_data = false;
44150         
44151         if(canvas.width > 0) {
44152             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44153         }
44154         // setting canvas width will clean img data
44155         canvas.width = 0;
44156         
44157         var style = window.getComputedStyle ? 
44158             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44159             
44160         var padding_left = parseInt(style.paddingLeft) || 0;
44161         var padding_right = parseInt(style.paddingRight) || 0;
44162         
44163         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44164         
44165         if(img_data) {
44166             ctx.putImageData(img_data, 0, 0);
44167         }
44168     },
44169     
44170     _handleMouseDown: function(e)
44171     {
44172         if (e.browserEvent.which === 1) {
44173             this.mouse_btn_down = true;
44174             this.strokeBegin(e);
44175         }
44176     },
44177     
44178     _handleMouseMove: function (e)
44179     {
44180         if (this.mouse_btn_down) {
44181             this.strokeMoveUpdate(e);
44182         }
44183     },
44184     
44185     _handleMouseUp: function (e)
44186     {
44187         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44188             this.mouse_btn_down = false;
44189             this.strokeEnd(e);
44190         }
44191     },
44192     
44193     _handleTouchStart: function (e) {
44194         
44195         e.preventDefault();
44196         if (e.browserEvent.targetTouches.length === 1) {
44197             // var touch = e.browserEvent.changedTouches[0];
44198             // this.strokeBegin(touch);
44199             
44200              this.strokeBegin(e); // assume e catching the correct xy...
44201         }
44202     },
44203     
44204     _handleTouchMove: function (e) {
44205         e.preventDefault();
44206         // var touch = event.targetTouches[0];
44207         // _this._strokeMoveUpdate(touch);
44208         this.strokeMoveUpdate(e);
44209     },
44210     
44211     _handleTouchEnd: function (e) {
44212         var wasCanvasTouched = e.target === this.canvasEl().dom;
44213         if (wasCanvasTouched) {
44214             e.preventDefault();
44215             // var touch = event.changedTouches[0];
44216             // _this._strokeEnd(touch);
44217             this.strokeEnd(e);
44218         }
44219     },
44220     
44221     reset: function () {
44222         this._lastPoints = [];
44223         this._lastVelocity = 0;
44224         this._lastWidth = (this.min_width + this.max_width) / 2;
44225         this.canvasElCtx().fillStyle = this.dot_color;
44226     },
44227     
44228     strokeMoveUpdate: function(e)
44229     {
44230         this.strokeUpdate(e);
44231         
44232         if (this.throttle) {
44233             this.throttleStroke(this.strokeUpdate, this.throttle);
44234         }
44235         else {
44236             this.strokeUpdate(e);
44237         }
44238     },
44239     
44240     strokeBegin: function(e)
44241     {
44242         var newPointGroup = {
44243             color: this.dot_color,
44244             points: []
44245         };
44246         
44247         if (typeof this.onBegin === 'function') {
44248             this.onBegin(e);
44249         }
44250         
44251         this.curve_data.push(newPointGroup);
44252         this.reset();
44253         this.strokeUpdate(e);
44254     },
44255     
44256     strokeUpdate: function(e)
44257     {
44258         var rect = this.canvasEl().dom.getBoundingClientRect();
44259         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44260         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44261         var lastPoints = lastPointGroup.points;
44262         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44263         var isLastPointTooClose = lastPoint
44264             ? point.distanceTo(lastPoint) <= this.min_distance
44265             : false;
44266         var color = lastPointGroup.color;
44267         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44268             var curve = this.addPoint(point);
44269             if (!lastPoint) {
44270                 this.drawDot({color: color, point: point});
44271             }
44272             else if (curve) {
44273                 this.drawCurve({color: color, curve: curve});
44274             }
44275             lastPoints.push({
44276                 time: point.time,
44277                 x: point.x,
44278                 y: point.y
44279             });
44280         }
44281     },
44282     
44283     strokeEnd: function(e)
44284     {
44285         this.strokeUpdate(e);
44286         if (typeof this.onEnd === 'function') {
44287             this.onEnd(e);
44288         }
44289     },
44290     
44291     addPoint:  function (point) {
44292         var _lastPoints = this._lastPoints;
44293         _lastPoints.push(point);
44294         if (_lastPoints.length > 2) {
44295             if (_lastPoints.length === 3) {
44296                 _lastPoints.unshift(_lastPoints[0]);
44297             }
44298             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44299             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44300             _lastPoints.shift();
44301             return curve;
44302         }
44303         return null;
44304     },
44305     
44306     calculateCurveWidths: function (startPoint, endPoint) {
44307         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44308             (1 - this.velocity_filter_weight) * this._lastVelocity;
44309
44310         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44311         var widths = {
44312             end: newWidth,
44313             start: this._lastWidth
44314         };
44315         
44316         this._lastVelocity = velocity;
44317         this._lastWidth = newWidth;
44318         return widths;
44319     },
44320     
44321     drawDot: function (_a) {
44322         var color = _a.color, point = _a.point;
44323         var ctx = this.canvasElCtx();
44324         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44325         ctx.beginPath();
44326         this.drawCurveSegment(point.x, point.y, width);
44327         ctx.closePath();
44328         ctx.fillStyle = color;
44329         ctx.fill();
44330     },
44331     
44332     drawCurve: function (_a) {
44333         var color = _a.color, curve = _a.curve;
44334         var ctx = this.canvasElCtx();
44335         var widthDelta = curve.endWidth - curve.startWidth;
44336         var drawSteps = Math.floor(curve.length()) * 2;
44337         ctx.beginPath();
44338         ctx.fillStyle = color;
44339         for (var i = 0; i < drawSteps; i += 1) {
44340         var t = i / drawSteps;
44341         var tt = t * t;
44342         var ttt = tt * t;
44343         var u = 1 - t;
44344         var uu = u * u;
44345         var uuu = uu * u;
44346         var x = uuu * curve.startPoint.x;
44347         x += 3 * uu * t * curve.control1.x;
44348         x += 3 * u * tt * curve.control2.x;
44349         x += ttt * curve.endPoint.x;
44350         var y = uuu * curve.startPoint.y;
44351         y += 3 * uu * t * curve.control1.y;
44352         y += 3 * u * tt * curve.control2.y;
44353         y += ttt * curve.endPoint.y;
44354         var width = curve.startWidth + ttt * widthDelta;
44355         this.drawCurveSegment(x, y, width);
44356         }
44357         ctx.closePath();
44358         ctx.fill();
44359     },
44360     
44361     drawCurveSegment: function (x, y, width) {
44362         var ctx = this.canvasElCtx();
44363         ctx.moveTo(x, y);
44364         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44365         this.is_empty = false;
44366     },
44367     
44368     clear: function()
44369     {
44370         var ctx = this.canvasElCtx();
44371         var canvas = this.canvasEl().dom;
44372         ctx.fillStyle = this.bg_color;
44373         ctx.clearRect(0, 0, canvas.width, canvas.height);
44374         ctx.fillRect(0, 0, canvas.width, canvas.height);
44375         this.curve_data = [];
44376         this.reset();
44377         this.is_empty = true;
44378     },
44379     
44380     fileEl: function()
44381     {
44382         return  this.el.select('input',true).first();
44383     },
44384     
44385     canvasEl: function()
44386     {
44387         return this.el.select('canvas',true).first();
44388     },
44389     
44390     canvasElCtx: function()
44391     {
44392         return this.el.select('canvas',true).first().dom.getContext('2d');
44393     },
44394     
44395     getImage: function(type)
44396     {
44397         if(this.is_empty) {
44398             return false;
44399         }
44400         
44401         // encryption ?
44402         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44403     },
44404     
44405     drawFromImage: function(img_src)
44406     {
44407         var img = new Image();
44408         
44409         img.onload = function(){
44410             this.canvasElCtx().drawImage(img, 0, 0);
44411         }.bind(this);
44412         
44413         img.src = img_src;
44414         
44415         this.is_empty = false;
44416     },
44417     
44418     selectImage: function()
44419     {
44420         this.fileEl().dom.click();
44421     },
44422     
44423     uploadImage: function(e)
44424     {
44425         var reader = new FileReader();
44426         
44427         reader.onload = function(e){
44428             var img = new Image();
44429             img.onload = function(){
44430                 this.reset();
44431                 this.canvasElCtx().drawImage(img, 0, 0);
44432             }.bind(this);
44433             img.src = e.target.result;
44434         }.bind(this);
44435         
44436         reader.readAsDataURL(e.target.files[0]);
44437     },
44438     
44439     // Bezier Point Constructor
44440     Point: (function () {
44441         function Point(x, y, time) {
44442             this.x = x;
44443             this.y = y;
44444             this.time = time || Date.now();
44445         }
44446         Point.prototype.distanceTo = function (start) {
44447             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44448         };
44449         Point.prototype.equals = function (other) {
44450             return this.x === other.x && this.y === other.y && this.time === other.time;
44451         };
44452         Point.prototype.velocityFrom = function (start) {
44453             return this.time !== start.time
44454             ? this.distanceTo(start) / (this.time - start.time)
44455             : 0;
44456         };
44457         return Point;
44458     }()),
44459     
44460     
44461     // Bezier Constructor
44462     Bezier: (function () {
44463         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44464             this.startPoint = startPoint;
44465             this.control2 = control2;
44466             this.control1 = control1;
44467             this.endPoint = endPoint;
44468             this.startWidth = startWidth;
44469             this.endWidth = endWidth;
44470         }
44471         Bezier.fromPoints = function (points, widths, scope) {
44472             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44473             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44474             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44475         };
44476         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44477             var dx1 = s1.x - s2.x;
44478             var dy1 = s1.y - s2.y;
44479             var dx2 = s2.x - s3.x;
44480             var dy2 = s2.y - s3.y;
44481             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44482             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44483             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44484             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44485             var dxm = m1.x - m2.x;
44486             var dym = m1.y - m2.y;
44487             var k = l2 / (l1 + l2);
44488             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44489             var tx = s2.x - cm.x;
44490             var ty = s2.y - cm.y;
44491             return {
44492                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44493                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44494             };
44495         };
44496         Bezier.prototype.length = function () {
44497             var steps = 10;
44498             var length = 0;
44499             var px;
44500             var py;
44501             for (var i = 0; i <= steps; i += 1) {
44502                 var t = i / steps;
44503                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44504                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44505                 if (i > 0) {
44506                     var xdiff = cx - px;
44507                     var ydiff = cy - py;
44508                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44509                 }
44510                 px = cx;
44511                 py = cy;
44512             }
44513             return length;
44514         };
44515         Bezier.prototype.point = function (t, start, c1, c2, end) {
44516             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44517             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44518             + (3.0 * c2 * (1.0 - t) * t * t)
44519             + (end * t * t * t);
44520         };
44521         return Bezier;
44522     }()),
44523     
44524     throttleStroke: function(fn, wait) {
44525       if (wait === void 0) { wait = 250; }
44526       var previous = 0;
44527       var timeout = null;
44528       var result;
44529       var storedContext;
44530       var storedArgs;
44531       var later = function () {
44532           previous = Date.now();
44533           timeout = null;
44534           result = fn.apply(storedContext, storedArgs);
44535           if (!timeout) {
44536               storedContext = null;
44537               storedArgs = [];
44538           }
44539       };
44540       return function wrapper() {
44541           var args = [];
44542           for (var _i = 0; _i < arguments.length; _i++) {
44543               args[_i] = arguments[_i];
44544           }
44545           var now = Date.now();
44546           var remaining = wait - (now - previous);
44547           storedContext = this;
44548           storedArgs = args;
44549           if (remaining <= 0 || remaining > wait) {
44550               if (timeout) {
44551                   clearTimeout(timeout);
44552                   timeout = null;
44553               }
44554               previous = now;
44555               result = fn.apply(storedContext, storedArgs);
44556               if (!timeout) {
44557                   storedContext = null;
44558                   storedArgs = [];
44559               }
44560           }
44561           else if (!timeout) {
44562               timeout = window.setTimeout(later, remaining);
44563           }
44564           return result;
44565       };
44566   }
44567   
44568 });
44569
44570  
44571
44572