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(ev, he)
2691     {
2692         if (!this.header_image_fit_square) {
2693             return;
2694         }
2695         
2696         var hw = he.naturalHeight / he.naturalWidth;
2697         // wide image = < 0
2698         // tall image = > 1
2699         //var w = he.dom.naturalWidth;
2700         var ww = he.width;
2701         Roo.get(he).setX( 0 );
2702         if (hw > 1) {
2703             Roo.get(he).setSize( ww * (1/hw),  ww);
2704             Roo.get(he).setX( (ww - (ww * (1/hw)))/ 2);
2705         }
2706
2707     }
2708
2709     
2710 });
2711
2712 /*
2713  * - LGPL
2714  *
2715  * Card header - holder for the card header elements.
2716  * 
2717  */
2718
2719 /**
2720  * @class Roo.bootstrap.CardHeader
2721  * @extends Roo.bootstrap.Element
2722  * Bootstrap CardHeader class
2723  * @constructor
2724  * Create a new Card Header - that you can embed children into
2725  * @param {Object} config The config object
2726  */
2727
2728 Roo.bootstrap.CardHeader = function(config){
2729     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2730 };
2731
2732 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2733     
2734     
2735     container_method : 'getCardHeader' 
2736     
2737      
2738     
2739     
2740    
2741 });
2742
2743  
2744
2745  /*
2746  * - LGPL
2747  *
2748  * Card footer - holder for the card footer elements.
2749  * 
2750  */
2751
2752 /**
2753  * @class Roo.bootstrap.CardFooter
2754  * @extends Roo.bootstrap.Element
2755  * Bootstrap CardFooter class
2756  * @constructor
2757  * Create a new Card Footer - that you can embed children into
2758  * @param {Object} config The config object
2759  */
2760
2761 Roo.bootstrap.CardFooter = function(config){
2762     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2763 };
2764
2765 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2766     
2767     
2768     container_method : 'getCardFooter' 
2769     
2770      
2771     
2772     
2773    
2774 });
2775
2776  
2777
2778  /*
2779  * - LGPL
2780  *
2781  * Card header - holder for the card header elements.
2782  * 
2783  */
2784
2785 /**
2786  * @class Roo.bootstrap.CardImageTop
2787  * @extends Roo.bootstrap.Element
2788  * Bootstrap CardImageTop class
2789  * @constructor
2790  * Create a new Card Image Top container
2791  * @param {Object} config The config object
2792  */
2793
2794 Roo.bootstrap.CardImageTop = function(config){
2795     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2796 };
2797
2798 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2799     
2800    
2801     container_method : 'getCardImageTop' 
2802     
2803      
2804     
2805    
2806 });
2807
2808  
2809
2810  /*
2811  * - LGPL
2812  *
2813  * image
2814  * 
2815  */
2816
2817
2818 /**
2819  * @class Roo.bootstrap.Img
2820  * @extends Roo.bootstrap.Component
2821  * Bootstrap Img class
2822  * @cfg {Boolean} imgResponsive false | true
2823  * @cfg {String} border rounded | circle | thumbnail
2824  * @cfg {String} src image source
2825  * @cfg {String} alt image alternative text
2826  * @cfg {String} href a tag href
2827  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2828  * @cfg {String} xsUrl xs image source
2829  * @cfg {String} smUrl sm image source
2830  * @cfg {String} mdUrl md image source
2831  * @cfg {String} lgUrl lg image source
2832  * 
2833  * @constructor
2834  * Create a new Input
2835  * @param {Object} config The config object
2836  */
2837
2838 Roo.bootstrap.Img = function(config){
2839     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2840     
2841     this.addEvents({
2842         // img events
2843         /**
2844          * @event click
2845          * The img click event for the img.
2846          * @param {Roo.EventObject} e
2847          */
2848         "click" : true
2849     });
2850 };
2851
2852 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2853     
2854     imgResponsive: true,
2855     border: '',
2856     src: 'about:blank',
2857     href: false,
2858     target: false,
2859     xsUrl: '',
2860     smUrl: '',
2861     mdUrl: '',
2862     lgUrl: '',
2863
2864     getAutoCreate : function()
2865     {   
2866         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2867             return this.createSingleImg();
2868         }
2869         
2870         var cfg = {
2871             tag: 'div',
2872             cls: 'roo-image-responsive-group',
2873             cn: []
2874         };
2875         var _this = this;
2876         
2877         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2878             
2879             if(!_this[size + 'Url']){
2880                 return;
2881             }
2882             
2883             var img = {
2884                 tag: 'img',
2885                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2886                 html: _this.html || cfg.html,
2887                 src: _this[size + 'Url']
2888             };
2889             
2890             img.cls += ' roo-image-responsive-' + size;
2891             
2892             var s = ['xs', 'sm', 'md', 'lg'];
2893             
2894             s.splice(s.indexOf(size), 1);
2895             
2896             Roo.each(s, function(ss){
2897                 img.cls += ' hidden-' + ss;
2898             });
2899             
2900             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2901                 cfg.cls += ' img-' + _this.border;
2902             }
2903             
2904             if(_this.alt){
2905                 cfg.alt = _this.alt;
2906             }
2907             
2908             if(_this.href){
2909                 var a = {
2910                     tag: 'a',
2911                     href: _this.href,
2912                     cn: [
2913                         img
2914                     ]
2915                 };
2916
2917                 if(this.target){
2918                     a.target = _this.target;
2919                 }
2920             }
2921             
2922             cfg.cn.push((_this.href) ? a : img);
2923             
2924         });
2925         
2926         return cfg;
2927     },
2928     
2929     createSingleImg : function()
2930     {
2931         var cfg = {
2932             tag: 'img',
2933             cls: (this.imgResponsive) ? 'img-responsive' : '',
2934             html : null,
2935             src : 'about:blank'  // just incase src get's set to undefined?!?
2936         };
2937         
2938         cfg.html = this.html || cfg.html;
2939         
2940         cfg.src = this.src || cfg.src;
2941         
2942         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2943             cfg.cls += ' img-' + this.border;
2944         }
2945         
2946         if(this.alt){
2947             cfg.alt = this.alt;
2948         }
2949         
2950         if(this.href){
2951             var a = {
2952                 tag: 'a',
2953                 href: this.href,
2954                 cn: [
2955                     cfg
2956                 ]
2957             };
2958             
2959             if(this.target){
2960                 a.target = this.target;
2961             }
2962             
2963         }
2964         
2965         return (this.href) ? a : cfg;
2966     },
2967     
2968     initEvents: function() 
2969     {
2970         if(!this.href){
2971             this.el.on('click', this.onClick, this);
2972         }
2973         
2974     },
2975     
2976     onClick : function(e)
2977     {
2978         Roo.log('img onclick');
2979         this.fireEvent('click', this, e);
2980     },
2981     /**
2982      * Sets the url of the image - used to update it
2983      * @param {String} url the url of the image
2984      */
2985     
2986     setSrc : function(url)
2987     {
2988         this.src =  url;
2989         
2990         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2991             this.el.dom.src =  url;
2992             return;
2993         }
2994         
2995         this.el.select('img', true).first().dom.src =  url;
2996     }
2997     
2998     
2999    
3000 });
3001
3002  /*
3003  * - LGPL
3004  *
3005  * image
3006  * 
3007  */
3008
3009
3010 /**
3011  * @class Roo.bootstrap.Link
3012  * @extends Roo.bootstrap.Component
3013  * Bootstrap Link Class
3014  * @cfg {String} alt image alternative text
3015  * @cfg {String} href a tag href
3016  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3017  * @cfg {String} html the content of the link.
3018  * @cfg {String} anchor name for the anchor link
3019  * @cfg {String} fa - favicon
3020
3021  * @cfg {Boolean} preventDefault (true | false) default false
3022
3023  * 
3024  * @constructor
3025  * Create a new Input
3026  * @param {Object} config The config object
3027  */
3028
3029 Roo.bootstrap.Link = function(config){
3030     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3031     
3032     this.addEvents({
3033         // img events
3034         /**
3035          * @event click
3036          * The img click event for the img.
3037          * @param {Roo.EventObject} e
3038          */
3039         "click" : true
3040     });
3041 };
3042
3043 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3044     
3045     href: false,
3046     target: false,
3047     preventDefault: false,
3048     anchor : false,
3049     alt : false,
3050     fa: false,
3051
3052
3053     getAutoCreate : function()
3054     {
3055         var html = this.html || '';
3056         
3057         if (this.fa !== false) {
3058             html = '<i class="fa fa-' + this.fa + '"></i>';
3059         }
3060         var cfg = {
3061             tag: 'a'
3062         };
3063         // anchor's do not require html/href...
3064         if (this.anchor === false) {
3065             cfg.html = html;
3066             cfg.href = this.href || '#';
3067         } else {
3068             cfg.name = this.anchor;
3069             if (this.html !== false || this.fa !== false) {
3070                 cfg.html = html;
3071             }
3072             if (this.href !== false) {
3073                 cfg.href = this.href;
3074             }
3075         }
3076         
3077         if(this.alt !== false){
3078             cfg.alt = this.alt;
3079         }
3080         
3081         
3082         if(this.target !== false) {
3083             cfg.target = this.target;
3084         }
3085         
3086         return cfg;
3087     },
3088     
3089     initEvents: function() {
3090         
3091         if(!this.href || this.preventDefault){
3092             this.el.on('click', this.onClick, this);
3093         }
3094     },
3095     
3096     onClick : function(e)
3097     {
3098         if(this.preventDefault){
3099             e.preventDefault();
3100         }
3101         //Roo.log('img onclick');
3102         this.fireEvent('click', this, e);
3103     }
3104    
3105 });
3106
3107  /*
3108  * - LGPL
3109  *
3110  * header
3111  * 
3112  */
3113
3114 /**
3115  * @class Roo.bootstrap.Header
3116  * @extends Roo.bootstrap.Component
3117  * Bootstrap Header class
3118  * @cfg {String} html content of header
3119  * @cfg {Number} level (1|2|3|4|5|6) default 1
3120  * 
3121  * @constructor
3122  * Create a new Header
3123  * @param {Object} config The config object
3124  */
3125
3126
3127 Roo.bootstrap.Header  = function(config){
3128     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3129 };
3130
3131 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3132     
3133     //href : false,
3134     html : false,
3135     level : 1,
3136     
3137     
3138     
3139     getAutoCreate : function(){
3140         
3141         
3142         
3143         var cfg = {
3144             tag: 'h' + (1 *this.level),
3145             html: this.html || ''
3146         } ;
3147         
3148         return cfg;
3149     }
3150    
3151 });
3152
3153  
3154
3155  /*
3156  * Based on:
3157  * Ext JS Library 1.1.1
3158  * Copyright(c) 2006-2007, Ext JS, LLC.
3159  *
3160  * Originally Released Under LGPL - original licence link has changed is not relivant.
3161  *
3162  * Fork - LGPL
3163  * <script type="text/javascript">
3164  */
3165  
3166 /**
3167  * @class Roo.bootstrap.MenuMgr
3168  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3169  * @singleton
3170  */
3171 Roo.bootstrap.MenuMgr = function(){
3172    var menus, active, groups = {}, attached = false, lastShow = new Date();
3173
3174    // private - called when first menu is created
3175    function init(){
3176        menus = {};
3177        active = new Roo.util.MixedCollection();
3178        Roo.get(document).addKeyListener(27, function(){
3179            if(active.length > 0){
3180                hideAll();
3181            }
3182        });
3183    }
3184
3185    // private
3186    function hideAll(){
3187        if(active && active.length > 0){
3188            var c = active.clone();
3189            c.each(function(m){
3190                m.hide();
3191            });
3192        }
3193    }
3194
3195    // private
3196    function onHide(m){
3197        active.remove(m);
3198        if(active.length < 1){
3199            Roo.get(document).un("mouseup", onMouseDown);
3200             
3201            attached = false;
3202        }
3203    }
3204
3205    // private
3206    function onShow(m){
3207        var last = active.last();
3208        lastShow = new Date();
3209        active.add(m);
3210        if(!attached){
3211           Roo.get(document).on("mouseup", onMouseDown);
3212            
3213            attached = true;
3214        }
3215        if(m.parentMenu){
3216           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3217           m.parentMenu.activeChild = m;
3218        }else if(last && last.isVisible()){
3219           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3220        }
3221    }
3222
3223    // private
3224    function onBeforeHide(m){
3225        if(m.activeChild){
3226            m.activeChild.hide();
3227        }
3228        if(m.autoHideTimer){
3229            clearTimeout(m.autoHideTimer);
3230            delete m.autoHideTimer;
3231        }
3232    }
3233
3234    // private
3235    function onBeforeShow(m){
3236        var pm = m.parentMenu;
3237        if(!pm && !m.allowOtherMenus){
3238            hideAll();
3239        }else if(pm && pm.activeChild && active != m){
3240            pm.activeChild.hide();
3241        }
3242    }
3243
3244    // private this should really trigger on mouseup..
3245    function onMouseDown(e){
3246         Roo.log("on Mouse Up");
3247         
3248         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3249             Roo.log("MenuManager hideAll");
3250             hideAll();
3251             e.stopEvent();
3252         }
3253         
3254         
3255    }
3256
3257    // private
3258    function onBeforeCheck(mi, state){
3259        if(state){
3260            var g = groups[mi.group];
3261            for(var i = 0, l = g.length; i < l; i++){
3262                if(g[i] != mi){
3263                    g[i].setChecked(false);
3264                }
3265            }
3266        }
3267    }
3268
3269    return {
3270
3271        /**
3272         * Hides all menus that are currently visible
3273         */
3274        hideAll : function(){
3275             hideAll();  
3276        },
3277
3278        // private
3279        register : function(menu){
3280            if(!menus){
3281                init();
3282            }
3283            menus[menu.id] = menu;
3284            menu.on("beforehide", onBeforeHide);
3285            menu.on("hide", onHide);
3286            menu.on("beforeshow", onBeforeShow);
3287            menu.on("show", onShow);
3288            var g = menu.group;
3289            if(g && menu.events["checkchange"]){
3290                if(!groups[g]){
3291                    groups[g] = [];
3292                }
3293                groups[g].push(menu);
3294                menu.on("checkchange", onCheck);
3295            }
3296        },
3297
3298         /**
3299          * Returns a {@link Roo.menu.Menu} object
3300          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3301          * be used to generate and return a new Menu instance.
3302          */
3303        get : function(menu){
3304            if(typeof menu == "string"){ // menu id
3305                return menus[menu];
3306            }else if(menu.events){  // menu instance
3307                return menu;
3308            }
3309            /*else if(typeof menu.length == 'number'){ // array of menu items?
3310                return new Roo.bootstrap.Menu({items:menu});
3311            }else{ // otherwise, must be a config
3312                return new Roo.bootstrap.Menu(menu);
3313            }
3314            */
3315            return false;
3316        },
3317
3318        // private
3319        unregister : function(menu){
3320            delete menus[menu.id];
3321            menu.un("beforehide", onBeforeHide);
3322            menu.un("hide", onHide);
3323            menu.un("beforeshow", onBeforeShow);
3324            menu.un("show", onShow);
3325            var g = menu.group;
3326            if(g && menu.events["checkchange"]){
3327                groups[g].remove(menu);
3328                menu.un("checkchange", onCheck);
3329            }
3330        },
3331
3332        // private
3333        registerCheckable : function(menuItem){
3334            var g = menuItem.group;
3335            if(g){
3336                if(!groups[g]){
3337                    groups[g] = [];
3338                }
3339                groups[g].push(menuItem);
3340                menuItem.on("beforecheckchange", onBeforeCheck);
3341            }
3342        },
3343
3344        // private
3345        unregisterCheckable : function(menuItem){
3346            var g = menuItem.group;
3347            if(g){
3348                groups[g].remove(menuItem);
3349                menuItem.un("beforecheckchange", onBeforeCheck);
3350            }
3351        }
3352    };
3353 }();/*
3354  * - LGPL
3355  *
3356  * menu
3357  * 
3358  */
3359
3360 /**
3361  * @class Roo.bootstrap.Menu
3362  * @extends Roo.bootstrap.Component
3363  * Bootstrap Menu class - container for MenuItems
3364  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3365  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3366  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3367  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3368  * 
3369  * @constructor
3370  * Create a new Menu
3371  * @param {Object} config The config object
3372  */
3373
3374
3375 Roo.bootstrap.Menu = function(config){
3376     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3377     if (this.registerMenu && this.type != 'treeview')  {
3378         Roo.bootstrap.MenuMgr.register(this);
3379     }
3380     
3381     
3382     this.addEvents({
3383         /**
3384          * @event beforeshow
3385          * Fires before this menu is displayed (return false to block)
3386          * @param {Roo.menu.Menu} this
3387          */
3388         beforeshow : true,
3389         /**
3390          * @event beforehide
3391          * Fires before this menu is hidden (return false to block)
3392          * @param {Roo.menu.Menu} this
3393          */
3394         beforehide : true,
3395         /**
3396          * @event show
3397          * Fires after this menu is displayed
3398          * @param {Roo.menu.Menu} this
3399          */
3400         show : true,
3401         /**
3402          * @event hide
3403          * Fires after this menu is hidden
3404          * @param {Roo.menu.Menu} this
3405          */
3406         hide : true,
3407         /**
3408          * @event click
3409          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3410          * @param {Roo.menu.Menu} this
3411          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3412          * @param {Roo.EventObject} e
3413          */
3414         click : true,
3415         /**
3416          * @event mouseover
3417          * Fires when the mouse is hovering over this menu
3418          * @param {Roo.menu.Menu} this
3419          * @param {Roo.EventObject} e
3420          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3421          */
3422         mouseover : true,
3423         /**
3424          * @event mouseout
3425          * Fires when the mouse exits this menu
3426          * @param {Roo.menu.Menu} this
3427          * @param {Roo.EventObject} e
3428          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3429          */
3430         mouseout : true,
3431         /**
3432          * @event itemclick
3433          * Fires when a menu item contained in this menu is clicked
3434          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3435          * @param {Roo.EventObject} e
3436          */
3437         itemclick: true
3438     });
3439     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3440 };
3441
3442 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3443     
3444    /// html : false,
3445     //align : '',
3446     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3447     type: false,
3448     /**
3449      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3450      */
3451     registerMenu : true,
3452     
3453     menuItems :false, // stores the menu items..
3454     
3455     hidden:true,
3456         
3457     parentMenu : false,
3458     
3459     stopEvent : true,
3460     
3461     isLink : false,
3462     
3463     getChildContainer : function() {
3464         return this.el;  
3465     },
3466     
3467     getAutoCreate : function(){
3468          
3469         //if (['right'].indexOf(this.align)!==-1) {
3470         //    cfg.cn[1].cls += ' pull-right'
3471         //}
3472         
3473         
3474         var cfg = {
3475             tag : 'ul',
3476             cls : 'dropdown-menu' ,
3477             style : 'z-index:1000'
3478             
3479         };
3480         
3481         if (this.type === 'submenu') {
3482             cfg.cls = 'submenu active';
3483         }
3484         if (this.type === 'treeview') {
3485             cfg.cls = 'treeview-menu';
3486         }
3487         
3488         return cfg;
3489     },
3490     initEvents : function() {
3491         
3492        // Roo.log("ADD event");
3493        // Roo.log(this.triggerEl.dom);
3494         
3495         this.triggerEl.on('click', this.onTriggerClick, this);
3496         
3497         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3498         
3499         
3500         if (this.triggerEl.hasClass('nav-item')) {
3501             // dropdown toggle on the 'a' in BS4?
3502             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3503         } else {
3504             this.triggerEl.addClass('dropdown-toggle');
3505         }
3506         if (Roo.isTouch) {
3507             this.el.on('touchstart'  , this.onTouch, this);
3508         }
3509         this.el.on('click' , this.onClick, this);
3510
3511         this.el.on("mouseover", this.onMouseOver, this);
3512         this.el.on("mouseout", this.onMouseOut, this);
3513         
3514     },
3515     
3516     findTargetItem : function(e)
3517     {
3518         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3519         if(!t){
3520             return false;
3521         }
3522         //Roo.log(t);         Roo.log(t.id);
3523         if(t && t.id){
3524             //Roo.log(this.menuitems);
3525             return this.menuitems.get(t.id);
3526             
3527             //return this.items.get(t.menuItemId);
3528         }
3529         
3530         return false;
3531     },
3532     
3533     onTouch : function(e) 
3534     {
3535         Roo.log("menu.onTouch");
3536         //e.stopEvent(); this make the user popdown broken
3537         this.onClick(e);
3538     },
3539     
3540     onClick : function(e)
3541     {
3542         Roo.log("menu.onClick");
3543         
3544         var t = this.findTargetItem(e);
3545         if(!t || t.isContainer){
3546             return;
3547         }
3548         Roo.log(e);
3549         /*
3550         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3551             if(t == this.activeItem && t.shouldDeactivate(e)){
3552                 this.activeItem.deactivate();
3553                 delete this.activeItem;
3554                 return;
3555             }
3556             if(t.canActivate){
3557                 this.setActiveItem(t, true);
3558             }
3559             return;
3560             
3561             
3562         }
3563         */
3564        
3565         Roo.log('pass click event');
3566         
3567         t.onClick(e);
3568         
3569         this.fireEvent("click", this, t, e);
3570         
3571         var _this = this;
3572         
3573         if(!t.href.length || t.href == '#'){
3574             (function() { _this.hide(); }).defer(100);
3575         }
3576         
3577     },
3578     
3579     onMouseOver : function(e){
3580         var t  = this.findTargetItem(e);
3581         //Roo.log(t);
3582         //if(t){
3583         //    if(t.canActivate && !t.disabled){
3584         //        this.setActiveItem(t, true);
3585         //    }
3586         //}
3587         
3588         this.fireEvent("mouseover", this, e, t);
3589     },
3590     isVisible : function(){
3591         return !this.hidden;
3592     },
3593     onMouseOut : function(e){
3594         var t  = this.findTargetItem(e);
3595         
3596         //if(t ){
3597         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3598         //        this.activeItem.deactivate();
3599         //        delete this.activeItem;
3600         //    }
3601         //}
3602         this.fireEvent("mouseout", this, e, t);
3603     },
3604     
3605     
3606     /**
3607      * Displays this menu relative to another element
3608      * @param {String/HTMLElement/Roo.Element} element The element to align to
3609      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3610      * the element (defaults to this.defaultAlign)
3611      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3612      */
3613     show : function(el, pos, parentMenu)
3614     {
3615         if (false === this.fireEvent("beforeshow", this)) {
3616             Roo.log("show canceled");
3617             return;
3618         }
3619         this.parentMenu = parentMenu;
3620         if(!this.el){
3621             this.render();
3622         }
3623         
3624         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3625     },
3626      /**
3627      * Displays this menu at a specific xy position
3628      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3629      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3630      */
3631     showAt : function(xy, parentMenu, /* private: */_e){
3632         this.parentMenu = parentMenu;
3633         if(!this.el){
3634             this.render();
3635         }
3636         if(_e !== false){
3637             this.fireEvent("beforeshow", this);
3638             //xy = this.el.adjustForConstraints(xy);
3639         }
3640         
3641         //this.el.show();
3642         this.hideMenuItems();
3643         this.hidden = false;
3644         this.triggerEl.addClass('open');
3645         this.el.addClass('show');
3646         
3647         // reassign x when hitting right
3648         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3649             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3650         }
3651         
3652         // reassign y when hitting bottom
3653         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3654             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3655         }
3656         
3657         // but the list may align on trigger left or trigger top... should it be a properity?
3658         
3659         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3660             this.el.setXY(xy);
3661         }
3662         
3663         this.focus();
3664         this.fireEvent("show", this);
3665     },
3666     
3667     focus : function(){
3668         return;
3669         if(!this.hidden){
3670             this.doFocus.defer(50, this);
3671         }
3672     },
3673
3674     doFocus : function(){
3675         if(!this.hidden){
3676             this.focusEl.focus();
3677         }
3678     },
3679
3680     /**
3681      * Hides this menu and optionally all parent menus
3682      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3683      */
3684     hide : function(deep)
3685     {
3686         if (false === this.fireEvent("beforehide", this)) {
3687             Roo.log("hide canceled");
3688             return;
3689         }
3690         this.hideMenuItems();
3691         if(this.el && this.isVisible()){
3692            
3693             if(this.activeItem){
3694                 this.activeItem.deactivate();
3695                 this.activeItem = null;
3696             }
3697             this.triggerEl.removeClass('open');;
3698             this.el.removeClass('show');
3699             this.hidden = true;
3700             this.fireEvent("hide", this);
3701         }
3702         if(deep === true && this.parentMenu){
3703             this.parentMenu.hide(true);
3704         }
3705     },
3706     
3707     onTriggerClick : function(e)
3708     {
3709         Roo.log('trigger click');
3710         
3711         var target = e.getTarget();
3712         
3713         Roo.log(target.nodeName.toLowerCase());
3714         
3715         if(target.nodeName.toLowerCase() === 'i'){
3716             e.preventDefault();
3717         }
3718         
3719     },
3720     
3721     onTriggerPress  : function(e)
3722     {
3723         Roo.log('trigger press');
3724         //Roo.log(e.getTarget());
3725        // Roo.log(this.triggerEl.dom);
3726        
3727         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3728         var pel = Roo.get(e.getTarget());
3729         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3730             Roo.log('is treeview or dropdown?');
3731             return;
3732         }
3733         
3734         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3735             return;
3736         }
3737         
3738         if (this.isVisible()) {
3739             Roo.log('hide');
3740             this.hide();
3741         } else {
3742             Roo.log('show');
3743             this.show(this.triggerEl, '?', false);
3744         }
3745         
3746         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3747             e.stopEvent();
3748         }
3749         
3750     },
3751        
3752     
3753     hideMenuItems : function()
3754     {
3755         Roo.log("hide Menu Items");
3756         if (!this.el) { 
3757             return;
3758         }
3759         
3760         this.el.select('.open',true).each(function(aa) {
3761             
3762             aa.removeClass('open');
3763          
3764         });
3765     },
3766     addxtypeChild : function (tree, cntr) {
3767         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3768           
3769         this.menuitems.add(comp);
3770         return comp;
3771
3772     },
3773     getEl : function()
3774     {
3775         Roo.log(this.el);
3776         return this.el;
3777     },
3778     
3779     clear : function()
3780     {
3781         this.getEl().dom.innerHTML = '';
3782         this.menuitems.clear();
3783     }
3784 });
3785
3786  
3787  /*
3788  * - LGPL
3789  *
3790  * menu item
3791  * 
3792  */
3793
3794
3795 /**
3796  * @class Roo.bootstrap.MenuItem
3797  * @extends Roo.bootstrap.Component
3798  * Bootstrap MenuItem class
3799  * @cfg {String} html the menu label
3800  * @cfg {String} href the link
3801  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3802  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3803  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3804  * @cfg {String} fa favicon to show on left of menu item.
3805  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3806  * 
3807  * 
3808  * @constructor
3809  * Create a new MenuItem
3810  * @param {Object} config The config object
3811  */
3812
3813
3814 Roo.bootstrap.MenuItem = function(config){
3815     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3816     this.addEvents({
3817         // raw events
3818         /**
3819          * @event click
3820          * The raw click event for the entire grid.
3821          * @param {Roo.bootstrap.MenuItem} this
3822          * @param {Roo.EventObject} e
3823          */
3824         "click" : true
3825     });
3826 };
3827
3828 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3829     
3830     href : false,
3831     html : false,
3832     preventDefault: false,
3833     isContainer : false,
3834     active : false,
3835     fa: false,
3836     
3837     getAutoCreate : function(){
3838         
3839         if(this.isContainer){
3840             return {
3841                 tag: 'li',
3842                 cls: 'dropdown-menu-item '
3843             };
3844         }
3845         var ctag = {
3846             tag: 'span',
3847             html: 'Link'
3848         };
3849         
3850         var anc = {
3851             tag : 'a',
3852             cls : 'dropdown-item',
3853             href : '#',
3854             cn : [  ]
3855         };
3856         
3857         if (this.fa !== false) {
3858             anc.cn.push({
3859                 tag : 'i',
3860                 cls : 'fa fa-' + this.fa
3861             });
3862         }
3863         
3864         anc.cn.push(ctag);
3865         
3866         
3867         var cfg= {
3868             tag: 'li',
3869             cls: 'dropdown-menu-item',
3870             cn: [ anc ]
3871         };
3872         if (this.parent().type == 'treeview') {
3873             cfg.cls = 'treeview-menu';
3874         }
3875         if (this.active) {
3876             cfg.cls += ' active';
3877         }
3878         
3879         
3880         
3881         anc.href = this.href || cfg.cn[0].href ;
3882         ctag.html = this.html || cfg.cn[0].html ;
3883         return cfg;
3884     },
3885     
3886     initEvents: function()
3887     {
3888         if (this.parent().type == 'treeview') {
3889             this.el.select('a').on('click', this.onClick, this);
3890         }
3891         
3892         if (this.menu) {
3893             this.menu.parentType = this.xtype;
3894             this.menu.triggerEl = this.el;
3895             this.menu = this.addxtype(Roo.apply({}, this.menu));
3896         }
3897         
3898     },
3899     onClick : function(e)
3900     {
3901         Roo.log('item on click ');
3902         
3903         if(this.preventDefault){
3904             e.preventDefault();
3905         }
3906         //this.parent().hideMenuItems();
3907         
3908         this.fireEvent('click', this, e);
3909     },
3910     getEl : function()
3911     {
3912         return this.el;
3913     } 
3914 });
3915
3916  
3917
3918  /*
3919  * - LGPL
3920  *
3921  * menu separator
3922  * 
3923  */
3924
3925
3926 /**
3927  * @class Roo.bootstrap.MenuSeparator
3928  * @extends Roo.bootstrap.Component
3929  * Bootstrap MenuSeparator class
3930  * 
3931  * @constructor
3932  * Create a new MenuItem
3933  * @param {Object} config The config object
3934  */
3935
3936
3937 Roo.bootstrap.MenuSeparator = function(config){
3938     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3939 };
3940
3941 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3942     
3943     getAutoCreate : function(){
3944         var cfg = {
3945             cls: 'divider',
3946             tag : 'li'
3947         };
3948         
3949         return cfg;
3950     }
3951    
3952 });
3953
3954  
3955
3956  
3957 /*
3958 * Licence: LGPL
3959 */
3960
3961 /**
3962  * @class Roo.bootstrap.Modal
3963  * @extends Roo.bootstrap.Component
3964  * Bootstrap Modal class
3965  * @cfg {String} title Title of dialog
3966  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3967  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3968  * @cfg {Boolean} specificTitle default false
3969  * @cfg {Array} buttons Array of buttons or standard button set..
3970  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3971  * @cfg {Boolean} animate default true
3972  * @cfg {Boolean} allow_close default true
3973  * @cfg {Boolean} fitwindow default false
3974  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3975  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3976  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3977  * @cfg {String} size (sm|lg|xl) default empty
3978  * @cfg {Number} max_width set the max width of modal
3979  * @cfg {Boolean} editableTitle can the title be edited
3980
3981  *
3982  *
3983  * @constructor
3984  * Create a new Modal Dialog
3985  * @param {Object} config The config object
3986  */
3987
3988 Roo.bootstrap.Modal = function(config){
3989     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3990     this.addEvents({
3991         // raw events
3992         /**
3993          * @event btnclick
3994          * The raw btnclick event for the button
3995          * @param {Roo.EventObject} e
3996          */
3997         "btnclick" : true,
3998         /**
3999          * @event resize
4000          * Fire when dialog resize
4001          * @param {Roo.bootstrap.Modal} this
4002          * @param {Roo.EventObject} e
4003          */
4004         "resize" : true,
4005         /**
4006          * @event titlechanged
4007          * Fire when the editable title has been changed
4008          * @param {Roo.bootstrap.Modal} this
4009          * @param {Roo.EventObject} value
4010          */
4011         "titlechanged" : true 
4012         
4013     });
4014     this.buttons = this.buttons || [];
4015
4016     if (this.tmpl) {
4017         this.tmpl = Roo.factory(this.tmpl);
4018     }
4019
4020 };
4021
4022 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4023
4024     title : 'test dialog',
4025
4026     buttons : false,
4027
4028     // set on load...
4029
4030     html: false,
4031
4032     tmp: false,
4033
4034     specificTitle: false,
4035
4036     buttonPosition: 'right',
4037
4038     allow_close : true,
4039
4040     animate : true,
4041
4042     fitwindow: false,
4043     
4044      // private
4045     dialogEl: false,
4046     bodyEl:  false,
4047     footerEl:  false,
4048     titleEl:  false,
4049     closeEl:  false,
4050
4051     size: '',
4052     
4053     max_width: 0,
4054     
4055     max_height: 0,
4056     
4057     fit_content: false,
4058     editableTitle  : false,
4059
4060     onRender : function(ct, position)
4061     {
4062         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4063
4064         if(!this.el){
4065             var cfg = Roo.apply({},  this.getAutoCreate());
4066             cfg.id = Roo.id();
4067             //if(!cfg.name){
4068             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4069             //}
4070             //if (!cfg.name.length) {
4071             //    delete cfg.name;
4072            // }
4073             if (this.cls) {
4074                 cfg.cls += ' ' + this.cls;
4075             }
4076             if (this.style) {
4077                 cfg.style = this.style;
4078             }
4079             this.el = Roo.get(document.body).createChild(cfg, position);
4080         }
4081         //var type = this.el.dom.type;
4082
4083
4084         if(this.tabIndex !== undefined){
4085             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4086         }
4087
4088         this.dialogEl = this.el.select('.modal-dialog',true).first();
4089         this.bodyEl = this.el.select('.modal-body',true).first();
4090         this.closeEl = this.el.select('.modal-header .close', true).first();
4091         this.headerEl = this.el.select('.modal-header',true).first();
4092         this.titleEl = this.el.select('.modal-title',true).first();
4093         this.footerEl = this.el.select('.modal-footer',true).first();
4094
4095         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4096         
4097         //this.el.addClass("x-dlg-modal");
4098
4099         if (this.buttons.length) {
4100             Roo.each(this.buttons, function(bb) {
4101                 var b = Roo.apply({}, bb);
4102                 b.xns = b.xns || Roo.bootstrap;
4103                 b.xtype = b.xtype || 'Button';
4104                 if (typeof(b.listeners) == 'undefined') {
4105                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4106                 }
4107
4108                 var btn = Roo.factory(b);
4109
4110                 btn.render(this.getButtonContainer());
4111
4112             },this);
4113         }
4114         // render the children.
4115         var nitems = [];
4116
4117         if(typeof(this.items) != 'undefined'){
4118             var items = this.items;
4119             delete this.items;
4120
4121             for(var i =0;i < items.length;i++) {
4122                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4123             }
4124         }
4125
4126         this.items = nitems;
4127
4128         // where are these used - they used to be body/close/footer
4129
4130
4131         this.initEvents();
4132         //this.el.addClass([this.fieldClass, this.cls]);
4133
4134     },
4135
4136     getAutoCreate : function()
4137     {
4138         // we will default to modal-body-overflow - might need to remove or make optional later.
4139         var bdy = {
4140                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4141                 html : this.html || ''
4142         };
4143
4144         var title = {
4145             tag: 'h5',
4146             cls : 'modal-title',
4147             html : this.title
4148         };
4149
4150         if(this.specificTitle){ // WTF is this?
4151             title = this.title;
4152         }
4153
4154         var header = [];
4155         if (this.allow_close && Roo.bootstrap.version == 3) {
4156             header.push({
4157                 tag: 'button',
4158                 cls : 'close',
4159                 html : '&times'
4160             });
4161         }
4162
4163         header.push(title);
4164
4165         if (this.editableTitle) {
4166             header.push({
4167                 cls: 'form-control roo-editable-title d-none',
4168                 tag: 'input',
4169                 type: 'text'
4170             });
4171         }
4172         
4173         if (this.allow_close && Roo.bootstrap.version == 4) {
4174             header.push({
4175                 tag: 'button',
4176                 cls : 'close',
4177                 html : '&times'
4178             });
4179         }
4180         
4181         var size = '';
4182
4183         if(this.size.length){
4184             size = 'modal-' + this.size;
4185         }
4186         
4187         var footer = Roo.bootstrap.version == 3 ?
4188             {
4189                 cls : 'modal-footer',
4190                 cn : [
4191                     {
4192                         tag: 'div',
4193                         cls: 'btn-' + this.buttonPosition
4194                     }
4195                 ]
4196
4197             } :
4198             {  // BS4 uses mr-auto on left buttons....
4199                 cls : 'modal-footer'
4200             };
4201
4202             
4203
4204         
4205         
4206         var modal = {
4207             cls: "modal",
4208              cn : [
4209                 {
4210                     cls: "modal-dialog " + size,
4211                     cn : [
4212                         {
4213                             cls : "modal-content",
4214                             cn : [
4215                                 {
4216                                     cls : 'modal-header',
4217                                     cn : header
4218                                 },
4219                                 bdy,
4220                                 footer
4221                             ]
4222
4223                         }
4224                     ]
4225
4226                 }
4227             ]
4228         };
4229
4230         if(this.animate){
4231             modal.cls += ' fade';
4232         }
4233
4234         return modal;
4235
4236     },
4237     getChildContainer : function() {
4238
4239          return this.bodyEl;
4240
4241     },
4242     getButtonContainer : function() {
4243         
4244          return Roo.bootstrap.version == 4 ?
4245             this.el.select('.modal-footer',true).first()
4246             : this.el.select('.modal-footer div',true).first();
4247
4248     },
4249     initEvents : function()
4250     {
4251         if (this.allow_close) {
4252             this.closeEl.on('click', this.hide, this);
4253         }
4254         Roo.EventManager.onWindowResize(this.resize, this, true);
4255         if (this.editableTitle) {
4256             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4257             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4258             this.headerEditEl.on('keyup', function(e) {
4259                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4260                         this.toggleHeaderInput(false)
4261                     }
4262                 }, this);
4263             this.headerEditEl.on('blur', function(e) {
4264                 this.toggleHeaderInput(false)
4265             },this);
4266         }
4267
4268     },
4269   
4270
4271     resize : function()
4272     {
4273         this.maskEl.setSize(
4274             Roo.lib.Dom.getViewWidth(true),
4275             Roo.lib.Dom.getViewHeight(true)
4276         );
4277         
4278         if (this.fitwindow) {
4279             
4280            this.dialogEl.setStyle( { 'max-width' : '100%' });
4281             this.setSize(
4282                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4283                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4284             );
4285             return;
4286         }
4287         
4288         if(this.max_width !== 0) {
4289             
4290             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4291             
4292             if(this.height) {
4293                 this.setSize(w, this.height);
4294                 return;
4295             }
4296             
4297             if(this.max_height) {
4298                 this.setSize(w,Math.min(
4299                     this.max_height,
4300                     Roo.lib.Dom.getViewportHeight(true) - 60
4301                 ));
4302                 
4303                 return;
4304             }
4305             
4306             if(!this.fit_content) {
4307                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4308                 return;
4309             }
4310             
4311             this.setSize(w, Math.min(
4312                 60 +
4313                 this.headerEl.getHeight() + 
4314                 this.footerEl.getHeight() + 
4315                 this.getChildHeight(this.bodyEl.dom.childNodes),
4316                 Roo.lib.Dom.getViewportHeight(true) - 60)
4317             );
4318         }
4319         
4320     },
4321
4322     setSize : function(w,h)
4323     {
4324         if (!w && !h) {
4325             return;
4326         }
4327         
4328         this.resizeTo(w,h);
4329     },
4330
4331     show : function() {
4332
4333         if (!this.rendered) {
4334             this.render();
4335         }
4336         this.toggleHeaderInput(false);
4337         //this.el.setStyle('display', 'block');
4338         this.el.removeClass('hideing');
4339         this.el.dom.style.display='block';
4340         
4341         Roo.get(document.body).addClass('modal-open');
4342  
4343         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4344             
4345             (function(){
4346                 this.el.addClass('show');
4347                 this.el.addClass('in');
4348             }).defer(50, this);
4349         }else{
4350             this.el.addClass('show');
4351             this.el.addClass('in');
4352         }
4353
4354         // not sure how we can show data in here..
4355         //if (this.tmpl) {
4356         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4357         //}
4358
4359         Roo.get(document.body).addClass("x-body-masked");
4360         
4361         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4362         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4363         this.maskEl.dom.style.display = 'block';
4364         this.maskEl.addClass('show');
4365         
4366         
4367         this.resize();
4368         
4369         this.fireEvent('show', this);
4370
4371         // set zindex here - otherwise it appears to be ignored...
4372         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4373
4374         (function () {
4375             this.items.forEach( function(e) {
4376                 e.layout ? e.layout() : false;
4377
4378             });
4379         }).defer(100,this);
4380
4381     },
4382     hide : function()
4383     {
4384         if(this.fireEvent("beforehide", this) !== false){
4385             
4386             this.maskEl.removeClass('show');
4387             
4388             this.maskEl.dom.style.display = '';
4389             Roo.get(document.body).removeClass("x-body-masked");
4390             this.el.removeClass('in');
4391             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4392
4393             if(this.animate){ // why
4394                 this.el.addClass('hideing');
4395                 this.el.removeClass('show');
4396                 (function(){
4397                     if (!this.el.hasClass('hideing')) {
4398                         return; // it's been shown again...
4399                     }
4400                     
4401                     this.el.dom.style.display='';
4402
4403                     Roo.get(document.body).removeClass('modal-open');
4404                     this.el.removeClass('hideing');
4405                 }).defer(150,this);
4406                 
4407             }else{
4408                 this.el.removeClass('show');
4409                 this.el.dom.style.display='';
4410                 Roo.get(document.body).removeClass('modal-open');
4411
4412             }
4413             this.fireEvent('hide', this);
4414         }
4415     },
4416     isVisible : function()
4417     {
4418         
4419         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4420         
4421     },
4422
4423     addButton : function(str, cb)
4424     {
4425
4426
4427         var b = Roo.apply({}, { html : str } );
4428         b.xns = b.xns || Roo.bootstrap;
4429         b.xtype = b.xtype || 'Button';
4430         if (typeof(b.listeners) == 'undefined') {
4431             b.listeners = { click : cb.createDelegate(this)  };
4432         }
4433
4434         var btn = Roo.factory(b);
4435
4436         btn.render(this.getButtonContainer());
4437
4438         return btn;
4439
4440     },
4441
4442     setDefaultButton : function(btn)
4443     {
4444         //this.el.select('.modal-footer').()
4445     },
4446
4447     resizeTo: function(w,h)
4448     {
4449         this.dialogEl.setWidth(w);
4450         
4451         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4452
4453         this.bodyEl.setHeight(h - diff);
4454         
4455         this.fireEvent('resize', this);
4456     },
4457     
4458     setContentSize  : function(w, h)
4459     {
4460
4461     },
4462     onButtonClick: function(btn,e)
4463     {
4464         //Roo.log([a,b,c]);
4465         this.fireEvent('btnclick', btn.name, e);
4466     },
4467      /**
4468      * Set the title of the Dialog
4469      * @param {String} str new Title
4470      */
4471     setTitle: function(str) {
4472         this.titleEl.dom.innerHTML = str;
4473         this.title = str;
4474     },
4475     /**
4476      * Set the body of the Dialog
4477      * @param {String} str new Title
4478      */
4479     setBody: function(str) {
4480         this.bodyEl.dom.innerHTML = str;
4481     },
4482     /**
4483      * Set the body of the Dialog using the template
4484      * @param {Obj} data - apply this data to the template and replace the body contents.
4485      */
4486     applyBody: function(obj)
4487     {
4488         if (!this.tmpl) {
4489             Roo.log("Error - using apply Body without a template");
4490             //code
4491         }
4492         this.tmpl.overwrite(this.bodyEl, obj);
4493     },
4494     
4495     getChildHeight : function(child_nodes)
4496     {
4497         if(
4498             !child_nodes ||
4499             child_nodes.length == 0
4500         ) {
4501             return 0;
4502         }
4503         
4504         var child_height = 0;
4505         
4506         for(var i = 0; i < child_nodes.length; i++) {
4507             
4508             /*
4509             * for modal with tabs...
4510             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4511                 
4512                 var layout_childs = child_nodes[i].childNodes;
4513                 
4514                 for(var j = 0; j < layout_childs.length; j++) {
4515                     
4516                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4517                         
4518                         var layout_body_childs = layout_childs[j].childNodes;
4519                         
4520                         for(var k = 0; k < layout_body_childs.length; k++) {
4521                             
4522                             if(layout_body_childs[k].classList.contains('navbar')) {
4523                                 child_height += layout_body_childs[k].offsetHeight;
4524                                 continue;
4525                             }
4526                             
4527                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4528                                 
4529                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4530                                 
4531                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4532                                     
4533                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4534                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4535                                         continue;
4536                                     }
4537                                     
4538                                 }
4539                                 
4540                             }
4541                             
4542                         }
4543                     }
4544                 }
4545                 continue;
4546             }
4547             */
4548             
4549             child_height += child_nodes[i].offsetHeight;
4550             // Roo.log(child_nodes[i].offsetHeight);
4551         }
4552         
4553         return child_height;
4554     },
4555     toggleHeaderInput : function(is_edit)
4556     {
4557         if (!this.editableTitle) {
4558             return; // not editable.
4559         }
4560         if (is_edit && this.is_header_editing) {
4561             return; // already editing..
4562         }
4563         if (is_edit) {
4564     
4565             this.headerEditEl.dom.value = this.title;
4566             this.headerEditEl.removeClass('d-none');
4567             this.headerEditEl.dom.focus();
4568             this.titleEl.addClass('d-none');
4569             
4570             this.is_header_editing = true;
4571             return
4572         }
4573         // flip back to not editing.
4574         this.title = this.headerEditEl.dom.value;
4575         this.headerEditEl.addClass('d-none');
4576         this.titleEl.removeClass('d-none');
4577         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4578         this.is_header_editing = false;
4579         this.fireEvent('titlechanged', this, this.title);
4580     
4581             
4582         
4583     }
4584
4585 });
4586
4587
4588 Roo.apply(Roo.bootstrap.Modal,  {
4589     /**
4590          * Button config that displays a single OK button
4591          * @type Object
4592          */
4593         OK :  [{
4594             name : 'ok',
4595             weight : 'primary',
4596             html : 'OK'
4597         }],
4598         /**
4599          * Button config that displays Yes and No buttons
4600          * @type Object
4601          */
4602         YESNO : [
4603             {
4604                 name  : 'no',
4605                 html : 'No'
4606             },
4607             {
4608                 name  :'yes',
4609                 weight : 'primary',
4610                 html : 'Yes'
4611             }
4612         ],
4613
4614         /**
4615          * Button config that displays OK and Cancel buttons
4616          * @type Object
4617          */
4618         OKCANCEL : [
4619             {
4620                name : 'cancel',
4621                 html : 'Cancel'
4622             },
4623             {
4624                 name : 'ok',
4625                 weight : 'primary',
4626                 html : 'OK'
4627             }
4628         ],
4629         /**
4630          * Button config that displays Yes, No and Cancel buttons
4631          * @type Object
4632          */
4633         YESNOCANCEL : [
4634             {
4635                 name : 'yes',
4636                 weight : 'primary',
4637                 html : 'Yes'
4638             },
4639             {
4640                 name : 'no',
4641                 html : 'No'
4642             },
4643             {
4644                 name : 'cancel',
4645                 html : 'Cancel'
4646             }
4647         ],
4648         
4649         zIndex : 10001
4650 });
4651
4652 /*
4653  * - LGPL
4654  *
4655  * messagebox - can be used as a replace
4656  * 
4657  */
4658 /**
4659  * @class Roo.MessageBox
4660  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4661  * Example usage:
4662  *<pre><code>
4663 // Basic alert:
4664 Roo.Msg.alert('Status', 'Changes saved successfully.');
4665
4666 // Prompt for user data:
4667 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4668     if (btn == 'ok'){
4669         // process text value...
4670     }
4671 });
4672
4673 // Show a dialog using config options:
4674 Roo.Msg.show({
4675    title:'Save Changes?',
4676    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4677    buttons: Roo.Msg.YESNOCANCEL,
4678    fn: processResult,
4679    animEl: 'elId'
4680 });
4681 </code></pre>
4682  * @singleton
4683  */
4684 Roo.bootstrap.MessageBox = function(){
4685     var dlg, opt, mask, waitTimer;
4686     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4687     var buttons, activeTextEl, bwidth;
4688
4689     
4690     // private
4691     var handleButton = function(button){
4692         dlg.hide();
4693         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4694     };
4695
4696     // private
4697     var handleHide = function(){
4698         if(opt && opt.cls){
4699             dlg.el.removeClass(opt.cls);
4700         }
4701         //if(waitTimer){
4702         //    Roo.TaskMgr.stop(waitTimer);
4703         //    waitTimer = null;
4704         //}
4705     };
4706
4707     // private
4708     var updateButtons = function(b){
4709         var width = 0;
4710         if(!b){
4711             buttons["ok"].hide();
4712             buttons["cancel"].hide();
4713             buttons["yes"].hide();
4714             buttons["no"].hide();
4715             dlg.footerEl.hide();
4716             
4717             return width;
4718         }
4719         dlg.footerEl.show();
4720         for(var k in buttons){
4721             if(typeof buttons[k] != "function"){
4722                 if(b[k]){
4723                     buttons[k].show();
4724                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4725                     width += buttons[k].el.getWidth()+15;
4726                 }else{
4727                     buttons[k].hide();
4728                 }
4729             }
4730         }
4731         return width;
4732     };
4733
4734     // private
4735     var handleEsc = function(d, k, e){
4736         if(opt && opt.closable !== false){
4737             dlg.hide();
4738         }
4739         if(e){
4740             e.stopEvent();
4741         }
4742     };
4743
4744     return {
4745         /**
4746          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4747          * @return {Roo.BasicDialog} The BasicDialog element
4748          */
4749         getDialog : function(){
4750            if(!dlg){
4751                 dlg = new Roo.bootstrap.Modal( {
4752                     //draggable: true,
4753                     //resizable:false,
4754                     //constraintoviewport:false,
4755                     //fixedcenter:true,
4756                     //collapsible : false,
4757                     //shim:true,
4758                     //modal: true,
4759                 //    width: 'auto',
4760                   //  height:100,
4761                     //buttonAlign:"center",
4762                     closeClick : function(){
4763                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4764                             handleButton("no");
4765                         }else{
4766                             handleButton("cancel");
4767                         }
4768                     }
4769                 });
4770                 dlg.render();
4771                 dlg.on("hide", handleHide);
4772                 mask = dlg.mask;
4773                 //dlg.addKeyListener(27, handleEsc);
4774                 buttons = {};
4775                 this.buttons = buttons;
4776                 var bt = this.buttonText;
4777                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4778                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4779                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4780                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4781                 //Roo.log(buttons);
4782                 bodyEl = dlg.bodyEl.createChild({
4783
4784                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4785                         '<textarea class="roo-mb-textarea"></textarea>' +
4786                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4787                 });
4788                 msgEl = bodyEl.dom.firstChild;
4789                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4790                 textboxEl.enableDisplayMode();
4791                 textboxEl.addKeyListener([10,13], function(){
4792                     if(dlg.isVisible() && opt && opt.buttons){
4793                         if(opt.buttons.ok){
4794                             handleButton("ok");
4795                         }else if(opt.buttons.yes){
4796                             handleButton("yes");
4797                         }
4798                     }
4799                 });
4800                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4801                 textareaEl.enableDisplayMode();
4802                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4803                 progressEl.enableDisplayMode();
4804                 
4805                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4806                 var pf = progressEl.dom.firstChild;
4807                 if (pf) {
4808                     pp = Roo.get(pf.firstChild);
4809                     pp.setHeight(pf.offsetHeight);
4810                 }
4811                 
4812             }
4813             return dlg;
4814         },
4815
4816         /**
4817          * Updates the message box body text
4818          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4819          * the XHTML-compliant non-breaking space character '&amp;#160;')
4820          * @return {Roo.MessageBox} This message box
4821          */
4822         updateText : function(text)
4823         {
4824             if(!dlg.isVisible() && !opt.width){
4825                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4826                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4827             }
4828             msgEl.innerHTML = text || '&#160;';
4829       
4830             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4831             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4832             var w = Math.max(
4833                     Math.min(opt.width || cw , this.maxWidth), 
4834                     Math.max(opt.minWidth || this.minWidth, bwidth)
4835             );
4836             if(opt.prompt){
4837                 activeTextEl.setWidth(w);
4838             }
4839             if(dlg.isVisible()){
4840                 dlg.fixedcenter = false;
4841             }
4842             // to big, make it scroll. = But as usual stupid IE does not support
4843             // !important..
4844             
4845             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4846                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4847                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4848             } else {
4849                 bodyEl.dom.style.height = '';
4850                 bodyEl.dom.style.overflowY = '';
4851             }
4852             if (cw > w) {
4853                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4854             } else {
4855                 bodyEl.dom.style.overflowX = '';
4856             }
4857             
4858             dlg.setContentSize(w, bodyEl.getHeight());
4859             if(dlg.isVisible()){
4860                 dlg.fixedcenter = true;
4861             }
4862             return this;
4863         },
4864
4865         /**
4866          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4867          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4868          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4869          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4870          * @return {Roo.MessageBox} This message box
4871          */
4872         updateProgress : function(value, text){
4873             if(text){
4874                 this.updateText(text);
4875             }
4876             
4877             if (pp) { // weird bug on my firefox - for some reason this is not defined
4878                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4879                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4880             }
4881             return this;
4882         },        
4883
4884         /**
4885          * Returns true if the message box is currently displayed
4886          * @return {Boolean} True if the message box is visible, else false
4887          */
4888         isVisible : function(){
4889             return dlg && dlg.isVisible();  
4890         },
4891
4892         /**
4893          * Hides the message box if it is displayed
4894          */
4895         hide : function(){
4896             if(this.isVisible()){
4897                 dlg.hide();
4898             }  
4899         },
4900
4901         /**
4902          * Displays a new message box, or reinitializes an existing message box, based on the config options
4903          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4904          * The following config object properties are supported:
4905          * <pre>
4906 Property    Type             Description
4907 ----------  ---------------  ------------------------------------------------------------------------------------
4908 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4909                                    closes (defaults to undefined)
4910 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4911                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4912 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4913                                    progress and wait dialogs will ignore this property and always hide the
4914                                    close button as they can only be closed programmatically.
4915 cls               String           A custom CSS class to apply to the message box element
4916 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4917                                    displayed (defaults to 75)
4918 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4919                                    function will be btn (the name of the button that was clicked, if applicable,
4920                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4921                                    Progress and wait dialogs will ignore this option since they do not respond to
4922                                    user actions and can only be closed programmatically, so any required function
4923                                    should be called by the same code after it closes the dialog.
4924 icon              String           A CSS class that provides a background image to be used as an icon for
4925                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4926 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4927 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4928 modal             Boolean          False to allow user interaction with the page while the message box is
4929                                    displayed (defaults to true)
4930 msg               String           A string that will replace the existing message box body text (defaults
4931                                    to the XHTML-compliant non-breaking space character '&#160;')
4932 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4933 progress          Boolean          True to display a progress bar (defaults to false)
4934 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4935 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4936 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4937 title             String           The title text
4938 value             String           The string value to set into the active textbox element if displayed
4939 wait              Boolean          True to display a progress bar (defaults to false)
4940 width             Number           The width of the dialog in pixels
4941 </pre>
4942          *
4943          * Example usage:
4944          * <pre><code>
4945 Roo.Msg.show({
4946    title: 'Address',
4947    msg: 'Please enter your address:',
4948    width: 300,
4949    buttons: Roo.MessageBox.OKCANCEL,
4950    multiline: true,
4951    fn: saveAddress,
4952    animEl: 'addAddressBtn'
4953 });
4954 </code></pre>
4955          * @param {Object} config Configuration options
4956          * @return {Roo.MessageBox} This message box
4957          */
4958         show : function(options)
4959         {
4960             
4961             // this causes nightmares if you show one dialog after another
4962             // especially on callbacks..
4963              
4964             if(this.isVisible()){
4965                 
4966                 this.hide();
4967                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4968                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4969                 Roo.log("New Dialog Message:" +  options.msg )
4970                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4971                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4972                 
4973             }
4974             var d = this.getDialog();
4975             opt = options;
4976             d.setTitle(opt.title || "&#160;");
4977             d.closeEl.setDisplayed(opt.closable !== false);
4978             activeTextEl = textboxEl;
4979             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4980             if(opt.prompt){
4981                 if(opt.multiline){
4982                     textboxEl.hide();
4983                     textareaEl.show();
4984                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4985                         opt.multiline : this.defaultTextHeight);
4986                     activeTextEl = textareaEl;
4987                 }else{
4988                     textboxEl.show();
4989                     textareaEl.hide();
4990                 }
4991             }else{
4992                 textboxEl.hide();
4993                 textareaEl.hide();
4994             }
4995             progressEl.setDisplayed(opt.progress === true);
4996             if (opt.progress) {
4997                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4998             }
4999             this.updateProgress(0);
5000             activeTextEl.dom.value = opt.value || "";
5001             if(opt.prompt){
5002                 dlg.setDefaultButton(activeTextEl);
5003             }else{
5004                 var bs = opt.buttons;
5005                 var db = null;
5006                 if(bs && bs.ok){
5007                     db = buttons["ok"];
5008                 }else if(bs && bs.yes){
5009                     db = buttons["yes"];
5010                 }
5011                 dlg.setDefaultButton(db);
5012             }
5013             bwidth = updateButtons(opt.buttons);
5014             this.updateText(opt.msg);
5015             if(opt.cls){
5016                 d.el.addClass(opt.cls);
5017             }
5018             d.proxyDrag = opt.proxyDrag === true;
5019             d.modal = opt.modal !== false;
5020             d.mask = opt.modal !== false ? mask : false;
5021             if(!d.isVisible()){
5022                 // force it to the end of the z-index stack so it gets a cursor in FF
5023                 document.body.appendChild(dlg.el.dom);
5024                 d.animateTarget = null;
5025                 d.show(options.animEl);
5026             }
5027             return this;
5028         },
5029
5030         /**
5031          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5032          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5033          * and closing the message box when the process is complete.
5034          * @param {String} title The title bar text
5035          * @param {String} msg The message box body text
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         progress : function(title, msg){
5039             this.show({
5040                 title : title,
5041                 msg : msg,
5042                 buttons: false,
5043                 progress:true,
5044                 closable:false,
5045                 minWidth: this.minProgressWidth,
5046                 modal : true
5047             });
5048             return this;
5049         },
5050
5051         /**
5052          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5053          * If a callback function is passed it will be called after the user clicks the button, and the
5054          * id of the button that was clicked will be passed as the only parameter to the callback
5055          * (could also be the top-right close button).
5056          * @param {String} title The title bar text
5057          * @param {String} msg The message box body text
5058          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5059          * @param {Object} scope (optional) The scope of the callback function
5060          * @return {Roo.MessageBox} This message box
5061          */
5062         alert : function(title, msg, fn, scope)
5063         {
5064             this.show({
5065                 title : title,
5066                 msg : msg,
5067                 buttons: this.OK,
5068                 fn: fn,
5069                 closable : false,
5070                 scope : scope,
5071                 modal : true
5072             });
5073             return this;
5074         },
5075
5076         /**
5077          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5078          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5079          * You are responsible for closing the message box when the process is complete.
5080          * @param {String} msg The message box body text
5081          * @param {String} title (optional) The title bar text
5082          * @return {Roo.MessageBox} This message box
5083          */
5084         wait : function(msg, title){
5085             this.show({
5086                 title : title,
5087                 msg : msg,
5088                 buttons: false,
5089                 closable:false,
5090                 progress:true,
5091                 modal:true,
5092                 width:300,
5093                 wait:true
5094             });
5095             waitTimer = Roo.TaskMgr.start({
5096                 run: function(i){
5097                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5098                 },
5099                 interval: 1000
5100             });
5101             return this;
5102         },
5103
5104         /**
5105          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5106          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5107          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @return {Roo.MessageBox} This message box
5113          */
5114         confirm : function(title, msg, fn, scope){
5115             this.show({
5116                 title : title,
5117                 msg : msg,
5118                 buttons: this.YESNO,
5119                 fn: fn,
5120                 scope : scope,
5121                 modal : true
5122             });
5123             return this;
5124         },
5125
5126         /**
5127          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5128          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5129          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5130          * (could also be the top-right close button) and the text that was entered will be passed as the two
5131          * parameters to the callback.
5132          * @param {String} title The title bar text
5133          * @param {String} msg The message box body text
5134          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5135          * @param {Object} scope (optional) The scope of the callback function
5136          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5137          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5138          * @return {Roo.MessageBox} This message box
5139          */
5140         prompt : function(title, msg, fn, scope, multiline){
5141             this.show({
5142                 title : title,
5143                 msg : msg,
5144                 buttons: this.OKCANCEL,
5145                 fn: fn,
5146                 minWidth:250,
5147                 scope : scope,
5148                 prompt:true,
5149                 multiline: multiline,
5150                 modal : true
5151             });
5152             return this;
5153         },
5154
5155         /**
5156          * Button config that displays a single OK button
5157          * @type Object
5158          */
5159         OK : {ok:true},
5160         /**
5161          * Button config that displays Yes and No buttons
5162          * @type Object
5163          */
5164         YESNO : {yes:true, no:true},
5165         /**
5166          * Button config that displays OK and Cancel buttons
5167          * @type Object
5168          */
5169         OKCANCEL : {ok:true, cancel:true},
5170         /**
5171          * Button config that displays Yes, No and Cancel buttons
5172          * @type Object
5173          */
5174         YESNOCANCEL : {yes:true, no:true, cancel:true},
5175
5176         /**
5177          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5178          * @type Number
5179          */
5180         defaultTextHeight : 75,
5181         /**
5182          * The maximum width in pixels of the message box (defaults to 600)
5183          * @type Number
5184          */
5185         maxWidth : 600,
5186         /**
5187          * The minimum width in pixels of the message box (defaults to 100)
5188          * @type Number
5189          */
5190         minWidth : 100,
5191         /**
5192          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5193          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5194          * @type Number
5195          */
5196         minProgressWidth : 250,
5197         /**
5198          * An object containing the default button text strings that can be overriden for localized language support.
5199          * Supported properties are: ok, cancel, yes and no.
5200          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5201          * @type Object
5202          */
5203         buttonText : {
5204             ok : "OK",
5205             cancel : "Cancel",
5206             yes : "Yes",
5207             no : "No"
5208         }
5209     };
5210 }();
5211
5212 /**
5213  * Shorthand for {@link Roo.MessageBox}
5214  */
5215 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5216 Roo.Msg = Roo.Msg || Roo.MessageBox;
5217 /*
5218  * - LGPL
5219  *
5220  * navbar
5221  * 
5222  */
5223
5224 /**
5225  * @class Roo.bootstrap.Navbar
5226  * @extends Roo.bootstrap.Component
5227  * Bootstrap Navbar class
5228
5229  * @constructor
5230  * Create a new Navbar
5231  * @param {Object} config The config object
5232  */
5233
5234
5235 Roo.bootstrap.Navbar = function(config){
5236     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5237     this.addEvents({
5238         // raw events
5239         /**
5240          * @event beforetoggle
5241          * Fire before toggle the menu
5242          * @param {Roo.EventObject} e
5243          */
5244         "beforetoggle" : true
5245     });
5246 };
5247
5248 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5249     
5250     
5251    
5252     // private
5253     navItems : false,
5254     loadMask : false,
5255     
5256     
5257     getAutoCreate : function(){
5258         
5259         
5260         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5261         
5262     },
5263     
5264     initEvents :function ()
5265     {
5266         //Roo.log(this.el.select('.navbar-toggle',true));
5267         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5268         
5269         var mark = {
5270             tag: "div",
5271             cls:"x-dlg-mask"
5272         };
5273         
5274         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5275         
5276         var size = this.el.getSize();
5277         this.maskEl.setSize(size.width, size.height);
5278         this.maskEl.enableDisplayMode("block");
5279         this.maskEl.hide();
5280         
5281         if(this.loadMask){
5282             this.maskEl.show();
5283         }
5284     },
5285     
5286     
5287     getChildContainer : function()
5288     {
5289         if (this.el && this.el.select('.collapse').getCount()) {
5290             return this.el.select('.collapse',true).first();
5291         }
5292         
5293         return this.el;
5294     },
5295     
5296     mask : function()
5297     {
5298         this.maskEl.show();
5299     },
5300     
5301     unmask : function()
5302     {
5303         this.maskEl.hide();
5304     },
5305     onToggle : function()
5306     {
5307         
5308         if(this.fireEvent('beforetoggle', this) === false){
5309             return;
5310         }
5311         var ce = this.el.select('.navbar-collapse',true).first();
5312       
5313         if (!ce.hasClass('show')) {
5314            this.expand();
5315         } else {
5316             this.collapse();
5317         }
5318         
5319         
5320     
5321     },
5322     /**
5323      * Expand the navbar pulldown 
5324      */
5325     expand : function ()
5326     {
5327        
5328         var ce = this.el.select('.navbar-collapse',true).first();
5329         if (ce.hasClass('collapsing')) {
5330             return;
5331         }
5332         ce.dom.style.height = '';
5333                // show it...
5334         ce.addClass('in'); // old...
5335         ce.removeClass('collapse');
5336         ce.addClass('show');
5337         var h = ce.getHeight();
5338         Roo.log(h);
5339         ce.removeClass('show');
5340         // at this point we should be able to see it..
5341         ce.addClass('collapsing');
5342         
5343         ce.setHeight(0); // resize it ...
5344         ce.on('transitionend', function() {
5345             //Roo.log('done transition');
5346             ce.removeClass('collapsing');
5347             ce.addClass('show');
5348             ce.removeClass('collapse');
5349
5350             ce.dom.style.height = '';
5351         }, this, { single: true} );
5352         ce.setHeight(h);
5353         ce.dom.scrollTop = 0;
5354     },
5355     /**
5356      * Collapse the navbar pulldown 
5357      */
5358     collapse : function()
5359     {
5360          var ce = this.el.select('.navbar-collapse',true).first();
5361        
5362         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5363             // it's collapsed or collapsing..
5364             return;
5365         }
5366         ce.removeClass('in'); // old...
5367         ce.setHeight(ce.getHeight());
5368         ce.removeClass('show');
5369         ce.addClass('collapsing');
5370         
5371         ce.on('transitionend', function() {
5372             ce.dom.style.height = '';
5373             ce.removeClass('collapsing');
5374             ce.addClass('collapse');
5375         }, this, { single: true} );
5376         ce.setHeight(0);
5377     }
5378     
5379     
5380     
5381 });
5382
5383
5384
5385  
5386
5387  /*
5388  * - LGPL
5389  *
5390  * navbar
5391  * 
5392  */
5393
5394 /**
5395  * @class Roo.bootstrap.NavSimplebar
5396  * @extends Roo.bootstrap.Navbar
5397  * Bootstrap Sidebar class
5398  *
5399  * @cfg {Boolean} inverse is inverted color
5400  * 
5401  * @cfg {String} type (nav | pills | tabs)
5402  * @cfg {Boolean} arrangement stacked | justified
5403  * @cfg {String} align (left | right) alignment
5404  * 
5405  * @cfg {Boolean} main (true|false) main nav bar? default false
5406  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5407  * 
5408  * @cfg {String} tag (header|footer|nav|div) default is nav 
5409
5410  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5411  * 
5412  * 
5413  * @constructor
5414  * Create a new Sidebar
5415  * @param {Object} config The config object
5416  */
5417
5418
5419 Roo.bootstrap.NavSimplebar = function(config){
5420     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5421 };
5422
5423 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5424     
5425     inverse: false,
5426     
5427     type: false,
5428     arrangement: '',
5429     align : false,
5430     
5431     weight : 'light',
5432     
5433     main : false,
5434     
5435     
5436     tag : false,
5437     
5438     
5439     getAutoCreate : function(){
5440         
5441         
5442         var cfg = {
5443             tag : this.tag || 'div',
5444             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5445         };
5446         if (['light','white'].indexOf(this.weight) > -1) {
5447             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5448         }
5449         cfg.cls += ' bg-' + this.weight;
5450         
5451         if (this.inverse) {
5452             cfg.cls += ' navbar-inverse';
5453             
5454         }
5455         
5456         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5457         
5458         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5459             return cfg;
5460         }
5461         
5462         
5463     
5464         
5465         cfg.cn = [
5466             {
5467                 cls: 'nav nav-' + this.xtype,
5468                 tag : 'ul'
5469             }
5470         ];
5471         
5472          
5473         this.type = this.type || 'nav';
5474         if (['tabs','pills'].indexOf(this.type) != -1) {
5475             cfg.cn[0].cls += ' nav-' + this.type
5476         
5477         
5478         } else {
5479             if (this.type!=='nav') {
5480                 Roo.log('nav type must be nav/tabs/pills')
5481             }
5482             cfg.cn[0].cls += ' navbar-nav'
5483         }
5484         
5485         
5486         
5487         
5488         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5489             cfg.cn[0].cls += ' nav-' + this.arrangement;
5490         }
5491         
5492         
5493         if (this.align === 'right') {
5494             cfg.cn[0].cls += ' navbar-right';
5495         }
5496         
5497         
5498         
5499         
5500         return cfg;
5501     
5502         
5503     }
5504     
5505     
5506     
5507 });
5508
5509
5510
5511  
5512
5513  
5514        /*
5515  * - LGPL
5516  *
5517  * navbar
5518  * navbar-fixed-top
5519  * navbar-expand-md  fixed-top 
5520  */
5521
5522 /**
5523  * @class Roo.bootstrap.NavHeaderbar
5524  * @extends Roo.bootstrap.NavSimplebar
5525  * Bootstrap Sidebar class
5526  *
5527  * @cfg {String} brand what is brand
5528  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5529  * @cfg {String} brand_href href of the brand
5530  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5531  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5532  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5533  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5534  * 
5535  * @constructor
5536  * Create a new Sidebar
5537  * @param {Object} config The config object
5538  */
5539
5540
5541 Roo.bootstrap.NavHeaderbar = function(config){
5542     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5543       
5544 };
5545
5546 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5547     
5548     position: '',
5549     brand: '',
5550     brand_href: false,
5551     srButton : true,
5552     autohide : false,
5553     desktopCenter : false,
5554    
5555     
5556     getAutoCreate : function(){
5557         
5558         var   cfg = {
5559             tag: this.nav || 'nav',
5560             cls: 'navbar navbar-expand-md',
5561             role: 'navigation',
5562             cn: []
5563         };
5564         
5565         var cn = cfg.cn;
5566         if (this.desktopCenter) {
5567             cn.push({cls : 'container', cn : []});
5568             cn = cn[0].cn;
5569         }
5570         
5571         if(this.srButton){
5572             var btn = {
5573                 tag: 'button',
5574                 type: 'button',
5575                 cls: 'navbar-toggle navbar-toggler',
5576                 'data-toggle': 'collapse',
5577                 cn: [
5578                     {
5579                         tag: 'span',
5580                         cls: 'sr-only',
5581                         html: 'Toggle navigation'
5582                     },
5583                     {
5584                         tag: 'span',
5585                         cls: 'icon-bar navbar-toggler-icon'
5586                     },
5587                     {
5588                         tag: 'span',
5589                         cls: 'icon-bar'
5590                     },
5591                     {
5592                         tag: 'span',
5593                         cls: 'icon-bar'
5594                     }
5595                 ]
5596             };
5597             
5598             cn.push( Roo.bootstrap.version == 4 ? btn : {
5599                 tag: 'div',
5600                 cls: 'navbar-header',
5601                 cn: [
5602                     btn
5603                 ]
5604             });
5605         }
5606         
5607         cn.push({
5608             tag: 'div',
5609             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5610             cn : []
5611         });
5612         
5613         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5614         
5615         if (['light','white'].indexOf(this.weight) > -1) {
5616             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5617         }
5618         cfg.cls += ' bg-' + this.weight;
5619         
5620         
5621         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5622             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5623             
5624             // tag can override this..
5625             
5626             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5627         }
5628         
5629         if (this.brand !== '') {
5630             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5631             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5632                 tag: 'a',
5633                 href: this.brand_href ? this.brand_href : '#',
5634                 cls: 'navbar-brand',
5635                 cn: [
5636                 this.brand
5637                 ]
5638             });
5639         }
5640         
5641         if(this.main){
5642             cfg.cls += ' main-nav';
5643         }
5644         
5645         
5646         return cfg;
5647
5648         
5649     },
5650     getHeaderChildContainer : function()
5651     {
5652         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5653             return this.el.select('.navbar-header',true).first();
5654         }
5655         
5656         return this.getChildContainer();
5657     },
5658     
5659     getChildContainer : function()
5660     {
5661          
5662         return this.el.select('.roo-navbar-collapse',true).first();
5663          
5664         
5665     },
5666     
5667     initEvents : function()
5668     {
5669         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5670         
5671         if (this.autohide) {
5672             
5673             var prevScroll = 0;
5674             var ft = this.el;
5675             
5676             Roo.get(document).on('scroll',function(e) {
5677                 var ns = Roo.get(document).getScroll().top;
5678                 var os = prevScroll;
5679                 prevScroll = ns;
5680                 
5681                 if(ns > os){
5682                     ft.removeClass('slideDown');
5683                     ft.addClass('slideUp');
5684                     return;
5685                 }
5686                 ft.removeClass('slideUp');
5687                 ft.addClass('slideDown');
5688                  
5689               
5690           },this);
5691         }
5692     }    
5693     
5694 });
5695
5696
5697
5698  
5699
5700  /*
5701  * - LGPL
5702  *
5703  * navbar
5704  * 
5705  */
5706
5707 /**
5708  * @class Roo.bootstrap.NavSidebar
5709  * @extends Roo.bootstrap.Navbar
5710  * Bootstrap Sidebar class
5711  * 
5712  * @constructor
5713  * Create a new Sidebar
5714  * @param {Object} config The config object
5715  */
5716
5717
5718 Roo.bootstrap.NavSidebar = function(config){
5719     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5720 };
5721
5722 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5723     
5724     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5725     
5726     getAutoCreate : function(){
5727         
5728         
5729         return  {
5730             tag: 'div',
5731             cls: 'sidebar sidebar-nav'
5732         };
5733     
5734         
5735     }
5736     
5737     
5738     
5739 });
5740
5741
5742
5743  
5744
5745  /*
5746  * - LGPL
5747  *
5748  * nav group
5749  * 
5750  */
5751
5752 /**
5753  * @class Roo.bootstrap.NavGroup
5754  * @extends Roo.bootstrap.Component
5755  * Bootstrap NavGroup class
5756  * @cfg {String} align (left|right)
5757  * @cfg {Boolean} inverse
5758  * @cfg {String} type (nav|pills|tab) default nav
5759  * @cfg {String} navId - reference Id for navbar.
5760  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5761  * 
5762  * @constructor
5763  * Create a new nav group
5764  * @param {Object} config The config object
5765  */
5766
5767 Roo.bootstrap.NavGroup = function(config){
5768     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5769     this.navItems = [];
5770    
5771     Roo.bootstrap.NavGroup.register(this);
5772      this.addEvents({
5773         /**
5774              * @event changed
5775              * Fires when the active item changes
5776              * @param {Roo.bootstrap.NavGroup} this
5777              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5778              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5779          */
5780         'changed': true
5781      });
5782     
5783 };
5784
5785 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5786     
5787     align: '',
5788     inverse: false,
5789     form: false,
5790     type: 'nav',
5791     navId : '',
5792     // private
5793     pilltype : true,
5794     
5795     navItems : false, 
5796     
5797     getAutoCreate : function()
5798     {
5799         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5800         
5801         cfg = {
5802             tag : 'ul',
5803             cls: 'nav' 
5804         };
5805         if (Roo.bootstrap.version == 4) {
5806             if (['tabs','pills'].indexOf(this.type) != -1) {
5807                 cfg.cls += ' nav-' + this.type; 
5808             } else {
5809                 // trying to remove so header bar can right align top?
5810                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5811                     // do not use on header bar... 
5812                     cfg.cls += ' navbar-nav';
5813                 }
5814             }
5815             
5816         } else {
5817             if (['tabs','pills'].indexOf(this.type) != -1) {
5818                 cfg.cls += ' nav-' + this.type
5819             } else {
5820                 if (this.type !== 'nav') {
5821                     Roo.log('nav type must be nav/tabs/pills')
5822                 }
5823                 cfg.cls += ' navbar-nav'
5824             }
5825         }
5826         
5827         if (this.parent() && this.parent().sidebar) {
5828             cfg = {
5829                 tag: 'ul',
5830                 cls: 'dashboard-menu sidebar-menu'
5831             };
5832             
5833             return cfg;
5834         }
5835         
5836         if (this.form === true) {
5837             cfg = {
5838                 tag: 'form',
5839                 cls: 'navbar-form form-inline'
5840             };
5841             //nav navbar-right ml-md-auto
5842             if (this.align === 'right') {
5843                 cfg.cls += ' navbar-right ml-md-auto';
5844             } else {
5845                 cfg.cls += ' navbar-left';
5846             }
5847         }
5848         
5849         if (this.align === 'right') {
5850             cfg.cls += ' navbar-right ml-md-auto';
5851         } else {
5852             cfg.cls += ' mr-auto';
5853         }
5854         
5855         if (this.inverse) {
5856             cfg.cls += ' navbar-inverse';
5857             
5858         }
5859         
5860         
5861         return cfg;
5862     },
5863     /**
5864     * sets the active Navigation item
5865     * @param {Roo.bootstrap.NavItem} the new current navitem
5866     */
5867     setActiveItem : function(item)
5868     {
5869         var prev = false;
5870         Roo.each(this.navItems, function(v){
5871             if (v == item) {
5872                 return ;
5873             }
5874             if (v.isActive()) {
5875                 v.setActive(false, true);
5876                 prev = v;
5877                 
5878             }
5879             
5880         });
5881
5882         item.setActive(true, true);
5883         this.fireEvent('changed', this, item, prev);
5884         
5885         
5886     },
5887     /**
5888     * gets the active Navigation item
5889     * @return {Roo.bootstrap.NavItem} the current navitem
5890     */
5891     getActive : function()
5892     {
5893         
5894         var prev = false;
5895         Roo.each(this.navItems, function(v){
5896             
5897             if (v.isActive()) {
5898                 prev = v;
5899                 
5900             }
5901             
5902         });
5903         return prev;
5904     },
5905     
5906     indexOfNav : function()
5907     {
5908         
5909         var prev = false;
5910         Roo.each(this.navItems, function(v,i){
5911             
5912             if (v.isActive()) {
5913                 prev = i;
5914                 
5915             }
5916             
5917         });
5918         return prev;
5919     },
5920     /**
5921     * adds a Navigation item
5922     * @param {Roo.bootstrap.NavItem} the navitem to add
5923     */
5924     addItem : function(cfg)
5925     {
5926         if (this.form && Roo.bootstrap.version == 4) {
5927             cfg.tag = 'div';
5928         }
5929         var cn = new Roo.bootstrap.NavItem(cfg);
5930         this.register(cn);
5931         cn.parentId = this.id;
5932         cn.onRender(this.el, null);
5933         return cn;
5934     },
5935     /**
5936     * register a Navigation item
5937     * @param {Roo.bootstrap.NavItem} the navitem to add
5938     */
5939     register : function(item)
5940     {
5941         this.navItems.push( item);
5942         item.navId = this.navId;
5943     
5944     },
5945     
5946     /**
5947     * clear all the Navigation item
5948     */
5949    
5950     clearAll : function()
5951     {
5952         this.navItems = [];
5953         this.el.dom.innerHTML = '';
5954     },
5955     
5956     getNavItem: function(tabId)
5957     {
5958         var ret = false;
5959         Roo.each(this.navItems, function(e) {
5960             if (e.tabId == tabId) {
5961                ret =  e;
5962                return false;
5963             }
5964             return true;
5965             
5966         });
5967         return ret;
5968     },
5969     
5970     setActiveNext : function()
5971     {
5972         var i = this.indexOfNav(this.getActive());
5973         if (i > this.navItems.length) {
5974             return;
5975         }
5976         this.setActiveItem(this.navItems[i+1]);
5977     },
5978     setActivePrev : function()
5979     {
5980         var i = this.indexOfNav(this.getActive());
5981         if (i  < 1) {
5982             return;
5983         }
5984         this.setActiveItem(this.navItems[i-1]);
5985     },
5986     clearWasActive : function(except) {
5987         Roo.each(this.navItems, function(e) {
5988             if (e.tabId != except.tabId && e.was_active) {
5989                e.was_active = false;
5990                return false;
5991             }
5992             return true;
5993             
5994         });
5995     },
5996     getWasActive : function ()
5997     {
5998         var r = false;
5999         Roo.each(this.navItems, function(e) {
6000             if (e.was_active) {
6001                r = e;
6002                return false;
6003             }
6004             return true;
6005             
6006         });
6007         return r;
6008     }
6009     
6010     
6011 });
6012
6013  
6014 Roo.apply(Roo.bootstrap.NavGroup, {
6015     
6016     groups: {},
6017      /**
6018     * register a Navigation Group
6019     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6020     */
6021     register : function(navgrp)
6022     {
6023         this.groups[navgrp.navId] = navgrp;
6024         
6025     },
6026     /**
6027     * fetch a Navigation Group based on the navigation ID
6028     * @param {string} the navgroup to add
6029     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6030     */
6031     get: function(navId) {
6032         if (typeof(this.groups[navId]) == 'undefined') {
6033             return false;
6034             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6035         }
6036         return this.groups[navId] ;
6037     }
6038     
6039     
6040     
6041 });
6042
6043  /*
6044  * - LGPL
6045  *
6046  * row
6047  * 
6048  */
6049
6050 /**
6051  * @class Roo.bootstrap.NavItem
6052  * @extends Roo.bootstrap.Component
6053  * Bootstrap Navbar.NavItem class
6054  * @cfg {String} href  link to
6055  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6056  * @cfg {Boolean} button_outline show and outlined button
6057  * @cfg {String} html content of button
6058  * @cfg {String} badge text inside badge
6059  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6060  * @cfg {String} glyphicon DEPRICATED - use fa
6061  * @cfg {String} icon DEPRICATED - use fa
6062  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6063  * @cfg {Boolean} active Is item active
6064  * @cfg {Boolean} disabled Is item disabled
6065  * @cfg {String} linkcls  Link Class
6066  * @cfg {Boolean} preventDefault (true | false) default false
6067  * @cfg {String} tabId the tab that this item activates.
6068  * @cfg {String} tagtype (a|span) render as a href or span?
6069  * @cfg {Boolean} animateRef (true|false) link to element default false  
6070   
6071  * @constructor
6072  * Create a new Navbar Item
6073  * @param {Object} config The config object
6074  */
6075 Roo.bootstrap.NavItem = function(config){
6076     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6077     this.addEvents({
6078         // raw events
6079         /**
6080          * @event click
6081          * The raw click event for the entire grid.
6082          * @param {Roo.EventObject} e
6083          */
6084         "click" : true,
6085          /**
6086             * @event changed
6087             * Fires when the active item active state changes
6088             * @param {Roo.bootstrap.NavItem} this
6089             * @param {boolean} state the new state
6090              
6091          */
6092         'changed': true,
6093         /**
6094             * @event scrollto
6095             * Fires when scroll to element
6096             * @param {Roo.bootstrap.NavItem} this
6097             * @param {Object} options
6098             * @param {Roo.EventObject} e
6099              
6100          */
6101         'scrollto': true
6102     });
6103    
6104 };
6105
6106 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6107     
6108     href: false,
6109     html: '',
6110     badge: '',
6111     icon: false,
6112     fa : false,
6113     glyphicon: false,
6114     active: false,
6115     preventDefault : false,
6116     tabId : false,
6117     tagtype : 'a',
6118     tag: 'li',
6119     disabled : false,
6120     animateRef : false,
6121     was_active : false,
6122     button_weight : '',
6123     button_outline : false,
6124     linkcls : '',
6125     navLink: false,
6126     
6127     getAutoCreate : function(){
6128          
6129         var cfg = {
6130             tag: this.tag,
6131             cls: 'nav-item'
6132         };
6133         
6134         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6135         
6136         if (this.active) {
6137             cfg.cls +=  ' active' ;
6138         }
6139         if (this.disabled) {
6140             cfg.cls += ' disabled';
6141         }
6142         
6143         // BS4 only?
6144         if (this.button_weight.length) {
6145             cfg.tag = this.href ? 'a' : 'button';
6146             cfg.html = this.html || '';
6147             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6148             if (this.href) {
6149                 cfg.href = this.href;
6150             }
6151             if (this.fa) {
6152                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6153             }
6154             
6155             // menu .. should add dropdown-menu class - so no need for carat..
6156             
6157             if (this.badge !== '') {
6158                  
6159                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6160             }
6161             return cfg;
6162         }
6163         
6164         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6165             cfg.cn = [
6166                 {
6167                     tag: this.tagtype,
6168                     href : this.href || "#",
6169                     html: this.html || ''
6170                 }
6171             ];
6172             if (this.tagtype == 'a') {
6173                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6174         
6175             }
6176             if (this.icon) {
6177                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6178             }
6179             if (this.fa) {
6180                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6181             }
6182             if(this.glyphicon) {
6183                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6184             }
6185             
6186             if (this.menu) {
6187                 
6188                 cfg.cn[0].html += " <span class='caret'></span>";
6189              
6190             }
6191             
6192             if (this.badge !== '') {
6193                  
6194                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6195             }
6196         }
6197         
6198         
6199         
6200         return cfg;
6201     },
6202     onRender : function(ct, position)
6203     {
6204        // Roo.log("Call onRender: " + this.xtype);
6205         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6206             this.tag = 'div';
6207         }
6208         
6209         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6210         this.navLink = this.el.select('.nav-link',true).first();
6211         return ret;
6212     },
6213       
6214     
6215     initEvents: function() 
6216     {
6217         if (typeof (this.menu) != 'undefined') {
6218             this.menu.parentType = this.xtype;
6219             this.menu.triggerEl = this.el;
6220             this.menu = this.addxtype(Roo.apply({}, this.menu));
6221         }
6222         
6223         this.el.on('click', this.onClick, this);
6224         
6225         //if(this.tagtype == 'span'){
6226         //    this.el.select('span',true).on('click', this.onClick, this);
6227         //}
6228        
6229         // at this point parent should be available..
6230         this.parent().register(this);
6231     },
6232     
6233     onClick : function(e)
6234     {
6235         if (e.getTarget('.dropdown-menu-item')) {
6236             // did you click on a menu itemm.... - then don't trigger onclick..
6237             return;
6238         }
6239         
6240         if(
6241                 this.preventDefault || 
6242                 this.href == '#' 
6243         ){
6244             Roo.log("NavItem - prevent Default?");
6245             e.preventDefault();
6246         }
6247         
6248         if (this.disabled) {
6249             return;
6250         }
6251         
6252         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6253         if (tg && tg.transition) {
6254             Roo.log("waiting for the transitionend");
6255             return;
6256         }
6257         
6258         
6259         
6260         //Roo.log("fire event clicked");
6261         if(this.fireEvent('click', this, e) === false){
6262             return;
6263         };
6264         
6265         if(this.tagtype == 'span'){
6266             return;
6267         }
6268         
6269         //Roo.log(this.href);
6270         var ael = this.el.select('a',true).first();
6271         //Roo.log(ael);
6272         
6273         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6274             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6275             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6276                 return; // ignore... - it's a 'hash' to another page.
6277             }
6278             Roo.log("NavItem - prevent Default?");
6279             e.preventDefault();
6280             this.scrollToElement(e);
6281         }
6282         
6283         
6284         var p =  this.parent();
6285    
6286         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6287             if (typeof(p.setActiveItem) !== 'undefined') {
6288                 p.setActiveItem(this);
6289             }
6290         }
6291         
6292         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6293         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6294             // remove the collapsed menu expand...
6295             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6296         }
6297     },
6298     
6299     isActive: function () {
6300         return this.active
6301     },
6302     setActive : function(state, fire, is_was_active)
6303     {
6304         if (this.active && !state && this.navId) {
6305             this.was_active = true;
6306             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6307             if (nv) {
6308                 nv.clearWasActive(this);
6309             }
6310             
6311         }
6312         this.active = state;
6313         
6314         if (!state ) {
6315             this.el.removeClass('active');
6316             this.navLink ? this.navLink.removeClass('active') : false;
6317         } else if (!this.el.hasClass('active')) {
6318             
6319             this.el.addClass('active');
6320             if (Roo.bootstrap.version == 4 && this.navLink ) {
6321                 this.navLink.addClass('active');
6322             }
6323             
6324         }
6325         if (fire) {
6326             this.fireEvent('changed', this, state);
6327         }
6328         
6329         // show a panel if it's registered and related..
6330         
6331         if (!this.navId || !this.tabId || !state || is_was_active) {
6332             return;
6333         }
6334         
6335         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6336         if (!tg) {
6337             return;
6338         }
6339         var pan = tg.getPanelByName(this.tabId);
6340         if (!pan) {
6341             return;
6342         }
6343         // if we can not flip to new panel - go back to old nav highlight..
6344         if (false == tg.showPanel(pan)) {
6345             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6346             if (nv) {
6347                 var onav = nv.getWasActive();
6348                 if (onav) {
6349                     onav.setActive(true, false, true);
6350                 }
6351             }
6352             
6353         }
6354         
6355         
6356         
6357     },
6358      // this should not be here...
6359     setDisabled : function(state)
6360     {
6361         this.disabled = state;
6362         if (!state ) {
6363             this.el.removeClass('disabled');
6364         } else if (!this.el.hasClass('disabled')) {
6365             this.el.addClass('disabled');
6366         }
6367         
6368     },
6369     
6370     /**
6371      * Fetch the element to display the tooltip on.
6372      * @return {Roo.Element} defaults to this.el
6373      */
6374     tooltipEl : function()
6375     {
6376         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6377     },
6378     
6379     scrollToElement : function(e)
6380     {
6381         var c = document.body;
6382         
6383         /*
6384          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6385          */
6386         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6387             c = document.documentElement;
6388         }
6389         
6390         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6391         
6392         if(!target){
6393             return;
6394         }
6395
6396         var o = target.calcOffsetsTo(c);
6397         
6398         var options = {
6399             target : target,
6400             value : o[1]
6401         };
6402         
6403         this.fireEvent('scrollto', this, options, e);
6404         
6405         Roo.get(c).scrollTo('top', options.value, true);
6406         
6407         return;
6408     }
6409 });
6410  
6411
6412  /*
6413  * - LGPL
6414  *
6415  * sidebar item
6416  *
6417  *  li
6418  *    <span> icon </span>
6419  *    <span> text </span>
6420  *    <span>badge </span>
6421  */
6422
6423 /**
6424  * @class Roo.bootstrap.NavSidebarItem
6425  * @extends Roo.bootstrap.NavItem
6426  * Bootstrap Navbar.NavSidebarItem class
6427  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6428  * {Boolean} open is the menu open
6429  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6430  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6431  * {String} buttonSize (sm|md|lg)the extra classes for the button
6432  * {Boolean} showArrow show arrow next to the text (default true)
6433  * @constructor
6434  * Create a new Navbar Button
6435  * @param {Object} config The config object
6436  */
6437 Roo.bootstrap.NavSidebarItem = function(config){
6438     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6439     this.addEvents({
6440         // raw events
6441         /**
6442          * @event click
6443          * The raw click event for the entire grid.
6444          * @param {Roo.EventObject} e
6445          */
6446         "click" : true,
6447          /**
6448             * @event changed
6449             * Fires when the active item active state changes
6450             * @param {Roo.bootstrap.NavSidebarItem} this
6451             * @param {boolean} state the new state
6452              
6453          */
6454         'changed': true
6455     });
6456    
6457 };
6458
6459 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6460     
6461     badgeWeight : 'default',
6462     
6463     open: false,
6464     
6465     buttonView : false,
6466     
6467     buttonWeight : 'default',
6468     
6469     buttonSize : 'md',
6470     
6471     showArrow : true,
6472     
6473     getAutoCreate : function(){
6474         
6475         
6476         var a = {
6477                 tag: 'a',
6478                 href : this.href || '#',
6479                 cls: '',
6480                 html : '',
6481                 cn : []
6482         };
6483         
6484         if(this.buttonView){
6485             a = {
6486                 tag: 'button',
6487                 href : this.href || '#',
6488                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6489                 html : this.html,
6490                 cn : []
6491             };
6492         }
6493         
6494         var cfg = {
6495             tag: 'li',
6496             cls: '',
6497             cn: [ a ]
6498         };
6499         
6500         if (this.active) {
6501             cfg.cls += ' active';
6502         }
6503         
6504         if (this.disabled) {
6505             cfg.cls += ' disabled';
6506         }
6507         if (this.open) {
6508             cfg.cls += ' open x-open';
6509         }
6510         // left icon..
6511         if (this.glyphicon || this.icon) {
6512             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6513             a.cn.push({ tag : 'i', cls : c }) ;
6514         }
6515         
6516         if(!this.buttonView){
6517             var span = {
6518                 tag: 'span',
6519                 html : this.html || ''
6520             };
6521
6522             a.cn.push(span);
6523             
6524         }
6525         
6526         if (this.badge !== '') {
6527             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6528         }
6529         
6530         if (this.menu) {
6531             
6532             if(this.showArrow){
6533                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6534             }
6535             
6536             a.cls += ' dropdown-toggle treeview' ;
6537         }
6538         
6539         return cfg;
6540     },
6541     
6542     initEvents : function()
6543     { 
6544         if (typeof (this.menu) != 'undefined') {
6545             this.menu.parentType = this.xtype;
6546             this.menu.triggerEl = this.el;
6547             this.menu = this.addxtype(Roo.apply({}, this.menu));
6548         }
6549         
6550         this.el.on('click', this.onClick, this);
6551         
6552         if(this.badge !== ''){
6553             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6554         }
6555         
6556     },
6557     
6558     onClick : function(e)
6559     {
6560         if(this.disabled){
6561             e.preventDefault();
6562             return;
6563         }
6564         
6565         if(this.preventDefault){
6566             e.preventDefault();
6567         }
6568         
6569         this.fireEvent('click', this, e);
6570     },
6571     
6572     disable : function()
6573     {
6574         this.setDisabled(true);
6575     },
6576     
6577     enable : function()
6578     {
6579         this.setDisabled(false);
6580     },
6581     
6582     setDisabled : function(state)
6583     {
6584         if(this.disabled == state){
6585             return;
6586         }
6587         
6588         this.disabled = state;
6589         
6590         if (state) {
6591             this.el.addClass('disabled');
6592             return;
6593         }
6594         
6595         this.el.removeClass('disabled');
6596         
6597         return;
6598     },
6599     
6600     setActive : function(state)
6601     {
6602         if(this.active == state){
6603             return;
6604         }
6605         
6606         this.active = state;
6607         
6608         if (state) {
6609             this.el.addClass('active');
6610             return;
6611         }
6612         
6613         this.el.removeClass('active');
6614         
6615         return;
6616     },
6617     
6618     isActive: function () 
6619     {
6620         return this.active;
6621     },
6622     
6623     setBadge : function(str)
6624     {
6625         if(!this.badgeEl){
6626             return;
6627         }
6628         
6629         this.badgeEl.dom.innerHTML = str;
6630     }
6631     
6632    
6633      
6634  
6635 });
6636  
6637
6638  /*
6639  * - LGPL
6640  *
6641  *  Breadcrumb Nav
6642  * 
6643  */
6644 Roo.namespace('Roo.bootstrap.breadcrumb');
6645
6646
6647 /**
6648  * @class Roo.bootstrap.breadcrumb.Nav
6649  * @extends Roo.bootstrap.Component
6650  * Bootstrap Breadcrumb Nav Class
6651  *  
6652  * @children Roo.bootstrap.breadcrumb.Item
6653  * 
6654  * @constructor
6655  * Create a new breadcrumb.Nav
6656  * @param {Object} config The config object
6657  */
6658
6659
6660 Roo.bootstrap.breadcrumb.Nav = function(config){
6661     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6662     
6663     
6664 };
6665
6666 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6667     
6668     getAutoCreate : function()
6669     {
6670
6671         var cfg = {
6672             tag: 'nav',
6673             cn : [
6674                 {
6675                     tag : 'ol',
6676                     cls : 'breadcrumb'
6677                 }
6678             ]
6679             
6680         };
6681           
6682         return cfg;
6683     },
6684     
6685     initEvents: function()
6686     {
6687         this.olEl = this.el.select('ol',true).first();    
6688     },
6689     getChildContainer : function()
6690     {
6691         return this.olEl;  
6692     }
6693     
6694 });
6695
6696  /*
6697  * - LGPL
6698  *
6699  *  Breadcrumb Item
6700  * 
6701  */
6702
6703
6704 /**
6705  * @class Roo.bootstrap.breadcrumb.Nav
6706  * @extends Roo.bootstrap.Component
6707  * Bootstrap Breadcrumb Nav Class
6708  *  
6709  * @children Roo.bootstrap.breadcrumb.Component
6710  * @cfg {String} html the content of the link.
6711  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6712  * @cfg {Boolean} active is it active
6713
6714  * 
6715  * @constructor
6716  * Create a new breadcrumb.Nav
6717  * @param {Object} config The config object
6718  */
6719
6720 Roo.bootstrap.breadcrumb.Item = function(config){
6721     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6722     this.addEvents({
6723         // img events
6724         /**
6725          * @event click
6726          * The img click event for the img.
6727          * @param {Roo.EventObject} e
6728          */
6729         "click" : true
6730     });
6731     
6732 };
6733
6734 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6735     
6736     href: false,
6737     html : '',
6738     
6739     getAutoCreate : function()
6740     {
6741
6742         var cfg = {
6743             tag: 'li',
6744             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6745         };
6746         if (this.href !== false) {
6747             cfg.cn = [{
6748                 tag : 'a',
6749                 href : this.href,
6750                 html : this.html
6751             }];
6752         } else {
6753             cfg.html = this.html;
6754         }
6755         
6756         return cfg;
6757     },
6758     
6759     initEvents: function()
6760     {
6761         if (this.href) {
6762             this.el.select('a', true).first().on('click',this.onClick, this)
6763         }
6764         
6765     },
6766     onClick : function(e)
6767     {
6768         e.preventDefault();
6769         this.fireEvent('click',this,  e);
6770     }
6771     
6772 });
6773
6774  /*
6775  * - LGPL
6776  *
6777  * row
6778  * 
6779  */
6780
6781 /**
6782  * @class Roo.bootstrap.Row
6783  * @extends Roo.bootstrap.Component
6784  * Bootstrap Row class (contains columns...)
6785  * 
6786  * @constructor
6787  * Create a new Row
6788  * @param {Object} config The config object
6789  */
6790
6791 Roo.bootstrap.Row = function(config){
6792     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6793 };
6794
6795 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6796     
6797     getAutoCreate : function(){
6798        return {
6799             cls: 'row clearfix'
6800        };
6801     }
6802     
6803     
6804 });
6805
6806  
6807
6808  /*
6809  * - LGPL
6810  *
6811  * pagination
6812  * 
6813  */
6814
6815 /**
6816  * @class Roo.bootstrap.Pagination
6817  * @extends Roo.bootstrap.Component
6818  * Bootstrap Pagination class
6819  * @cfg {String} size xs | sm | md | lg
6820  * @cfg {Boolean} inverse false | true
6821  * 
6822  * @constructor
6823  * Create a new Pagination
6824  * @param {Object} config The config object
6825  */
6826
6827 Roo.bootstrap.Pagination = function(config){
6828     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6829 };
6830
6831 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6832     
6833     cls: false,
6834     size: false,
6835     inverse: false,
6836     
6837     getAutoCreate : function(){
6838         var cfg = {
6839             tag: 'ul',
6840                 cls: 'pagination'
6841         };
6842         if (this.inverse) {
6843             cfg.cls += ' inverse';
6844         }
6845         if (this.html) {
6846             cfg.html=this.html;
6847         }
6848         if (this.cls) {
6849             cfg.cls += " " + this.cls;
6850         }
6851         return cfg;
6852     }
6853    
6854 });
6855
6856  
6857
6858  /*
6859  * - LGPL
6860  *
6861  * Pagination item
6862  * 
6863  */
6864
6865
6866 /**
6867  * @class Roo.bootstrap.PaginationItem
6868  * @extends Roo.bootstrap.Component
6869  * Bootstrap PaginationItem class
6870  * @cfg {String} html text
6871  * @cfg {String} href the link
6872  * @cfg {Boolean} preventDefault (true | false) default true
6873  * @cfg {Boolean} active (true | false) default false
6874  * @cfg {Boolean} disabled default false
6875  * 
6876  * 
6877  * @constructor
6878  * Create a new PaginationItem
6879  * @param {Object} config The config object
6880  */
6881
6882
6883 Roo.bootstrap.PaginationItem = function(config){
6884     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6885     this.addEvents({
6886         // raw events
6887         /**
6888          * @event click
6889          * The raw click event for the entire grid.
6890          * @param {Roo.EventObject} e
6891          */
6892         "click" : true
6893     });
6894 };
6895
6896 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6897     
6898     href : false,
6899     html : false,
6900     preventDefault: true,
6901     active : false,
6902     cls : false,
6903     disabled: false,
6904     
6905     getAutoCreate : function(){
6906         var cfg= {
6907             tag: 'li',
6908             cn: [
6909                 {
6910                     tag : 'a',
6911                     href : this.href ? this.href : '#',
6912                     html : this.html ? this.html : ''
6913                 }
6914             ]
6915         };
6916         
6917         if(this.cls){
6918             cfg.cls = this.cls;
6919         }
6920         
6921         if(this.disabled){
6922             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6923         }
6924         
6925         if(this.active){
6926             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6927         }
6928         
6929         return cfg;
6930     },
6931     
6932     initEvents: function() {
6933         
6934         this.el.on('click', this.onClick, this);
6935         
6936     },
6937     onClick : function(e)
6938     {
6939         Roo.log('PaginationItem on click ');
6940         if(this.preventDefault){
6941             e.preventDefault();
6942         }
6943         
6944         if(this.disabled){
6945             return;
6946         }
6947         
6948         this.fireEvent('click', this, e);
6949     }
6950    
6951 });
6952
6953  
6954
6955  /*
6956  * - LGPL
6957  *
6958  * slider
6959  * 
6960  */
6961
6962
6963 /**
6964  * @class Roo.bootstrap.Slider
6965  * @extends Roo.bootstrap.Component
6966  * Bootstrap Slider class
6967  *    
6968  * @constructor
6969  * Create a new Slider
6970  * @param {Object} config The config object
6971  */
6972
6973 Roo.bootstrap.Slider = function(config){
6974     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6975 };
6976
6977 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6978     
6979     getAutoCreate : function(){
6980         
6981         var cfg = {
6982             tag: 'div',
6983             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6984             cn: [
6985                 {
6986                     tag: 'a',
6987                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6988                 }
6989             ]
6990         };
6991         
6992         return cfg;
6993     }
6994    
6995 });
6996
6997  /*
6998  * Based on:
6999  * Ext JS Library 1.1.1
7000  * Copyright(c) 2006-2007, Ext JS, LLC.
7001  *
7002  * Originally Released Under LGPL - original licence link has changed is not relivant.
7003  *
7004  * Fork - LGPL
7005  * <script type="text/javascript">
7006  */
7007  
7008
7009 /**
7010  * @class Roo.grid.ColumnModel
7011  * @extends Roo.util.Observable
7012  * This is the default implementation of a ColumnModel used by the Grid. It defines
7013  * the columns in the grid.
7014  * <br>Usage:<br>
7015  <pre><code>
7016  var colModel = new Roo.grid.ColumnModel([
7017         {header: "Ticker", width: 60, sortable: true, locked: true},
7018         {header: "Company Name", width: 150, sortable: true},
7019         {header: "Market Cap.", width: 100, sortable: true},
7020         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7021         {header: "Employees", width: 100, sortable: true, resizable: false}
7022  ]);
7023  </code></pre>
7024  * <p>
7025  
7026  * The config options listed for this class are options which may appear in each
7027  * individual column definition.
7028  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7029  * @constructor
7030  * @param {Object} config An Array of column config objects. See this class's
7031  * config objects for details.
7032 */
7033 Roo.grid.ColumnModel = function(config){
7034         /**
7035      * The config passed into the constructor
7036      */
7037     this.config = config;
7038     this.lookup = {};
7039
7040     // if no id, create one
7041     // if the column does not have a dataIndex mapping,
7042     // map it to the order it is in the config
7043     for(var i = 0, len = config.length; i < len; i++){
7044         var c = config[i];
7045         if(typeof c.dataIndex == "undefined"){
7046             c.dataIndex = i;
7047         }
7048         if(typeof c.renderer == "string"){
7049             c.renderer = Roo.util.Format[c.renderer];
7050         }
7051         if(typeof c.id == "undefined"){
7052             c.id = Roo.id();
7053         }
7054         if(c.editor && c.editor.xtype){
7055             c.editor  = Roo.factory(c.editor, Roo.grid);
7056         }
7057         if(c.editor && c.editor.isFormField){
7058             c.editor = new Roo.grid.GridEditor(c.editor);
7059         }
7060         this.lookup[c.id] = c;
7061     }
7062
7063     /**
7064      * The width of columns which have no width specified (defaults to 100)
7065      * @type Number
7066      */
7067     this.defaultWidth = 100;
7068
7069     /**
7070      * Default sortable of columns which have no sortable specified (defaults to false)
7071      * @type Boolean
7072      */
7073     this.defaultSortable = false;
7074
7075     this.addEvents({
7076         /**
7077              * @event widthchange
7078              * Fires when the width of a column changes.
7079              * @param {ColumnModel} this
7080              * @param {Number} columnIndex The column index
7081              * @param {Number} newWidth The new width
7082              */
7083             "widthchange": true,
7084         /**
7085              * @event headerchange
7086              * Fires when the text of a header changes.
7087              * @param {ColumnModel} this
7088              * @param {Number} columnIndex The column index
7089              * @param {Number} newText The new header text
7090              */
7091             "headerchange": true,
7092         /**
7093              * @event hiddenchange
7094              * Fires when a column is hidden or "unhidden".
7095              * @param {ColumnModel} this
7096              * @param {Number} columnIndex The column index
7097              * @param {Boolean} hidden true if hidden, false otherwise
7098              */
7099             "hiddenchange": true,
7100             /**
7101          * @event columnmoved
7102          * Fires when a column is moved.
7103          * @param {ColumnModel} this
7104          * @param {Number} oldIndex
7105          * @param {Number} newIndex
7106          */
7107         "columnmoved" : true,
7108         /**
7109          * @event columlockchange
7110          * Fires when a column's locked state is changed
7111          * @param {ColumnModel} this
7112          * @param {Number} colIndex
7113          * @param {Boolean} locked true if locked
7114          */
7115         "columnlockchange" : true
7116     });
7117     Roo.grid.ColumnModel.superclass.constructor.call(this);
7118 };
7119 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7120     /**
7121      * @cfg {String} header The header text to display in the Grid view.
7122      */
7123     /**
7124      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7125      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7126      * specified, the column's index is used as an index into the Record's data Array.
7127      */
7128     /**
7129      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7130      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7131      */
7132     /**
7133      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7134      * Defaults to the value of the {@link #defaultSortable} property.
7135      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7136      */
7137     /**
7138      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7139      */
7140     /**
7141      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7142      */
7143     /**
7144      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7145      */
7146     /**
7147      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7148      */
7149     /**
7150      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7151      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7152      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7153      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7154      */
7155        /**
7156      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7157      */
7158     /**
7159      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7160      */
7161     /**
7162      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7163      */
7164     /**
7165      * @cfg {String} cursor (Optional)
7166      */
7167     /**
7168      * @cfg {String} tooltip (Optional)
7169      */
7170     /**
7171      * @cfg {Number} xs (Optional)
7172      */
7173     /**
7174      * @cfg {Number} sm (Optional)
7175      */
7176     /**
7177      * @cfg {Number} md (Optional)
7178      */
7179     /**
7180      * @cfg {Number} lg (Optional)
7181      */
7182     /**
7183      * Returns the id of the column at the specified index.
7184      * @param {Number} index The column index
7185      * @return {String} the id
7186      */
7187     getColumnId : function(index){
7188         return this.config[index].id;
7189     },
7190
7191     /**
7192      * Returns the column for a specified id.
7193      * @param {String} id The column id
7194      * @return {Object} the column
7195      */
7196     getColumnById : function(id){
7197         return this.lookup[id];
7198     },
7199
7200     
7201     /**
7202      * Returns the column for a specified dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Object|Boolean} the column or false if not found
7205      */
7206     getColumnByDataIndex: function(dataIndex){
7207         var index = this.findColumnIndex(dataIndex);
7208         return index > -1 ? this.config[index] : false;
7209     },
7210     
7211     /**
7212      * Returns the index for a specified column id.
7213      * @param {String} id The column id
7214      * @return {Number} the index, or -1 if not found
7215      */
7216     getIndexById : function(id){
7217         for(var i = 0, len = this.config.length; i < len; i++){
7218             if(this.config[i].id == id){
7219                 return i;
7220             }
7221         }
7222         return -1;
7223     },
7224     
7225     /**
7226      * Returns the index for a specified column dataIndex.
7227      * @param {String} dataIndex The column dataIndex
7228      * @return {Number} the index, or -1 if not found
7229      */
7230     
7231     findColumnIndex : function(dataIndex){
7232         for(var i = 0, len = this.config.length; i < len; i++){
7233             if(this.config[i].dataIndex == dataIndex){
7234                 return i;
7235             }
7236         }
7237         return -1;
7238     },
7239     
7240     
7241     moveColumn : function(oldIndex, newIndex){
7242         var c = this.config[oldIndex];
7243         this.config.splice(oldIndex, 1);
7244         this.config.splice(newIndex, 0, c);
7245         this.dataMap = null;
7246         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7247     },
7248
7249     isLocked : function(colIndex){
7250         return this.config[colIndex].locked === true;
7251     },
7252
7253     setLocked : function(colIndex, value, suppressEvent){
7254         if(this.isLocked(colIndex) == value){
7255             return;
7256         }
7257         this.config[colIndex].locked = value;
7258         if(!suppressEvent){
7259             this.fireEvent("columnlockchange", this, colIndex, value);
7260         }
7261     },
7262
7263     getTotalLockedWidth : function(){
7264         var totalWidth = 0;
7265         for(var i = 0; i < this.config.length; i++){
7266             if(this.isLocked(i) && !this.isHidden(i)){
7267                 this.totalWidth += this.getColumnWidth(i);
7268             }
7269         }
7270         return totalWidth;
7271     },
7272
7273     getLockedCount : function(){
7274         for(var i = 0, len = this.config.length; i < len; i++){
7275             if(!this.isLocked(i)){
7276                 return i;
7277             }
7278         }
7279         
7280         return this.config.length;
7281     },
7282
7283     /**
7284      * Returns the number of columns.
7285      * @return {Number}
7286      */
7287     getColumnCount : function(visibleOnly){
7288         if(visibleOnly === true){
7289             var c = 0;
7290             for(var i = 0, len = this.config.length; i < len; i++){
7291                 if(!this.isHidden(i)){
7292                     c++;
7293                 }
7294             }
7295             return c;
7296         }
7297         return this.config.length;
7298     },
7299
7300     /**
7301      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7302      * @param {Function} fn
7303      * @param {Object} scope (optional)
7304      * @return {Array} result
7305      */
7306     getColumnsBy : function(fn, scope){
7307         var r = [];
7308         for(var i = 0, len = this.config.length; i < len; i++){
7309             var c = this.config[i];
7310             if(fn.call(scope||this, c, i) === true){
7311                 r[r.length] = c;
7312             }
7313         }
7314         return r;
7315     },
7316
7317     /**
7318      * Returns true if the specified column is sortable.
7319      * @param {Number} col The column index
7320      * @return {Boolean}
7321      */
7322     isSortable : function(col){
7323         if(typeof this.config[col].sortable == "undefined"){
7324             return this.defaultSortable;
7325         }
7326         return this.config[col].sortable;
7327     },
7328
7329     /**
7330      * Returns the rendering (formatting) function defined for the column.
7331      * @param {Number} col The column index.
7332      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7333      */
7334     getRenderer : function(col){
7335         if(!this.config[col].renderer){
7336             return Roo.grid.ColumnModel.defaultRenderer;
7337         }
7338         return this.config[col].renderer;
7339     },
7340
7341     /**
7342      * Sets the rendering (formatting) function for a column.
7343      * @param {Number} col The column index
7344      * @param {Function} fn The function to use to process the cell's raw data
7345      * to return HTML markup for the grid view. The render function is called with
7346      * the following parameters:<ul>
7347      * <li>Data value.</li>
7348      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7349      * <li>css A CSS style string to apply to the table cell.</li>
7350      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7351      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7352      * <li>Row index</li>
7353      * <li>Column index</li>
7354      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7355      */
7356     setRenderer : function(col, fn){
7357         this.config[col].renderer = fn;
7358     },
7359
7360     /**
7361      * Returns the width for the specified column.
7362      * @param {Number} col The column index
7363      * @return {Number}
7364      */
7365     getColumnWidth : function(col){
7366         return this.config[col].width * 1 || this.defaultWidth;
7367     },
7368
7369     /**
7370      * Sets the width for a column.
7371      * @param {Number} col The column index
7372      * @param {Number} width The new width
7373      */
7374     setColumnWidth : function(col, width, suppressEvent){
7375         this.config[col].width = width;
7376         this.totalWidth = null;
7377         if(!suppressEvent){
7378              this.fireEvent("widthchange", this, col, width);
7379         }
7380     },
7381
7382     /**
7383      * Returns the total width of all columns.
7384      * @param {Boolean} includeHidden True to include hidden column widths
7385      * @return {Number}
7386      */
7387     getTotalWidth : function(includeHidden){
7388         if(!this.totalWidth){
7389             this.totalWidth = 0;
7390             for(var i = 0, len = this.config.length; i < len; i++){
7391                 if(includeHidden || !this.isHidden(i)){
7392                     this.totalWidth += this.getColumnWidth(i);
7393                 }
7394             }
7395         }
7396         return this.totalWidth;
7397     },
7398
7399     /**
7400      * Returns the header for the specified column.
7401      * @param {Number} col The column index
7402      * @return {String}
7403      */
7404     getColumnHeader : function(col){
7405         return this.config[col].header;
7406     },
7407
7408     /**
7409      * Sets the header for a column.
7410      * @param {Number} col The column index
7411      * @param {String} header The new header
7412      */
7413     setColumnHeader : function(col, header){
7414         this.config[col].header = header;
7415         this.fireEvent("headerchange", this, col, header);
7416     },
7417
7418     /**
7419      * Returns the tooltip for the specified column.
7420      * @param {Number} col The column index
7421      * @return {String}
7422      */
7423     getColumnTooltip : function(col){
7424             return this.config[col].tooltip;
7425     },
7426     /**
7427      * Sets the tooltip for a column.
7428      * @param {Number} col The column index
7429      * @param {String} tooltip The new tooltip
7430      */
7431     setColumnTooltip : function(col, tooltip){
7432             this.config[col].tooltip = tooltip;
7433     },
7434
7435     /**
7436      * Returns the dataIndex for the specified column.
7437      * @param {Number} col The column index
7438      * @return {Number}
7439      */
7440     getDataIndex : function(col){
7441         return this.config[col].dataIndex;
7442     },
7443
7444     /**
7445      * Sets the dataIndex for a column.
7446      * @param {Number} col The column index
7447      * @param {Number} dataIndex The new dataIndex
7448      */
7449     setDataIndex : function(col, dataIndex){
7450         this.config[col].dataIndex = dataIndex;
7451     },
7452
7453     
7454     
7455     /**
7456      * Returns true if the cell is editable.
7457      * @param {Number} colIndex The column index
7458      * @param {Number} rowIndex The row index - this is nto actually used..?
7459      * @return {Boolean}
7460      */
7461     isCellEditable : function(colIndex, rowIndex){
7462         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7463     },
7464
7465     /**
7466      * Returns the editor defined for the cell/column.
7467      * return false or null to disable editing.
7468      * @param {Number} colIndex The column index
7469      * @param {Number} rowIndex The row index
7470      * @return {Object}
7471      */
7472     getCellEditor : function(colIndex, rowIndex){
7473         return this.config[colIndex].editor;
7474     },
7475
7476     /**
7477      * Sets if a column is editable.
7478      * @param {Number} col The column index
7479      * @param {Boolean} editable True if the column is editable
7480      */
7481     setEditable : function(col, editable){
7482         this.config[col].editable = editable;
7483     },
7484
7485
7486     /**
7487      * Returns true if the column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @return {Boolean}
7490      */
7491     isHidden : function(colIndex){
7492         return this.config[colIndex].hidden;
7493     },
7494
7495
7496     /**
7497      * Returns true if the column width cannot be changed
7498      */
7499     isFixed : function(colIndex){
7500         return this.config[colIndex].fixed;
7501     },
7502
7503     /**
7504      * Returns true if the column can be resized
7505      * @return {Boolean}
7506      */
7507     isResizable : function(colIndex){
7508         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7509     },
7510     /**
7511      * Sets if a column is hidden.
7512      * @param {Number} colIndex The column index
7513      * @param {Boolean} hidden True if the column is hidden
7514      */
7515     setHidden : function(colIndex, hidden){
7516         this.config[colIndex].hidden = hidden;
7517         this.totalWidth = null;
7518         this.fireEvent("hiddenchange", this, colIndex, hidden);
7519     },
7520
7521     /**
7522      * Sets the editor for a column.
7523      * @param {Number} col The column index
7524      * @param {Object} editor The editor object
7525      */
7526     setEditor : function(col, editor){
7527         this.config[col].editor = editor;
7528     }
7529 });
7530
7531 Roo.grid.ColumnModel.defaultRenderer = function(value)
7532 {
7533     if(typeof value == "object") {
7534         return value;
7535     }
7536         if(typeof value == "string" && value.length < 1){
7537             return "&#160;";
7538         }
7539     
7540         return String.format("{0}", value);
7541 };
7542
7543 // Alias for backwards compatibility
7544 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7545 /*
7546  * Based on:
7547  * Ext JS Library 1.1.1
7548  * Copyright(c) 2006-2007, Ext JS, LLC.
7549  *
7550  * Originally Released Under LGPL - original licence link has changed is not relivant.
7551  *
7552  * Fork - LGPL
7553  * <script type="text/javascript">
7554  */
7555  
7556 /**
7557  * @class Roo.LoadMask
7558  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7559  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7560  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7561  * element's UpdateManager load indicator and will be destroyed after the initial load.
7562  * @constructor
7563  * Create a new LoadMask
7564  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7565  * @param {Object} config The config object
7566  */
7567 Roo.LoadMask = function(el, config){
7568     this.el = Roo.get(el);
7569     Roo.apply(this, config);
7570     if(this.store){
7571         this.store.on('beforeload', this.onBeforeLoad, this);
7572         this.store.on('load', this.onLoad, this);
7573         this.store.on('loadexception', this.onLoadException, this);
7574         this.removeMask = false;
7575     }else{
7576         var um = this.el.getUpdateManager();
7577         um.showLoadIndicator = false; // disable the default indicator
7578         um.on('beforeupdate', this.onBeforeLoad, this);
7579         um.on('update', this.onLoad, this);
7580         um.on('failure', this.onLoad, this);
7581         this.removeMask = true;
7582     }
7583 };
7584
7585 Roo.LoadMask.prototype = {
7586     /**
7587      * @cfg {Boolean} removeMask
7588      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7589      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7590      */
7591     /**
7592      * @cfg {String} msg
7593      * The text to display in a centered loading message box (defaults to 'Loading...')
7594      */
7595     msg : 'Loading...',
7596     /**
7597      * @cfg {String} msgCls
7598      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7599      */
7600     msgCls : 'x-mask-loading',
7601
7602     /**
7603      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7604      * @type Boolean
7605      */
7606     disabled: false,
7607
7608     /**
7609      * Disables the mask to prevent it from being displayed
7610      */
7611     disable : function(){
7612        this.disabled = true;
7613     },
7614
7615     /**
7616      * Enables the mask so that it can be displayed
7617      */
7618     enable : function(){
7619         this.disabled = false;
7620     },
7621     
7622     onLoadException : function()
7623     {
7624         Roo.log(arguments);
7625         
7626         if (typeof(arguments[3]) != 'undefined') {
7627             Roo.MessageBox.alert("Error loading",arguments[3]);
7628         } 
7629         /*
7630         try {
7631             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7632                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7633             }   
7634         } catch(e) {
7635             
7636         }
7637         */
7638     
7639         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7640     },
7641     // private
7642     onLoad : function()
7643     {
7644         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7645     },
7646
7647     // private
7648     onBeforeLoad : function(){
7649         if(!this.disabled){
7650             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7651         }
7652     },
7653
7654     // private
7655     destroy : function(){
7656         if(this.store){
7657             this.store.un('beforeload', this.onBeforeLoad, this);
7658             this.store.un('load', this.onLoad, this);
7659             this.store.un('loadexception', this.onLoadException, this);
7660         }else{
7661             var um = this.el.getUpdateManager();
7662             um.un('beforeupdate', this.onBeforeLoad, this);
7663             um.un('update', this.onLoad, this);
7664             um.un('failure', this.onLoad, this);
7665         }
7666     }
7667 };/*
7668  * - LGPL
7669  *
7670  * table
7671  * 
7672  */
7673
7674 /**
7675  * @class Roo.bootstrap.Table
7676  * @extends Roo.bootstrap.Component
7677  * Bootstrap Table class
7678  * @cfg {String} cls table class
7679  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7680  * @cfg {String} bgcolor Specifies the background color for a table
7681  * @cfg {Number} border Specifies whether the table cells should have borders or not
7682  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7683  * @cfg {Number} cellspacing Specifies the space between cells
7684  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7685  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7686  * @cfg {String} sortable Specifies that the table should be sortable
7687  * @cfg {String} summary Specifies a summary of the content of a table
7688  * @cfg {Number} width Specifies the width of a table
7689  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7690  * 
7691  * @cfg {boolean} striped Should the rows be alternative striped
7692  * @cfg {boolean} bordered Add borders to the table
7693  * @cfg {boolean} hover Add hover highlighting
7694  * @cfg {boolean} condensed Format condensed
7695  * @cfg {boolean} responsive Format condensed
7696  * @cfg {Boolean} loadMask (true|false) default false
7697  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7698  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7699  * @cfg {Boolean} rowSelection (true|false) default false
7700  * @cfg {Boolean} cellSelection (true|false) default false
7701  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7702  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7703  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7704  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7705  
7706  * 
7707  * @constructor
7708  * Create a new Table
7709  * @param {Object} config The config object
7710  */
7711
7712 Roo.bootstrap.Table = function(config){
7713     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7714     
7715   
7716     
7717     // BC...
7718     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7719     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7720     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7721     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7722     
7723     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7724     if (this.sm) {
7725         this.sm.grid = this;
7726         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7727         this.sm = this.selModel;
7728         this.sm.xmodule = this.xmodule || false;
7729     }
7730     
7731     if (this.cm && typeof(this.cm.config) == 'undefined') {
7732         this.colModel = new Roo.grid.ColumnModel(this.cm);
7733         this.cm = this.colModel;
7734         this.cm.xmodule = this.xmodule || false;
7735     }
7736     if (this.store) {
7737         this.store= Roo.factory(this.store, Roo.data);
7738         this.ds = this.store;
7739         this.ds.xmodule = this.xmodule || false;
7740          
7741     }
7742     if (this.footer && this.store) {
7743         this.footer.dataSource = this.ds;
7744         this.footer = Roo.factory(this.footer);
7745     }
7746     
7747     /** @private */
7748     this.addEvents({
7749         /**
7750          * @event cellclick
7751          * Fires when a cell is clicked
7752          * @param {Roo.bootstrap.Table} this
7753          * @param {Roo.Element} el
7754          * @param {Number} rowIndex
7755          * @param {Number} columnIndex
7756          * @param {Roo.EventObject} e
7757          */
7758         "cellclick" : true,
7759         /**
7760          * @event celldblclick
7761          * Fires when a cell is double clicked
7762          * @param {Roo.bootstrap.Table} this
7763          * @param {Roo.Element} el
7764          * @param {Number} rowIndex
7765          * @param {Number} columnIndex
7766          * @param {Roo.EventObject} e
7767          */
7768         "celldblclick" : true,
7769         /**
7770          * @event rowclick
7771          * Fires when a row is clicked
7772          * @param {Roo.bootstrap.Table} this
7773          * @param {Roo.Element} el
7774          * @param {Number} rowIndex
7775          * @param {Roo.EventObject} e
7776          */
7777         "rowclick" : true,
7778         /**
7779          * @event rowdblclick
7780          * Fires when a row is double clicked
7781          * @param {Roo.bootstrap.Table} this
7782          * @param {Roo.Element} el
7783          * @param {Number} rowIndex
7784          * @param {Roo.EventObject} e
7785          */
7786         "rowdblclick" : true,
7787         /**
7788          * @event mouseover
7789          * Fires when a mouseover occur
7790          * @param {Roo.bootstrap.Table} this
7791          * @param {Roo.Element} el
7792          * @param {Number} rowIndex
7793          * @param {Number} columnIndex
7794          * @param {Roo.EventObject} e
7795          */
7796         "mouseover" : true,
7797         /**
7798          * @event mouseout
7799          * Fires when a mouseout occur
7800          * @param {Roo.bootstrap.Table} this
7801          * @param {Roo.Element} el
7802          * @param {Number} rowIndex
7803          * @param {Number} columnIndex
7804          * @param {Roo.EventObject} e
7805          */
7806         "mouseout" : true,
7807         /**
7808          * @event rowclass
7809          * Fires when a row is rendered, so you can change add a style to it.
7810          * @param {Roo.bootstrap.Table} this
7811          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7812          */
7813         'rowclass' : true,
7814           /**
7815          * @event rowsrendered
7816          * Fires when all the  rows have been rendered
7817          * @param {Roo.bootstrap.Table} this
7818          */
7819         'rowsrendered' : true,
7820         /**
7821          * @event contextmenu
7822          * The raw contextmenu event for the entire grid.
7823          * @param {Roo.EventObject} e
7824          */
7825         "contextmenu" : true,
7826         /**
7827          * @event rowcontextmenu
7828          * Fires when a row is right clicked
7829          * @param {Roo.bootstrap.Table} this
7830          * @param {Number} rowIndex
7831          * @param {Roo.EventObject} e
7832          */
7833         "rowcontextmenu" : true,
7834         /**
7835          * @event cellcontextmenu
7836          * Fires when a cell is right clicked
7837          * @param {Roo.bootstrap.Table} this
7838          * @param {Number} rowIndex
7839          * @param {Number} cellIndex
7840          * @param {Roo.EventObject} e
7841          */
7842          "cellcontextmenu" : true,
7843          /**
7844          * @event headercontextmenu
7845          * Fires when a header is right clicked
7846          * @param {Roo.bootstrap.Table} this
7847          * @param {Number} columnIndex
7848          * @param {Roo.EventObject} e
7849          */
7850         "headercontextmenu" : true
7851     });
7852 };
7853
7854 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7855     
7856     cls: false,
7857     align: false,
7858     bgcolor: false,
7859     border: false,
7860     cellpadding: false,
7861     cellspacing: false,
7862     frame: false,
7863     rules: false,
7864     sortable: false,
7865     summary: false,
7866     width: false,
7867     striped : false,
7868     scrollBody : false,
7869     bordered: false,
7870     hover:  false,
7871     condensed : false,
7872     responsive : false,
7873     sm : false,
7874     cm : false,
7875     store : false,
7876     loadMask : false,
7877     footerShow : true,
7878     headerShow : true,
7879   
7880     rowSelection : false,
7881     cellSelection : false,
7882     layout : false,
7883     
7884     // Roo.Element - the tbody
7885     mainBody: false,
7886     // Roo.Element - thead element
7887     mainHead: false,
7888     
7889     container: false, // used by gridpanel...
7890     
7891     lazyLoad : false,
7892     
7893     CSS : Roo.util.CSS,
7894     
7895     auto_hide_footer : false,
7896     
7897     getAutoCreate : function()
7898     {
7899         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7900         
7901         cfg = {
7902             tag: 'table',
7903             cls : 'table',
7904             cn : []
7905         };
7906         if (this.scrollBody) {
7907             cfg.cls += ' table-body-fixed';
7908         }    
7909         if (this.striped) {
7910             cfg.cls += ' table-striped';
7911         }
7912         
7913         if (this.hover) {
7914             cfg.cls += ' table-hover';
7915         }
7916         if (this.bordered) {
7917             cfg.cls += ' table-bordered';
7918         }
7919         if (this.condensed) {
7920             cfg.cls += ' table-condensed';
7921         }
7922         if (this.responsive) {
7923             cfg.cls += ' table-responsive';
7924         }
7925         
7926         if (this.cls) {
7927             cfg.cls+=  ' ' +this.cls;
7928         }
7929         
7930         // this lot should be simplifed...
7931         var _t = this;
7932         var cp = [
7933             'align',
7934             'bgcolor',
7935             'border',
7936             'cellpadding',
7937             'cellspacing',
7938             'frame',
7939             'rules',
7940             'sortable',
7941             'summary',
7942             'width'
7943         ].forEach(function(k) {
7944             if (_t[k]) {
7945                 cfg[k] = _t[k];
7946             }
7947         });
7948         
7949         
7950         if (this.layout) {
7951             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7952         }
7953         
7954         if(this.store || this.cm){
7955             if(this.headerShow){
7956                 cfg.cn.push(this.renderHeader());
7957             }
7958             
7959             cfg.cn.push(this.renderBody());
7960             
7961             if(this.footerShow){
7962                 cfg.cn.push(this.renderFooter());
7963             }
7964             // where does this come from?
7965             //cfg.cls+=  ' TableGrid';
7966         }
7967         
7968         return { cn : [ cfg ] };
7969     },
7970     
7971     initEvents : function()
7972     {   
7973         if(!this.store || !this.cm){
7974             return;
7975         }
7976         if (this.selModel) {
7977             this.selModel.initEvents();
7978         }
7979         
7980         
7981         //Roo.log('initEvents with ds!!!!');
7982         
7983         this.mainBody = this.el.select('tbody', true).first();
7984         this.mainHead = this.el.select('thead', true).first();
7985         this.mainFoot = this.el.select('tfoot', true).first();
7986         
7987         
7988         
7989         var _this = this;
7990         
7991         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7992             e.on('click', _this.sort, _this);
7993         });
7994         
7995         this.mainBody.on("click", this.onClick, this);
7996         this.mainBody.on("dblclick", this.onDblClick, this);
7997         
7998         // why is this done????? = it breaks dialogs??
7999         //this.parent().el.setStyle('position', 'relative');
8000         
8001         
8002         if (this.footer) {
8003             this.footer.parentId = this.id;
8004             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8005             
8006             if(this.lazyLoad){
8007                 this.el.select('tfoot tr td').first().addClass('hide');
8008             }
8009         } 
8010         
8011         if(this.loadMask) {
8012             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8013         }
8014         
8015         this.store.on('load', this.onLoad, this);
8016         this.store.on('beforeload', this.onBeforeLoad, this);
8017         this.store.on('update', this.onUpdate, this);
8018         this.store.on('add', this.onAdd, this);
8019         this.store.on("clear", this.clear, this);
8020         
8021         this.el.on("contextmenu", this.onContextMenu, this);
8022         
8023         this.mainBody.on('scroll', this.onBodyScroll, this);
8024         
8025         this.cm.on("headerchange", this.onHeaderChange, this);
8026         
8027         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8028         
8029     },
8030     
8031     onContextMenu : function(e, t)
8032     {
8033         this.processEvent("contextmenu", e);
8034     },
8035     
8036     processEvent : function(name, e)
8037     {
8038         if (name != 'touchstart' ) {
8039             this.fireEvent(name, e);    
8040         }
8041         
8042         var t = e.getTarget();
8043         
8044         var cell = Roo.get(t);
8045         
8046         if(!cell){
8047             return;
8048         }
8049         
8050         if(cell.findParent('tfoot', false, true)){
8051             return;
8052         }
8053         
8054         if(cell.findParent('thead', false, true)){
8055             
8056             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8057                 cell = Roo.get(t).findParent('th', false, true);
8058                 if (!cell) {
8059                     Roo.log("failed to find th in thead?");
8060                     Roo.log(e.getTarget());
8061                     return;
8062                 }
8063             }
8064             
8065             var cellIndex = cell.dom.cellIndex;
8066             
8067             var ename = name == 'touchstart' ? 'click' : name;
8068             this.fireEvent("header" + ename, this, cellIndex, e);
8069             
8070             return;
8071         }
8072         
8073         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8074             cell = Roo.get(t).findParent('td', false, true);
8075             if (!cell) {
8076                 Roo.log("failed to find th in tbody?");
8077                 Roo.log(e.getTarget());
8078                 return;
8079             }
8080         }
8081         
8082         var row = cell.findParent('tr', false, true);
8083         var cellIndex = cell.dom.cellIndex;
8084         var rowIndex = row.dom.rowIndex - 1;
8085         
8086         if(row !== false){
8087             
8088             this.fireEvent("row" + name, this, rowIndex, e);
8089             
8090             if(cell !== false){
8091             
8092                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8093             }
8094         }
8095         
8096     },
8097     
8098     onMouseover : function(e, el)
8099     {
8100         var cell = Roo.get(el);
8101         
8102         if(!cell){
8103             return;
8104         }
8105         
8106         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8107             cell = cell.findParent('td', false, true);
8108         }
8109         
8110         var row = cell.findParent('tr', false, true);
8111         var cellIndex = cell.dom.cellIndex;
8112         var rowIndex = row.dom.rowIndex - 1; // start from 0
8113         
8114         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8115         
8116     },
8117     
8118     onMouseout : function(e, el)
8119     {
8120         var cell = Roo.get(el);
8121         
8122         if(!cell){
8123             return;
8124         }
8125         
8126         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8127             cell = cell.findParent('td', false, true);
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         var cellIndex = cell.dom.cellIndex;
8132         var rowIndex = row.dom.rowIndex - 1; // start from 0
8133         
8134         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8135         
8136     },
8137     
8138     onClick : function(e, el)
8139     {
8140         var cell = Roo.get(el);
8141         
8142         if(!cell || (!this.cellSelection && !this.rowSelection)){
8143             return;
8144         }
8145         
8146         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8147             cell = cell.findParent('td', false, true);
8148         }
8149         
8150         if(!cell || typeof(cell) == 'undefined'){
8151             return;
8152         }
8153         
8154         var row = cell.findParent('tr', false, true);
8155         
8156         if(!row || typeof(row) == 'undefined'){
8157             return;
8158         }
8159         
8160         var cellIndex = cell.dom.cellIndex;
8161         var rowIndex = this.getRowIndex(row);
8162         
8163         // why??? - should these not be based on SelectionModel?
8164         if(this.cellSelection){
8165             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8166         }
8167         
8168         if(this.rowSelection){
8169             this.fireEvent('rowclick', this, row, rowIndex, e);
8170         }
8171         
8172         
8173     },
8174         
8175     onDblClick : function(e,el)
8176     {
8177         var cell = Roo.get(el);
8178         
8179         if(!cell || (!this.cellSelection && !this.rowSelection)){
8180             return;
8181         }
8182         
8183         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8184             cell = cell.findParent('td', false, true);
8185         }
8186         
8187         if(!cell || typeof(cell) == 'undefined'){
8188             return;
8189         }
8190         
8191         var row = cell.findParent('tr', false, true);
8192         
8193         if(!row || typeof(row) == 'undefined'){
8194             return;
8195         }
8196         
8197         var cellIndex = cell.dom.cellIndex;
8198         var rowIndex = this.getRowIndex(row);
8199         
8200         if(this.cellSelection){
8201             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8202         }
8203         
8204         if(this.rowSelection){
8205             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8206         }
8207     },
8208     
8209     sort : function(e,el)
8210     {
8211         var col = Roo.get(el);
8212         
8213         if(!col.hasClass('sortable')){
8214             return;
8215         }
8216         
8217         var sort = col.attr('sort');
8218         var dir = 'ASC';
8219         
8220         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8221             dir = 'DESC';
8222         }
8223         
8224         this.store.sortInfo = {field : sort, direction : dir};
8225         
8226         if (this.footer) {
8227             Roo.log("calling footer first");
8228             this.footer.onClick('first');
8229         } else {
8230         
8231             this.store.load({ params : { start : 0 } });
8232         }
8233     },
8234     
8235     renderHeader : function()
8236     {
8237         var header = {
8238             tag: 'thead',
8239             cn : []
8240         };
8241         
8242         var cm = this.cm;
8243         this.totalWidth = 0;
8244         
8245         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8246             
8247             var config = cm.config[i];
8248             
8249             var c = {
8250                 tag: 'th',
8251                 cls : 'x-hcol-' + i,
8252                 style : '',
8253                 html: cm.getColumnHeader(i)
8254             };
8255             
8256             var hh = '';
8257             
8258             if(typeof(config.sortable) != 'undefined' && config.sortable){
8259                 c.cls = 'sortable';
8260                 c.html = '<i class="glyphicon"></i>' + c.html;
8261             }
8262             
8263             // could use BS4 hidden-..-down 
8264             
8265             if(typeof(config.lgHeader) != 'undefined'){
8266                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8267             }
8268             
8269             if(typeof(config.mdHeader) != 'undefined'){
8270                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8271             }
8272             
8273             if(typeof(config.smHeader) != 'undefined'){
8274                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8275             }
8276             
8277             if(typeof(config.xsHeader) != 'undefined'){
8278                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8279             }
8280             
8281             if(hh.length){
8282                 c.html = hh;
8283             }
8284             
8285             if(typeof(config.tooltip) != 'undefined'){
8286                 c.tooltip = config.tooltip;
8287             }
8288             
8289             if(typeof(config.colspan) != 'undefined'){
8290                 c.colspan = config.colspan;
8291             }
8292             
8293             if(typeof(config.hidden) != 'undefined' && config.hidden){
8294                 c.style += ' display:none;';
8295             }
8296             
8297             if(typeof(config.dataIndex) != 'undefined'){
8298                 c.sort = config.dataIndex;
8299             }
8300             
8301            
8302             
8303             if(typeof(config.align) != 'undefined' && config.align.length){
8304                 c.style += ' text-align:' + config.align + ';';
8305             }
8306             
8307             if(typeof(config.width) != 'undefined'){
8308                 c.style += ' width:' + config.width + 'px;';
8309                 this.totalWidth += config.width;
8310             } else {
8311                 this.totalWidth += 100; // assume minimum of 100 per column?
8312             }
8313             
8314             if(typeof(config.cls) != 'undefined'){
8315                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8316             }
8317             
8318             ['xs','sm','md','lg'].map(function(size){
8319                 
8320                 if(typeof(config[size]) == 'undefined'){
8321                     return;
8322                 }
8323                  
8324                 if (!config[size]) { // 0 = hidden
8325                     // BS 4 '0' is treated as hide that column and below.
8326                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8327                     return;
8328                 }
8329                 
8330                 c.cls += ' col-' + size + '-' + config[size] + (
8331                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8332                 );
8333                 
8334                 
8335             });
8336             
8337             header.cn.push(c)
8338         }
8339         
8340         return header;
8341     },
8342     
8343     renderBody : function()
8344     {
8345         var body = {
8346             tag: 'tbody',
8347             cn : [
8348                 {
8349                     tag: 'tr',
8350                     cn : [
8351                         {
8352                             tag : 'td',
8353                             colspan :  this.cm.getColumnCount()
8354                         }
8355                     ]
8356                 }
8357             ]
8358         };
8359         
8360         return body;
8361     },
8362     
8363     renderFooter : function()
8364     {
8365         var footer = {
8366             tag: 'tfoot',
8367             cn : [
8368                 {
8369                     tag: 'tr',
8370                     cn : [
8371                         {
8372                             tag : 'td',
8373                             colspan :  this.cm.getColumnCount()
8374                         }
8375                     ]
8376                 }
8377             ]
8378         };
8379         
8380         return footer;
8381     },
8382     
8383     
8384     
8385     onLoad : function()
8386     {
8387 //        Roo.log('ds onload');
8388         this.clear();
8389         
8390         var _this = this;
8391         var cm = this.cm;
8392         var ds = this.store;
8393         
8394         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8395             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8396             if (_this.store.sortInfo) {
8397                     
8398                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8399                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8400                 }
8401                 
8402                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8403                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8404                 }
8405             }
8406         });
8407         
8408         var tbody =  this.mainBody;
8409               
8410         if(ds.getCount() > 0){
8411             ds.data.each(function(d,rowIndex){
8412                 var row =  this.renderRow(cm, ds, rowIndex);
8413                 
8414                 tbody.createChild(row);
8415                 
8416                 var _this = this;
8417                 
8418                 if(row.cellObjects.length){
8419                     Roo.each(row.cellObjects, function(r){
8420                         _this.renderCellObject(r);
8421                     })
8422                 }
8423                 
8424             }, this);
8425         }
8426         
8427         var tfoot = this.el.select('tfoot', true).first();
8428         
8429         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8430             
8431             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8432             
8433             var total = this.ds.getTotalCount();
8434             
8435             if(this.footer.pageSize < total){
8436                 this.mainFoot.show();
8437             }
8438         }
8439         
8440         Roo.each(this.el.select('tbody td', true).elements, function(e){
8441             e.on('mouseover', _this.onMouseover, _this);
8442         });
8443         
8444         Roo.each(this.el.select('tbody td', true).elements, function(e){
8445             e.on('mouseout', _this.onMouseout, _this);
8446         });
8447         this.fireEvent('rowsrendered', this);
8448         
8449         this.autoSize();
8450     },
8451     
8452     
8453     onUpdate : function(ds,record)
8454     {
8455         this.refreshRow(record);
8456         this.autoSize();
8457     },
8458     
8459     onRemove : function(ds, record, index, isUpdate){
8460         if(isUpdate !== true){
8461             this.fireEvent("beforerowremoved", this, index, record);
8462         }
8463         var bt = this.mainBody.dom;
8464         
8465         var rows = this.el.select('tbody > tr', true).elements;
8466         
8467         if(typeof(rows[index]) != 'undefined'){
8468             bt.removeChild(rows[index].dom);
8469         }
8470         
8471 //        if(bt.rows[index]){
8472 //            bt.removeChild(bt.rows[index]);
8473 //        }
8474         
8475         if(isUpdate !== true){
8476             //this.stripeRows(index);
8477             //this.syncRowHeights(index, index);
8478             //this.layout();
8479             this.fireEvent("rowremoved", this, index, record);
8480         }
8481     },
8482     
8483     onAdd : function(ds, records, rowIndex)
8484     {
8485         //Roo.log('on Add called');
8486         // - note this does not handle multiple adding very well..
8487         var bt = this.mainBody.dom;
8488         for (var i =0 ; i < records.length;i++) {
8489             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8490             //Roo.log(records[i]);
8491             //Roo.log(this.store.getAt(rowIndex+i));
8492             this.insertRow(this.store, rowIndex + i, false);
8493             return;
8494         }
8495         
8496     },
8497     
8498     
8499     refreshRow : function(record){
8500         var ds = this.store, index;
8501         if(typeof record == 'number'){
8502             index = record;
8503             record = ds.getAt(index);
8504         }else{
8505             index = ds.indexOf(record);
8506             if (index < 0) {
8507                 return; // should not happen - but seems to 
8508             }
8509         }
8510         this.insertRow(ds, index, true);
8511         this.autoSize();
8512         this.onRemove(ds, record, index+1, true);
8513         this.autoSize();
8514         //this.syncRowHeights(index, index);
8515         //this.layout();
8516         this.fireEvent("rowupdated", this, index, record);
8517     },
8518     
8519     insertRow : function(dm, rowIndex, isUpdate){
8520         
8521         if(!isUpdate){
8522             this.fireEvent("beforerowsinserted", this, rowIndex);
8523         }
8524             //var s = this.getScrollState();
8525         var row = this.renderRow(this.cm, this.store, rowIndex);
8526         // insert before rowIndex..
8527         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8528         
8529         var _this = this;
8530                 
8531         if(row.cellObjects.length){
8532             Roo.each(row.cellObjects, function(r){
8533                 _this.renderCellObject(r);
8534             })
8535         }
8536             
8537         if(!isUpdate){
8538             this.fireEvent("rowsinserted", this, rowIndex);
8539             //this.syncRowHeights(firstRow, lastRow);
8540             //this.stripeRows(firstRow);
8541             //this.layout();
8542         }
8543         
8544     },
8545     
8546     
8547     getRowDom : function(rowIndex)
8548     {
8549         var rows = this.el.select('tbody > tr', true).elements;
8550         
8551         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8552         
8553     },
8554     // returns the object tree for a tr..
8555   
8556     
8557     renderRow : function(cm, ds, rowIndex) 
8558     {
8559         var d = ds.getAt(rowIndex);
8560         
8561         var row = {
8562             tag : 'tr',
8563             cls : 'x-row-' + rowIndex,
8564             cn : []
8565         };
8566             
8567         var cellObjects = [];
8568         
8569         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8570             var config = cm.config[i];
8571             
8572             var renderer = cm.getRenderer(i);
8573             var value = '';
8574             var id = false;
8575             
8576             if(typeof(renderer) !== 'undefined'){
8577                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8578             }
8579             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8580             // and are rendered into the cells after the row is rendered - using the id for the element.
8581             
8582             if(typeof(value) === 'object'){
8583                 id = Roo.id();
8584                 cellObjects.push({
8585                     container : id,
8586                     cfg : value 
8587                 })
8588             }
8589             
8590             var rowcfg = {
8591                 record: d,
8592                 rowIndex : rowIndex,
8593                 colIndex : i,
8594                 rowClass : ''
8595             };
8596
8597             this.fireEvent('rowclass', this, rowcfg);
8598             
8599             var td = {
8600                 tag: 'td',
8601                 cls : rowcfg.rowClass + ' x-col-' + i,
8602                 style: '',
8603                 html: (typeof(value) === 'object') ? '' : value
8604             };
8605             
8606             if (id) {
8607                 td.id = id;
8608             }
8609             
8610             if(typeof(config.colspan) != 'undefined'){
8611                 td.colspan = config.colspan;
8612             }
8613             
8614             if(typeof(config.hidden) != 'undefined' && config.hidden){
8615                 td.style += ' display:none;';
8616             }
8617             
8618             if(typeof(config.align) != 'undefined' && config.align.length){
8619                 td.style += ' text-align:' + config.align + ';';
8620             }
8621             if(typeof(config.valign) != 'undefined' && config.valign.length){
8622                 td.style += ' vertical-align:' + config.valign + ';';
8623             }
8624             
8625             if(typeof(config.width) != 'undefined'){
8626                 td.style += ' width:' +  config.width + 'px;';
8627             }
8628             
8629             if(typeof(config.cursor) != 'undefined'){
8630                 td.style += ' cursor:' +  config.cursor + ';';
8631             }
8632             
8633             if(typeof(config.cls) != 'undefined'){
8634                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8635             }
8636             
8637             ['xs','sm','md','lg'].map(function(size){
8638                 
8639                 if(typeof(config[size]) == 'undefined'){
8640                     return;
8641                 }
8642                 
8643                 
8644                   
8645                 if (!config[size]) { // 0 = hidden
8646                     // BS 4 '0' is treated as hide that column and below.
8647                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8648                     return;
8649                 }
8650                 
8651                 td.cls += ' col-' + size + '-' + config[size] + (
8652                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8653                 );
8654                  
8655
8656             });
8657             
8658             row.cn.push(td);
8659            
8660         }
8661         
8662         row.cellObjects = cellObjects;
8663         
8664         return row;
8665           
8666     },
8667     
8668     
8669     
8670     onBeforeLoad : function()
8671     {
8672         
8673     },
8674      /**
8675      * Remove all rows
8676      */
8677     clear : function()
8678     {
8679         this.el.select('tbody', true).first().dom.innerHTML = '';
8680     },
8681     /**
8682      * Show or hide a row.
8683      * @param {Number} rowIndex to show or hide
8684      * @param {Boolean} state hide
8685      */
8686     setRowVisibility : function(rowIndex, state)
8687     {
8688         var bt = this.mainBody.dom;
8689         
8690         var rows = this.el.select('tbody > tr', true).elements;
8691         
8692         if(typeof(rows[rowIndex]) == 'undefined'){
8693             return;
8694         }
8695         rows[rowIndex].dom.style.display = state ? '' : 'none';
8696     },
8697     
8698     
8699     getSelectionModel : function(){
8700         if(!this.selModel){
8701             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8702         }
8703         return this.selModel;
8704     },
8705     /*
8706      * Render the Roo.bootstrap object from renderder
8707      */
8708     renderCellObject : function(r)
8709     {
8710         var _this = this;
8711         
8712         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8713         
8714         var t = r.cfg.render(r.container);
8715         
8716         if(r.cfg.cn){
8717             Roo.each(r.cfg.cn, function(c){
8718                 var child = {
8719                     container: t.getChildContainer(),
8720                     cfg: c
8721                 };
8722                 _this.renderCellObject(child);
8723             })
8724         }
8725     },
8726     
8727     getRowIndex : function(row)
8728     {
8729         var rowIndex = -1;
8730         
8731         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8732             if(el != row){
8733                 return;
8734             }
8735             
8736             rowIndex = index;
8737         });
8738         
8739         return rowIndex;
8740     },
8741      /**
8742      * Returns the grid's underlying element = used by panel.Grid
8743      * @return {Element} The element
8744      */
8745     getGridEl : function(){
8746         return this.el;
8747     },
8748      /**
8749      * Forces a resize - used by panel.Grid
8750      * @return {Element} The element
8751      */
8752     autoSize : function()
8753     {
8754         //var ctr = Roo.get(this.container.dom.parentElement);
8755         var ctr = Roo.get(this.el.dom);
8756         
8757         var thd = this.getGridEl().select('thead',true).first();
8758         var tbd = this.getGridEl().select('tbody', true).first();
8759         var tfd = this.getGridEl().select('tfoot', true).first();
8760         
8761         var cw = ctr.getWidth();
8762         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8763         
8764         if (tbd) {
8765             
8766             tbd.setWidth(ctr.getWidth());
8767             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8768             // this needs fixing for various usage - currently only hydra job advers I think..
8769             //tdb.setHeight(
8770             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8771             //); 
8772             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8773             cw -= barsize;
8774         }
8775         cw = Math.max(cw, this.totalWidth);
8776         this.getGridEl().select('tbody tr',true).setWidth(cw);
8777         
8778         // resize 'expandable coloumn?
8779         
8780         return; // we doe not have a view in this design..
8781         
8782     },
8783     onBodyScroll: function()
8784     {
8785         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8786         if(this.mainHead){
8787             this.mainHead.setStyle({
8788                 'position' : 'relative',
8789                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8790             });
8791         }
8792         
8793         if(this.lazyLoad){
8794             
8795             var scrollHeight = this.mainBody.dom.scrollHeight;
8796             
8797             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8798             
8799             var height = this.mainBody.getHeight();
8800             
8801             if(scrollHeight - height == scrollTop) {
8802                 
8803                 var total = this.ds.getTotalCount();
8804                 
8805                 if(this.footer.cursor + this.footer.pageSize < total){
8806                     
8807                     this.footer.ds.load({
8808                         params : {
8809                             start : this.footer.cursor + this.footer.pageSize,
8810                             limit : this.footer.pageSize
8811                         },
8812                         add : true
8813                     });
8814                 }
8815             }
8816             
8817         }
8818     },
8819     
8820     onHeaderChange : function()
8821     {
8822         var header = this.renderHeader();
8823         var table = this.el.select('table', true).first();
8824         
8825         this.mainHead.remove();
8826         this.mainHead = table.createChild(header, this.mainBody, false);
8827     },
8828     
8829     onHiddenChange : function(colModel, colIndex, hidden)
8830     {
8831         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8832         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8833         
8834         this.CSS.updateRule(thSelector, "display", "");
8835         this.CSS.updateRule(tdSelector, "display", "");
8836         
8837         if(hidden){
8838             this.CSS.updateRule(thSelector, "display", "none");
8839             this.CSS.updateRule(tdSelector, "display", "none");
8840         }
8841         
8842         this.onHeaderChange();
8843         this.onLoad();
8844     },
8845     
8846     setColumnWidth: function(col_index, width)
8847     {
8848         // width = "md-2 xs-2..."
8849         if(!this.colModel.config[col_index]) {
8850             return;
8851         }
8852         
8853         var w = width.split(" ");
8854         
8855         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8856         
8857         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8858         
8859         
8860         for(var j = 0; j < w.length; j++) {
8861             
8862             if(!w[j]) {
8863                 continue;
8864             }
8865             
8866             var size_cls = w[j].split("-");
8867             
8868             if(!Number.isInteger(size_cls[1] * 1)) {
8869                 continue;
8870             }
8871             
8872             if(!this.colModel.config[col_index][size_cls[0]]) {
8873                 continue;
8874             }
8875             
8876             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8877                 continue;
8878             }
8879             
8880             h_row[0].classList.replace(
8881                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882                 "col-"+size_cls[0]+"-"+size_cls[1]
8883             );
8884             
8885             for(var i = 0; i < rows.length; i++) {
8886                 
8887                 var size_cls = w[j].split("-");
8888                 
8889                 if(!Number.isInteger(size_cls[1] * 1)) {
8890                     continue;
8891                 }
8892                 
8893                 if(!this.colModel.config[col_index][size_cls[0]]) {
8894                     continue;
8895                 }
8896                 
8897                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8898                     continue;
8899                 }
8900                 
8901                 rows[i].classList.replace(
8902                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8903                     "col-"+size_cls[0]+"-"+size_cls[1]
8904                 );
8905             }
8906             
8907             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8908         }
8909     }
8910 });
8911
8912  
8913
8914  /*
8915  * - LGPL
8916  *
8917  * table cell
8918  * 
8919  */
8920
8921 /**
8922  * @class Roo.bootstrap.TableCell
8923  * @extends Roo.bootstrap.Component
8924  * Bootstrap TableCell class
8925  * @cfg {String} html cell contain text
8926  * @cfg {String} cls cell class
8927  * @cfg {String} tag cell tag (td|th) default td
8928  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8929  * @cfg {String} align Aligns the content in a cell
8930  * @cfg {String} axis Categorizes cells
8931  * @cfg {String} bgcolor Specifies the background color of a cell
8932  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8933  * @cfg {Number} colspan Specifies the number of columns a cell should span
8934  * @cfg {String} headers Specifies one or more header cells a cell is related to
8935  * @cfg {Number} height Sets the height of a cell
8936  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8937  * @cfg {Number} rowspan Sets the number of rows a cell should span
8938  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8939  * @cfg {String} valign Vertical aligns the content in a cell
8940  * @cfg {Number} width Specifies the width of a cell
8941  * 
8942  * @constructor
8943  * Create a new TableCell
8944  * @param {Object} config The config object
8945  */
8946
8947 Roo.bootstrap.TableCell = function(config){
8948     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8949 };
8950
8951 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8952     
8953     html: false,
8954     cls: false,
8955     tag: false,
8956     abbr: false,
8957     align: false,
8958     axis: false,
8959     bgcolor: false,
8960     charoff: false,
8961     colspan: false,
8962     headers: false,
8963     height: false,
8964     nowrap: false,
8965     rowspan: false,
8966     scope: false,
8967     valign: false,
8968     width: false,
8969     
8970     
8971     getAutoCreate : function(){
8972         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8973         
8974         cfg = {
8975             tag: 'td'
8976         };
8977         
8978         if(this.tag){
8979             cfg.tag = this.tag;
8980         }
8981         
8982         if (this.html) {
8983             cfg.html=this.html
8984         }
8985         if (this.cls) {
8986             cfg.cls=this.cls
8987         }
8988         if (this.abbr) {
8989             cfg.abbr=this.abbr
8990         }
8991         if (this.align) {
8992             cfg.align=this.align
8993         }
8994         if (this.axis) {
8995             cfg.axis=this.axis
8996         }
8997         if (this.bgcolor) {
8998             cfg.bgcolor=this.bgcolor
8999         }
9000         if (this.charoff) {
9001             cfg.charoff=this.charoff
9002         }
9003         if (this.colspan) {
9004             cfg.colspan=this.colspan
9005         }
9006         if (this.headers) {
9007             cfg.headers=this.headers
9008         }
9009         if (this.height) {
9010             cfg.height=this.height
9011         }
9012         if (this.nowrap) {
9013             cfg.nowrap=this.nowrap
9014         }
9015         if (this.rowspan) {
9016             cfg.rowspan=this.rowspan
9017         }
9018         if (this.scope) {
9019             cfg.scope=this.scope
9020         }
9021         if (this.valign) {
9022             cfg.valign=this.valign
9023         }
9024         if (this.width) {
9025             cfg.width=this.width
9026         }
9027         
9028         
9029         return cfg;
9030     }
9031    
9032 });
9033
9034  
9035
9036  /*
9037  * - LGPL
9038  *
9039  * table row
9040  * 
9041  */
9042
9043 /**
9044  * @class Roo.bootstrap.TableRow
9045  * @extends Roo.bootstrap.Component
9046  * Bootstrap TableRow class
9047  * @cfg {String} cls row class
9048  * @cfg {String} align Aligns the content in a table row
9049  * @cfg {String} bgcolor Specifies a background color for a table row
9050  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9051  * @cfg {String} valign Vertical aligns the content in a table row
9052  * 
9053  * @constructor
9054  * Create a new TableRow
9055  * @param {Object} config The config object
9056  */
9057
9058 Roo.bootstrap.TableRow = function(config){
9059     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9060 };
9061
9062 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9063     
9064     cls: false,
9065     align: false,
9066     bgcolor: false,
9067     charoff: false,
9068     valign: false,
9069     
9070     getAutoCreate : function(){
9071         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9072         
9073         cfg = {
9074             tag: 'tr'
9075         };
9076             
9077         if(this.cls){
9078             cfg.cls = this.cls;
9079         }
9080         if(this.align){
9081             cfg.align = this.align;
9082         }
9083         if(this.bgcolor){
9084             cfg.bgcolor = this.bgcolor;
9085         }
9086         if(this.charoff){
9087             cfg.charoff = this.charoff;
9088         }
9089         if(this.valign){
9090             cfg.valign = this.valign;
9091         }
9092         
9093         return cfg;
9094     }
9095    
9096 });
9097
9098  
9099
9100  /*
9101  * - LGPL
9102  *
9103  * table body
9104  * 
9105  */
9106
9107 /**
9108  * @class Roo.bootstrap.TableBody
9109  * @extends Roo.bootstrap.Component
9110  * Bootstrap TableBody class
9111  * @cfg {String} cls element class
9112  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9113  * @cfg {String} align Aligns the content inside the element
9114  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9115  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9116  * 
9117  * @constructor
9118  * Create a new TableBody
9119  * @param {Object} config The config object
9120  */
9121
9122 Roo.bootstrap.TableBody = function(config){
9123     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9124 };
9125
9126 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9127     
9128     cls: false,
9129     tag: false,
9130     align: false,
9131     charoff: false,
9132     valign: false,
9133     
9134     getAutoCreate : function(){
9135         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9136         
9137         cfg = {
9138             tag: 'tbody'
9139         };
9140             
9141         if (this.cls) {
9142             cfg.cls=this.cls
9143         }
9144         if(this.tag){
9145             cfg.tag = this.tag;
9146         }
9147         
9148         if(this.align){
9149             cfg.align = this.align;
9150         }
9151         if(this.charoff){
9152             cfg.charoff = this.charoff;
9153         }
9154         if(this.valign){
9155             cfg.valign = this.valign;
9156         }
9157         
9158         return cfg;
9159     }
9160     
9161     
9162 //    initEvents : function()
9163 //    {
9164 //        
9165 //        if(!this.store){
9166 //            return;
9167 //        }
9168 //        
9169 //        this.store = Roo.factory(this.store, Roo.data);
9170 //        this.store.on('load', this.onLoad, this);
9171 //        
9172 //        this.store.load();
9173 //        
9174 //    },
9175 //    
9176 //    onLoad: function () 
9177 //    {   
9178 //        this.fireEvent('load', this);
9179 //    }
9180 //    
9181 //   
9182 });
9183
9184  
9185
9186  /*
9187  * Based on:
9188  * Ext JS Library 1.1.1
9189  * Copyright(c) 2006-2007, Ext JS, LLC.
9190  *
9191  * Originally Released Under LGPL - original licence link has changed is not relivant.
9192  *
9193  * Fork - LGPL
9194  * <script type="text/javascript">
9195  */
9196
9197 // as we use this in bootstrap.
9198 Roo.namespace('Roo.form');
9199  /**
9200  * @class Roo.form.Action
9201  * Internal Class used to handle form actions
9202  * @constructor
9203  * @param {Roo.form.BasicForm} el The form element or its id
9204  * @param {Object} config Configuration options
9205  */
9206
9207  
9208  
9209 // define the action interface
9210 Roo.form.Action = function(form, options){
9211     this.form = form;
9212     this.options = options || {};
9213 };
9214 /**
9215  * Client Validation Failed
9216  * @const 
9217  */
9218 Roo.form.Action.CLIENT_INVALID = 'client';
9219 /**
9220  * Server Validation Failed
9221  * @const 
9222  */
9223 Roo.form.Action.SERVER_INVALID = 'server';
9224  /**
9225  * Connect to Server Failed
9226  * @const 
9227  */
9228 Roo.form.Action.CONNECT_FAILURE = 'connect';
9229 /**
9230  * Reading Data from Server Failed
9231  * @const 
9232  */
9233 Roo.form.Action.LOAD_FAILURE = 'load';
9234
9235 Roo.form.Action.prototype = {
9236     type : 'default',
9237     failureType : undefined,
9238     response : undefined,
9239     result : undefined,
9240
9241     // interface method
9242     run : function(options){
9243
9244     },
9245
9246     // interface method
9247     success : function(response){
9248
9249     },
9250
9251     // interface method
9252     handleResponse : function(response){
9253
9254     },
9255
9256     // default connection failure
9257     failure : function(response){
9258         
9259         this.response = response;
9260         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9261         this.form.afterAction(this, false);
9262     },
9263
9264     processResponse : function(response){
9265         this.response = response;
9266         if(!response.responseText){
9267             return true;
9268         }
9269         this.result = this.handleResponse(response);
9270         return this.result;
9271     },
9272
9273     // utility functions used internally
9274     getUrl : function(appendParams){
9275         var url = this.options.url || this.form.url || this.form.el.dom.action;
9276         if(appendParams){
9277             var p = this.getParams();
9278             if(p){
9279                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9280             }
9281         }
9282         return url;
9283     },
9284
9285     getMethod : function(){
9286         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9287     },
9288
9289     getParams : function(){
9290         var bp = this.form.baseParams;
9291         var p = this.options.params;
9292         if(p){
9293             if(typeof p == "object"){
9294                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9295             }else if(typeof p == 'string' && bp){
9296                 p += '&' + Roo.urlEncode(bp);
9297             }
9298         }else if(bp){
9299             p = Roo.urlEncode(bp);
9300         }
9301         return p;
9302     },
9303
9304     createCallback : function(){
9305         return {
9306             success: this.success,
9307             failure: this.failure,
9308             scope: this,
9309             timeout: (this.form.timeout*1000),
9310             upload: this.form.fileUpload ? this.success : undefined
9311         };
9312     }
9313 };
9314
9315 Roo.form.Action.Submit = function(form, options){
9316     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9317 };
9318
9319 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9320     type : 'submit',
9321
9322     haveProgress : false,
9323     uploadComplete : false,
9324     
9325     // uploadProgress indicator.
9326     uploadProgress : function()
9327     {
9328         if (!this.form.progressUrl) {
9329             return;
9330         }
9331         
9332         if (!this.haveProgress) {
9333             Roo.MessageBox.progress("Uploading", "Uploading");
9334         }
9335         if (this.uploadComplete) {
9336            Roo.MessageBox.hide();
9337            return;
9338         }
9339         
9340         this.haveProgress = true;
9341    
9342         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9343         
9344         var c = new Roo.data.Connection();
9345         c.request({
9346             url : this.form.progressUrl,
9347             params: {
9348                 id : uid
9349             },
9350             method: 'GET',
9351             success : function(req){
9352                //console.log(data);
9353                 var rdata = false;
9354                 var edata;
9355                 try  {
9356                    rdata = Roo.decode(req.responseText)
9357                 } catch (e) {
9358                     Roo.log("Invalid data from server..");
9359                     Roo.log(edata);
9360                     return;
9361                 }
9362                 if (!rdata || !rdata.success) {
9363                     Roo.log(rdata);
9364                     Roo.MessageBox.alert(Roo.encode(rdata));
9365                     return;
9366                 }
9367                 var data = rdata.data;
9368                 
9369                 if (this.uploadComplete) {
9370                    Roo.MessageBox.hide();
9371                    return;
9372                 }
9373                    
9374                 if (data){
9375                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9376                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9377                     );
9378                 }
9379                 this.uploadProgress.defer(2000,this);
9380             },
9381        
9382             failure: function(data) {
9383                 Roo.log('progress url failed ');
9384                 Roo.log(data);
9385             },
9386             scope : this
9387         });
9388            
9389     },
9390     
9391     
9392     run : function()
9393     {
9394         // run get Values on the form, so it syncs any secondary forms.
9395         this.form.getValues();
9396         
9397         var o = this.options;
9398         var method = this.getMethod();
9399         var isPost = method == 'POST';
9400         if(o.clientValidation === false || this.form.isValid()){
9401             
9402             if (this.form.progressUrl) {
9403                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9404                     (new Date() * 1) + '' + Math.random());
9405                     
9406             } 
9407             
9408             
9409             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9410                 form:this.form.el.dom,
9411                 url:this.getUrl(!isPost),
9412                 method: method,
9413                 params:isPost ? this.getParams() : null,
9414                 isUpload: this.form.fileUpload,
9415                 formData : this.form.formData
9416             }));
9417             
9418             this.uploadProgress();
9419
9420         }else if (o.clientValidation !== false){ // client validation failed
9421             this.failureType = Roo.form.Action.CLIENT_INVALID;
9422             this.form.afterAction(this, false);
9423         }
9424     },
9425
9426     success : function(response)
9427     {
9428         this.uploadComplete= true;
9429         if (this.haveProgress) {
9430             Roo.MessageBox.hide();
9431         }
9432         
9433         
9434         var result = this.processResponse(response);
9435         if(result === true || result.success){
9436             this.form.afterAction(this, true);
9437             return;
9438         }
9439         if(result.errors){
9440             this.form.markInvalid(result.errors);
9441             this.failureType = Roo.form.Action.SERVER_INVALID;
9442         }
9443         this.form.afterAction(this, false);
9444     },
9445     failure : function(response)
9446     {
9447         this.uploadComplete= true;
9448         if (this.haveProgress) {
9449             Roo.MessageBox.hide();
9450         }
9451         
9452         this.response = response;
9453         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9454         this.form.afterAction(this, false);
9455     },
9456     
9457     handleResponse : function(response){
9458         if(this.form.errorReader){
9459             var rs = this.form.errorReader.read(response);
9460             var errors = [];
9461             if(rs.records){
9462                 for(var i = 0, len = rs.records.length; i < len; i++) {
9463                     var r = rs.records[i];
9464                     errors[i] = r.data;
9465                 }
9466             }
9467             if(errors.length < 1){
9468                 errors = null;
9469             }
9470             return {
9471                 success : rs.success,
9472                 errors : errors
9473             };
9474         }
9475         var ret = false;
9476         try {
9477             ret = Roo.decode(response.responseText);
9478         } catch (e) {
9479             ret = {
9480                 success: false,
9481                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9482                 errors : []
9483             };
9484         }
9485         return ret;
9486         
9487     }
9488 });
9489
9490
9491 Roo.form.Action.Load = function(form, options){
9492     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9493     this.reader = this.form.reader;
9494 };
9495
9496 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9497     type : 'load',
9498
9499     run : function(){
9500         
9501         Roo.Ajax.request(Roo.apply(
9502                 this.createCallback(), {
9503                     method:this.getMethod(),
9504                     url:this.getUrl(false),
9505                     params:this.getParams()
9506         }));
9507     },
9508
9509     success : function(response){
9510         
9511         var result = this.processResponse(response);
9512         if(result === true || !result.success || !result.data){
9513             this.failureType = Roo.form.Action.LOAD_FAILURE;
9514             this.form.afterAction(this, false);
9515             return;
9516         }
9517         this.form.clearInvalid();
9518         this.form.setValues(result.data);
9519         this.form.afterAction(this, true);
9520     },
9521
9522     handleResponse : function(response){
9523         if(this.form.reader){
9524             var rs = this.form.reader.read(response);
9525             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9526             return {
9527                 success : rs.success,
9528                 data : data
9529             };
9530         }
9531         return Roo.decode(response.responseText);
9532     }
9533 });
9534
9535 Roo.form.Action.ACTION_TYPES = {
9536     'load' : Roo.form.Action.Load,
9537     'submit' : Roo.form.Action.Submit
9538 };/*
9539  * - LGPL
9540  *
9541  * form
9542  *
9543  */
9544
9545 /**
9546  * @class Roo.bootstrap.Form
9547  * @extends Roo.bootstrap.Component
9548  * Bootstrap Form class
9549  * @cfg {String} method  GET | POST (default POST)
9550  * @cfg {String} labelAlign top | left (default top)
9551  * @cfg {String} align left  | right - for navbars
9552  * @cfg {Boolean} loadMask load mask when submit (default true)
9553
9554  *
9555  * @constructor
9556  * Create a new Form
9557  * @param {Object} config The config object
9558  */
9559
9560
9561 Roo.bootstrap.Form = function(config){
9562     
9563     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9564     
9565     Roo.bootstrap.Form.popover.apply();
9566     
9567     this.addEvents({
9568         /**
9569          * @event clientvalidation
9570          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9571          * @param {Form} this
9572          * @param {Boolean} valid true if the form has passed client-side validation
9573          */
9574         clientvalidation: true,
9575         /**
9576          * @event beforeaction
9577          * Fires before any action is performed. Return false to cancel the action.
9578          * @param {Form} this
9579          * @param {Action} action The action to be performed
9580          */
9581         beforeaction: true,
9582         /**
9583          * @event actionfailed
9584          * Fires when an action fails.
9585          * @param {Form} this
9586          * @param {Action} action The action that failed
9587          */
9588         actionfailed : true,
9589         /**
9590          * @event actioncomplete
9591          * Fires when an action is completed.
9592          * @param {Form} this
9593          * @param {Action} action The action that completed
9594          */
9595         actioncomplete : true
9596     });
9597 };
9598
9599 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9600
9601      /**
9602      * @cfg {String} method
9603      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9604      */
9605     method : 'POST',
9606     /**
9607      * @cfg {String} url
9608      * The URL to use for form actions if one isn't supplied in the action options.
9609      */
9610     /**
9611      * @cfg {Boolean} fileUpload
9612      * Set to true if this form is a file upload.
9613      */
9614
9615     /**
9616      * @cfg {Object} baseParams
9617      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9618      */
9619
9620     /**
9621      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9622      */
9623     timeout: 30,
9624     /**
9625      * @cfg {Sting} align (left|right) for navbar forms
9626      */
9627     align : 'left',
9628
9629     // private
9630     activeAction : null,
9631
9632     /**
9633      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9634      * element by passing it or its id or mask the form itself by passing in true.
9635      * @type Mixed
9636      */
9637     waitMsgTarget : false,
9638
9639     loadMask : true,
9640     
9641     /**
9642      * @cfg {Boolean} errorMask (true|false) default false
9643      */
9644     errorMask : false,
9645     
9646     /**
9647      * @cfg {Number} maskOffset Default 100
9648      */
9649     maskOffset : 100,
9650     
9651     /**
9652      * @cfg {Boolean} maskBody
9653      */
9654     maskBody : false,
9655
9656     getAutoCreate : function(){
9657
9658         var cfg = {
9659             tag: 'form',
9660             method : this.method || 'POST',
9661             id : this.id || Roo.id(),
9662             cls : ''
9663         };
9664         if (this.parent().xtype.match(/^Nav/)) {
9665             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9666
9667         }
9668
9669         if (this.labelAlign == 'left' ) {
9670             cfg.cls += ' form-horizontal';
9671         }
9672
9673
9674         return cfg;
9675     },
9676     initEvents : function()
9677     {
9678         this.el.on('submit', this.onSubmit, this);
9679         // this was added as random key presses on the form where triggering form submit.
9680         this.el.on('keypress', function(e) {
9681             if (e.getCharCode() != 13) {
9682                 return true;
9683             }
9684             // we might need to allow it for textareas.. and some other items.
9685             // check e.getTarget().
9686
9687             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9688                 return true;
9689             }
9690
9691             Roo.log("keypress blocked");
9692
9693             e.preventDefault();
9694             return false;
9695         });
9696         
9697     },
9698     // private
9699     onSubmit : function(e){
9700         e.stopEvent();
9701     },
9702
9703      /**
9704      * Returns true if client-side validation on the form is successful.
9705      * @return Boolean
9706      */
9707     isValid : function(){
9708         var items = this.getItems();
9709         var valid = true;
9710         var target = false;
9711         
9712         items.each(function(f){
9713             
9714             if(f.validate()){
9715                 return;
9716             }
9717             
9718             Roo.log('invalid field: ' + f.name);
9719             
9720             valid = false;
9721
9722             if(!target && f.el.isVisible(true)){
9723                 target = f;
9724             }
9725            
9726         });
9727         
9728         if(this.errorMask && !valid){
9729             Roo.bootstrap.Form.popover.mask(this, target);
9730         }
9731         
9732         return valid;
9733     },
9734     
9735     /**
9736      * Returns true if any fields in this form have changed since their original load.
9737      * @return Boolean
9738      */
9739     isDirty : function(){
9740         var dirty = false;
9741         var items = this.getItems();
9742         items.each(function(f){
9743            if(f.isDirty()){
9744                dirty = true;
9745                return false;
9746            }
9747            return true;
9748         });
9749         return dirty;
9750     },
9751      /**
9752      * Performs a predefined action (submit or load) or custom actions you define on this form.
9753      * @param {String} actionName The name of the action type
9754      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9755      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9756      * accept other config options):
9757      * <pre>
9758 Property          Type             Description
9759 ----------------  ---------------  ----------------------------------------------------------------------------------
9760 url               String           The url for the action (defaults to the form's url)
9761 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9762 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9763 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9764                                    validate the form on the client (defaults to false)
9765      * </pre>
9766      * @return {BasicForm} this
9767      */
9768     doAction : function(action, options){
9769         if(typeof action == 'string'){
9770             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9771         }
9772         if(this.fireEvent('beforeaction', this, action) !== false){
9773             this.beforeAction(action);
9774             action.run.defer(100, action);
9775         }
9776         return this;
9777     },
9778
9779     // private
9780     beforeAction : function(action){
9781         var o = action.options;
9782         
9783         if(this.loadMask){
9784             
9785             if(this.maskBody){
9786                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9787             } else {
9788                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9789             }
9790         }
9791         // not really supported yet.. ??
9792
9793         //if(this.waitMsgTarget === true){
9794         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9795         //}else if(this.waitMsgTarget){
9796         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9797         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9798         //}else {
9799         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9800        // }
9801
9802     },
9803
9804     // private
9805     afterAction : function(action, success){
9806         this.activeAction = null;
9807         var o = action.options;
9808
9809         if(this.loadMask){
9810             
9811             if(this.maskBody){
9812                 Roo.get(document.body).unmask();
9813             } else {
9814                 this.el.unmask();
9815             }
9816         }
9817         
9818         //if(this.waitMsgTarget === true){
9819 //            this.el.unmask();
9820         //}else if(this.waitMsgTarget){
9821         //    this.waitMsgTarget.unmask();
9822         //}else{
9823         //    Roo.MessageBox.updateProgress(1);
9824         //    Roo.MessageBox.hide();
9825        // }
9826         //
9827         if(success){
9828             if(o.reset){
9829                 this.reset();
9830             }
9831             Roo.callback(o.success, o.scope, [this, action]);
9832             this.fireEvent('actioncomplete', this, action);
9833
9834         }else{
9835
9836             // failure condition..
9837             // we have a scenario where updates need confirming.
9838             // eg. if a locking scenario exists..
9839             // we look for { errors : { needs_confirm : true }} in the response.
9840             if (
9841                 (typeof(action.result) != 'undefined')  &&
9842                 (typeof(action.result.errors) != 'undefined')  &&
9843                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9844            ){
9845                 var _t = this;
9846                 Roo.log("not supported yet");
9847                  /*
9848
9849                 Roo.MessageBox.confirm(
9850                     "Change requires confirmation",
9851                     action.result.errorMsg,
9852                     function(r) {
9853                         if (r != 'yes') {
9854                             return;
9855                         }
9856                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9857                     }
9858
9859                 );
9860                 */
9861
9862
9863                 return;
9864             }
9865
9866             Roo.callback(o.failure, o.scope, [this, action]);
9867             // show an error message if no failed handler is set..
9868             if (!this.hasListener('actionfailed')) {
9869                 Roo.log("need to add dialog support");
9870                 /*
9871                 Roo.MessageBox.alert("Error",
9872                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9873                         action.result.errorMsg :
9874                         "Saving Failed, please check your entries or try again"
9875                 );
9876                 */
9877             }
9878
9879             this.fireEvent('actionfailed', this, action);
9880         }
9881
9882     },
9883     /**
9884      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9885      * @param {String} id The value to search for
9886      * @return Field
9887      */
9888     findField : function(id){
9889         var items = this.getItems();
9890         var field = items.get(id);
9891         if(!field){
9892              items.each(function(f){
9893                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9894                     field = f;
9895                     return false;
9896                 }
9897                 return true;
9898             });
9899         }
9900         return field || null;
9901     },
9902      /**
9903      * Mark fields in this form invalid in bulk.
9904      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9905      * @return {BasicForm} this
9906      */
9907     markInvalid : function(errors){
9908         if(errors instanceof Array){
9909             for(var i = 0, len = errors.length; i < len; i++){
9910                 var fieldError = errors[i];
9911                 var f = this.findField(fieldError.id);
9912                 if(f){
9913                     f.markInvalid(fieldError.msg);
9914                 }
9915             }
9916         }else{
9917             var field, id;
9918             for(id in errors){
9919                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9920                     field.markInvalid(errors[id]);
9921                 }
9922             }
9923         }
9924         //Roo.each(this.childForms || [], function (f) {
9925         //    f.markInvalid(errors);
9926         //});
9927
9928         return this;
9929     },
9930
9931     /**
9932      * Set values for fields in this form in bulk.
9933      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9934      * @return {BasicForm} this
9935      */
9936     setValues : function(values){
9937         if(values instanceof Array){ // array of objects
9938             for(var i = 0, len = values.length; i < len; i++){
9939                 var v = values[i];
9940                 var f = this.findField(v.id);
9941                 if(f){
9942                     f.setValue(v.value);
9943                     if(this.trackResetOnLoad){
9944                         f.originalValue = f.getValue();
9945                     }
9946                 }
9947             }
9948         }else{ // object hash
9949             var field, id;
9950             for(id in values){
9951                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9952
9953                     if (field.setFromData &&
9954                         field.valueField &&
9955                         field.displayField &&
9956                         // combos' with local stores can
9957                         // be queried via setValue()
9958                         // to set their value..
9959                         (field.store && !field.store.isLocal)
9960                         ) {
9961                         // it's a combo
9962                         var sd = { };
9963                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9964                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9965                         field.setFromData(sd);
9966
9967                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9968                         
9969                         field.setFromData(values);
9970                         
9971                     } else {
9972                         field.setValue(values[id]);
9973                     }
9974
9975
9976                     if(this.trackResetOnLoad){
9977                         field.originalValue = field.getValue();
9978                     }
9979                 }
9980             }
9981         }
9982
9983         //Roo.each(this.childForms || [], function (f) {
9984         //    f.setValues(values);
9985         //});
9986
9987         return this;
9988     },
9989
9990     /**
9991      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9992      * they are returned as an array.
9993      * @param {Boolean} asString
9994      * @return {Object}
9995      */
9996     getValues : function(asString){
9997         //if (this.childForms) {
9998             // copy values from the child forms
9999         //    Roo.each(this.childForms, function (f) {
10000         //        this.setValues(f.getValues());
10001         //    }, this);
10002         //}
10003
10004
10005
10006         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10007         if(asString === true){
10008             return fs;
10009         }
10010         return Roo.urlDecode(fs);
10011     },
10012
10013     /**
10014      * Returns the fields in this form as an object with key/value pairs.
10015      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10016      * @return {Object}
10017      */
10018     getFieldValues : function(with_hidden)
10019     {
10020         var items = this.getItems();
10021         var ret = {};
10022         items.each(function(f){
10023             
10024             if (!f.getName()) {
10025                 return;
10026             }
10027             
10028             var v = f.getValue();
10029             
10030             if (f.inputType =='radio') {
10031                 if (typeof(ret[f.getName()]) == 'undefined') {
10032                     ret[f.getName()] = ''; // empty..
10033                 }
10034
10035                 if (!f.el.dom.checked) {
10036                     return;
10037
10038                 }
10039                 v = f.el.dom.value;
10040
10041             }
10042             
10043             if(f.xtype == 'MoneyField'){
10044                 ret[f.currencyName] = f.getCurrency();
10045             }
10046
10047             // not sure if this supported any more..
10048             if ((typeof(v) == 'object') && f.getRawValue) {
10049                 v = f.getRawValue() ; // dates..
10050             }
10051             // combo boxes where name != hiddenName...
10052             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10053                 ret[f.name] = f.getRawValue();
10054             }
10055             ret[f.getName()] = v;
10056         });
10057
10058         return ret;
10059     },
10060
10061     /**
10062      * Clears all invalid messages in this form.
10063      * @return {BasicForm} this
10064      */
10065     clearInvalid : function(){
10066         var items = this.getItems();
10067
10068         items.each(function(f){
10069            f.clearInvalid();
10070         });
10071
10072         return this;
10073     },
10074
10075     /**
10076      * Resets this form.
10077      * @return {BasicForm} this
10078      */
10079     reset : function(){
10080         var items = this.getItems();
10081         items.each(function(f){
10082             f.reset();
10083         });
10084
10085         Roo.each(this.childForms || [], function (f) {
10086             f.reset();
10087         });
10088
10089
10090         return this;
10091     },
10092     
10093     getItems : function()
10094     {
10095         var r=new Roo.util.MixedCollection(false, function(o){
10096             return o.id || (o.id = Roo.id());
10097         });
10098         var iter = function(el) {
10099             if (el.inputEl) {
10100                 r.add(el);
10101             }
10102             if (!el.items) {
10103                 return;
10104             }
10105             Roo.each(el.items,function(e) {
10106                 iter(e);
10107             });
10108         };
10109
10110         iter(this);
10111         return r;
10112     },
10113     
10114     hideFields : function(items)
10115     {
10116         Roo.each(items, function(i){
10117             
10118             var f = this.findField(i);
10119             
10120             if(!f){
10121                 return;
10122             }
10123             
10124             f.hide();
10125             
10126         }, this);
10127     },
10128     
10129     showFields : function(items)
10130     {
10131         Roo.each(items, function(i){
10132             
10133             var f = this.findField(i);
10134             
10135             if(!f){
10136                 return;
10137             }
10138             
10139             f.show();
10140             
10141         }, this);
10142     }
10143
10144 });
10145
10146 Roo.apply(Roo.bootstrap.Form, {
10147     
10148     popover : {
10149         
10150         padding : 5,
10151         
10152         isApplied : false,
10153         
10154         isMasked : false,
10155         
10156         form : false,
10157         
10158         target : false,
10159         
10160         toolTip : false,
10161         
10162         intervalID : false,
10163         
10164         maskEl : false,
10165         
10166         apply : function()
10167         {
10168             if(this.isApplied){
10169                 return;
10170             }
10171             
10172             this.maskEl = {
10173                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10174                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10175                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10176                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10177             };
10178             
10179             this.maskEl.top.enableDisplayMode("block");
10180             this.maskEl.left.enableDisplayMode("block");
10181             this.maskEl.bottom.enableDisplayMode("block");
10182             this.maskEl.right.enableDisplayMode("block");
10183             
10184             this.toolTip = new Roo.bootstrap.Tooltip({
10185                 cls : 'roo-form-error-popover',
10186                 alignment : {
10187                     'left' : ['r-l', [-2,0], 'right'],
10188                     'right' : ['l-r', [2,0], 'left'],
10189                     'bottom' : ['tl-bl', [0,2], 'top'],
10190                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10191                 }
10192             });
10193             
10194             this.toolTip.render(Roo.get(document.body));
10195
10196             this.toolTip.el.enableDisplayMode("block");
10197             
10198             Roo.get(document.body).on('click', function(){
10199                 this.unmask();
10200             }, this);
10201             
10202             Roo.get(document.body).on('touchstart', function(){
10203                 this.unmask();
10204             }, this);
10205             
10206             this.isApplied = true
10207         },
10208         
10209         mask : function(form, target)
10210         {
10211             this.form = form;
10212             
10213             this.target = target;
10214             
10215             if(!this.form.errorMask || !target.el){
10216                 return;
10217             }
10218             
10219             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10220             
10221             Roo.log(scrollable);
10222             
10223             var ot = this.target.el.calcOffsetsTo(scrollable);
10224             
10225             var scrollTo = ot[1] - this.form.maskOffset;
10226             
10227             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10228             
10229             scrollable.scrollTo('top', scrollTo);
10230             
10231             var box = this.target.el.getBox();
10232             Roo.log(box);
10233             var zIndex = Roo.bootstrap.Modal.zIndex++;
10234
10235             
10236             this.maskEl.top.setStyle('position', 'absolute');
10237             this.maskEl.top.setStyle('z-index', zIndex);
10238             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10239             this.maskEl.top.setLeft(0);
10240             this.maskEl.top.setTop(0);
10241             this.maskEl.top.show();
10242             
10243             this.maskEl.left.setStyle('position', 'absolute');
10244             this.maskEl.left.setStyle('z-index', zIndex);
10245             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10246             this.maskEl.left.setLeft(0);
10247             this.maskEl.left.setTop(box.y - this.padding);
10248             this.maskEl.left.show();
10249
10250             this.maskEl.bottom.setStyle('position', 'absolute');
10251             this.maskEl.bottom.setStyle('z-index', zIndex);
10252             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10253             this.maskEl.bottom.setLeft(0);
10254             this.maskEl.bottom.setTop(box.bottom + this.padding);
10255             this.maskEl.bottom.show();
10256
10257             this.maskEl.right.setStyle('position', 'absolute');
10258             this.maskEl.right.setStyle('z-index', zIndex);
10259             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10260             this.maskEl.right.setLeft(box.right + this.padding);
10261             this.maskEl.right.setTop(box.y - this.padding);
10262             this.maskEl.right.show();
10263
10264             this.toolTip.bindEl = this.target.el;
10265
10266             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10267
10268             var tip = this.target.blankText;
10269
10270             if(this.target.getValue() !== '' ) {
10271                 
10272                 if (this.target.invalidText.length) {
10273                     tip = this.target.invalidText;
10274                 } else if (this.target.regexText.length){
10275                     tip = this.target.regexText;
10276                 }
10277             }
10278
10279             this.toolTip.show(tip);
10280
10281             this.intervalID = window.setInterval(function() {
10282                 Roo.bootstrap.Form.popover.unmask();
10283             }, 10000);
10284
10285             window.onwheel = function(){ return false;};
10286             
10287             (function(){ this.isMasked = true; }).defer(500, this);
10288             
10289         },
10290         
10291         unmask : function()
10292         {
10293             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10294                 return;
10295             }
10296             
10297             this.maskEl.top.setStyle('position', 'absolute');
10298             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10299             this.maskEl.top.hide();
10300
10301             this.maskEl.left.setStyle('position', 'absolute');
10302             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10303             this.maskEl.left.hide();
10304
10305             this.maskEl.bottom.setStyle('position', 'absolute');
10306             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10307             this.maskEl.bottom.hide();
10308
10309             this.maskEl.right.setStyle('position', 'absolute');
10310             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10311             this.maskEl.right.hide();
10312             
10313             this.toolTip.hide();
10314             
10315             this.toolTip.el.hide();
10316             
10317             window.onwheel = function(){ return true;};
10318             
10319             if(this.intervalID){
10320                 window.clearInterval(this.intervalID);
10321                 this.intervalID = false;
10322             }
10323             
10324             this.isMasked = false;
10325             
10326         }
10327         
10328     }
10329     
10330 });
10331
10332 /*
10333  * Based on:
10334  * Ext JS Library 1.1.1
10335  * Copyright(c) 2006-2007, Ext JS, LLC.
10336  *
10337  * Originally Released Under LGPL - original licence link has changed is not relivant.
10338  *
10339  * Fork - LGPL
10340  * <script type="text/javascript">
10341  */
10342 /**
10343  * @class Roo.form.VTypes
10344  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10345  * @singleton
10346  */
10347 Roo.form.VTypes = function(){
10348     // closure these in so they are only created once.
10349     var alpha = /^[a-zA-Z_]+$/;
10350     var alphanum = /^[a-zA-Z0-9_]+$/;
10351     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10352     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10353
10354     // All these messages and functions are configurable
10355     return {
10356         /**
10357          * The function used to validate email addresses
10358          * @param {String} value The email address
10359          */
10360         'email' : function(v){
10361             return email.test(v);
10362         },
10363         /**
10364          * The error text to display when the email validation function returns false
10365          * @type String
10366          */
10367         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10368         /**
10369          * The keystroke filter mask to be applied on email input
10370          * @type RegExp
10371          */
10372         'emailMask' : /[a-z0-9_\.\-@]/i,
10373
10374         /**
10375          * The function used to validate URLs
10376          * @param {String} value The URL
10377          */
10378         'url' : function(v){
10379             return url.test(v);
10380         },
10381         /**
10382          * The error text to display when the url validation function returns false
10383          * @type String
10384          */
10385         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10386         
10387         /**
10388          * The function used to validate alpha values
10389          * @param {String} value The value
10390          */
10391         'alpha' : function(v){
10392             return alpha.test(v);
10393         },
10394         /**
10395          * The error text to display when the alpha validation function returns false
10396          * @type String
10397          */
10398         'alphaText' : 'This field should only contain letters and _',
10399         /**
10400          * The keystroke filter mask to be applied on alpha input
10401          * @type RegExp
10402          */
10403         'alphaMask' : /[a-z_]/i,
10404
10405         /**
10406          * The function used to validate alphanumeric values
10407          * @param {String} value The value
10408          */
10409         'alphanum' : function(v){
10410             return alphanum.test(v);
10411         },
10412         /**
10413          * The error text to display when the alphanumeric validation function returns false
10414          * @type String
10415          */
10416         'alphanumText' : 'This field should only contain letters, numbers and _',
10417         /**
10418          * The keystroke filter mask to be applied on alphanumeric input
10419          * @type RegExp
10420          */
10421         'alphanumMask' : /[a-z0-9_]/i
10422     };
10423 }();/*
10424  * - LGPL
10425  *
10426  * Input
10427  * 
10428  */
10429
10430 /**
10431  * @class Roo.bootstrap.Input
10432  * @extends Roo.bootstrap.Component
10433  * Bootstrap Input class
10434  * @cfg {Boolean} disabled is it disabled
10435  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10436  * @cfg {String} name name of the input
10437  * @cfg {string} fieldLabel - the label associated
10438  * @cfg {string} placeholder - placeholder to put in text.
10439  * @cfg {string}  before - input group add on before
10440  * @cfg {string} after - input group add on after
10441  * @cfg {string} size - (lg|sm) or leave empty..
10442  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10443  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10444  * @cfg {Number} md colspan out of 12 for computer-sized screens
10445  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10446  * @cfg {string} value default value of the input
10447  * @cfg {Number} labelWidth set the width of label 
10448  * @cfg {Number} labellg set the width of label (1-12)
10449  * @cfg {Number} labelmd set the width of label (1-12)
10450  * @cfg {Number} labelsm set the width of label (1-12)
10451  * @cfg {Number} labelxs set the width of label (1-12)
10452  * @cfg {String} labelAlign (top|left)
10453  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10454  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10455  * @cfg {String} indicatorpos (left|right) default left
10456  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10457  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10458  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10459
10460  * @cfg {String} align (left|center|right) Default left
10461  * @cfg {Boolean} forceFeedback (true|false) Default false
10462  * 
10463  * @constructor
10464  * Create a new Input
10465  * @param {Object} config The config object
10466  */
10467
10468 Roo.bootstrap.Input = function(config){
10469     
10470     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10471     
10472     this.addEvents({
10473         /**
10474          * @event focus
10475          * Fires when this field receives input focus.
10476          * @param {Roo.form.Field} this
10477          */
10478         focus : true,
10479         /**
10480          * @event blur
10481          * Fires when this field loses input focus.
10482          * @param {Roo.form.Field} this
10483          */
10484         blur : true,
10485         /**
10486          * @event specialkey
10487          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10488          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10489          * @param {Roo.form.Field} this
10490          * @param {Roo.EventObject} e The event object
10491          */
10492         specialkey : true,
10493         /**
10494          * @event change
10495          * Fires just before the field blurs if the field value has changed.
10496          * @param {Roo.form.Field} this
10497          * @param {Mixed} newValue The new value
10498          * @param {Mixed} oldValue The original value
10499          */
10500         change : true,
10501         /**
10502          * @event invalid
10503          * Fires after the field has been marked as invalid.
10504          * @param {Roo.form.Field} this
10505          * @param {String} msg The validation message
10506          */
10507         invalid : true,
10508         /**
10509          * @event valid
10510          * Fires after the field has been validated with no errors.
10511          * @param {Roo.form.Field} this
10512          */
10513         valid : true,
10514          /**
10515          * @event keyup
10516          * Fires after the key up
10517          * @param {Roo.form.Field} this
10518          * @param {Roo.EventObject}  e The event Object
10519          */
10520         keyup : true,
10521         /**
10522          * @event paste
10523          * Fires after the user pastes into input
10524          * @param {Roo.form.Field} this
10525          * @param {Roo.EventObject}  e The event Object
10526          */
10527         paste : true
10528     });
10529 };
10530
10531 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10532      /**
10533      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10534       automatic validation (defaults to "keyup").
10535      */
10536     validationEvent : "keyup",
10537      /**
10538      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10539      */
10540     validateOnBlur : true,
10541     /**
10542      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10543      */
10544     validationDelay : 250,
10545      /**
10546      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10547      */
10548     focusClass : "x-form-focus",  // not needed???
10549     
10550        
10551     /**
10552      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10553      */
10554     invalidClass : "has-warning",
10555     
10556     /**
10557      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10558      */
10559     validClass : "has-success",
10560     
10561     /**
10562      * @cfg {Boolean} hasFeedback (true|false) default true
10563      */
10564     hasFeedback : true,
10565     
10566     /**
10567      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10568      */
10569     invalidFeedbackClass : "glyphicon-warning-sign",
10570     
10571     /**
10572      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10573      */
10574     validFeedbackClass : "glyphicon-ok",
10575     
10576     /**
10577      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10578      */
10579     selectOnFocus : false,
10580     
10581      /**
10582      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10583      */
10584     maskRe : null,
10585        /**
10586      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10587      */
10588     vtype : null,
10589     
10590       /**
10591      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10592      */
10593     disableKeyFilter : false,
10594     
10595        /**
10596      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10597      */
10598     disabled : false,
10599      /**
10600      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10601      */
10602     allowBlank : true,
10603     /**
10604      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10605      */
10606     blankText : "Please complete this mandatory field",
10607     
10608      /**
10609      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10610      */
10611     minLength : 0,
10612     /**
10613      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10614      */
10615     maxLength : Number.MAX_VALUE,
10616     /**
10617      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10618      */
10619     minLengthText : "The minimum length for this field is {0}",
10620     /**
10621      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10622      */
10623     maxLengthText : "The maximum length for this field is {0}",
10624   
10625     
10626     /**
10627      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10628      * If available, this function will be called only after the basic validators all return true, and will be passed the
10629      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10630      */
10631     validator : null,
10632     /**
10633      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10634      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10635      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10636      */
10637     regex : null,
10638     /**
10639      * @cfg {String} regexText -- Depricated - use Invalid Text
10640      */
10641     regexText : "",
10642     
10643     /**
10644      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10645      */
10646     invalidText : "",
10647     
10648     
10649     
10650     autocomplete: false,
10651     
10652     
10653     fieldLabel : '',
10654     inputType : 'text',
10655     
10656     name : false,
10657     placeholder: false,
10658     before : false,
10659     after : false,
10660     size : false,
10661     hasFocus : false,
10662     preventMark: false,
10663     isFormField : true,
10664     value : '',
10665     labelWidth : 2,
10666     labelAlign : false,
10667     readOnly : false,
10668     align : false,
10669     formatedValue : false,
10670     forceFeedback : false,
10671     
10672     indicatorpos : 'left',
10673     
10674     labellg : 0,
10675     labelmd : 0,
10676     labelsm : 0,
10677     labelxs : 0,
10678     
10679     capture : '',
10680     accept : '',
10681     
10682     parentLabelAlign : function()
10683     {
10684         var parent = this;
10685         while (parent.parent()) {
10686             parent = parent.parent();
10687             if (typeof(parent.labelAlign) !='undefined') {
10688                 return parent.labelAlign;
10689             }
10690         }
10691         return 'left';
10692         
10693     },
10694     
10695     getAutoCreate : function()
10696     {
10697         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10698         
10699         var id = Roo.id();
10700         
10701         var cfg = {};
10702         
10703         if(this.inputType != 'hidden'){
10704             cfg.cls = 'form-group' //input-group
10705         }
10706         
10707         var input =  {
10708             tag: 'input',
10709             id : id,
10710             type : this.inputType,
10711             value : this.value,
10712             cls : 'form-control',
10713             placeholder : this.placeholder || '',
10714             autocomplete : this.autocomplete || 'new-password'
10715         };
10716         if (this.inputType == 'file') {
10717             input.style = 'overflow:hidden'; // why not in CSS?
10718         }
10719         
10720         if(this.capture.length){
10721             input.capture = this.capture;
10722         }
10723         
10724         if(this.accept.length){
10725             input.accept = this.accept + "/*";
10726         }
10727         
10728         if(this.align){
10729             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10730         }
10731         
10732         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10733             input.maxLength = this.maxLength;
10734         }
10735         
10736         if (this.disabled) {
10737             input.disabled=true;
10738         }
10739         
10740         if (this.readOnly) {
10741             input.readonly=true;
10742         }
10743         
10744         if (this.name) {
10745             input.name = this.name;
10746         }
10747         
10748         if (this.size) {
10749             input.cls += ' input-' + this.size;
10750         }
10751         
10752         var settings=this;
10753         ['xs','sm','md','lg'].map(function(size){
10754             if (settings[size]) {
10755                 cfg.cls += ' col-' + size + '-' + settings[size];
10756             }
10757         });
10758         
10759         var inputblock = input;
10760         
10761         var feedback = {
10762             tag: 'span',
10763             cls: 'glyphicon form-control-feedback'
10764         };
10765             
10766         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10767             
10768             inputblock = {
10769                 cls : 'has-feedback',
10770                 cn :  [
10771                     input,
10772                     feedback
10773                 ] 
10774             };  
10775         }
10776         
10777         if (this.before || this.after) {
10778             
10779             inputblock = {
10780                 cls : 'input-group',
10781                 cn :  [] 
10782             };
10783             
10784             if (this.before && typeof(this.before) == 'string') {
10785                 
10786                 inputblock.cn.push({
10787                     tag :'span',
10788                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10789                     html : this.before
10790                 });
10791             }
10792             if (this.before && typeof(this.before) == 'object') {
10793                 this.before = Roo.factory(this.before);
10794                 
10795                 inputblock.cn.push({
10796                     tag :'span',
10797                     cls : 'roo-input-before input-group-prepend   input-group-' +
10798                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10799                 });
10800             }
10801             
10802             inputblock.cn.push(input);
10803             
10804             if (this.after && typeof(this.after) == 'string') {
10805                 inputblock.cn.push({
10806                     tag :'span',
10807                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10808                     html : this.after
10809                 });
10810             }
10811             if (this.after && typeof(this.after) == 'object') {
10812                 this.after = Roo.factory(this.after);
10813                 
10814                 inputblock.cn.push({
10815                     tag :'span',
10816                     cls : 'roo-input-after input-group-append  input-group-' +
10817                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10818                 });
10819             }
10820             
10821             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10822                 inputblock.cls += ' has-feedback';
10823                 inputblock.cn.push(feedback);
10824             }
10825         };
10826         var indicator = {
10827             tag : 'i',
10828             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10829             tooltip : 'This field is required'
10830         };
10831         if (this.allowBlank ) {
10832             indicator.style = this.allowBlank ? ' display:none' : '';
10833         }
10834         if (align ==='left' && this.fieldLabel.length) {
10835             
10836             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10837             
10838             cfg.cn = [
10839                 indicator,
10840                 {
10841                     tag: 'label',
10842                     'for' :  id,
10843                     cls : 'control-label col-form-label',
10844                     html : this.fieldLabel
10845
10846                 },
10847                 {
10848                     cls : "", 
10849                     cn: [
10850                         inputblock
10851                     ]
10852                 }
10853             ];
10854             
10855             var labelCfg = cfg.cn[1];
10856             var contentCfg = cfg.cn[2];
10857             
10858             if(this.indicatorpos == 'right'){
10859                 cfg.cn = [
10860                     {
10861                         tag: 'label',
10862                         'for' :  id,
10863                         cls : 'control-label col-form-label',
10864                         cn : [
10865                             {
10866                                 tag : 'span',
10867                                 html : this.fieldLabel
10868                             },
10869                             indicator
10870                         ]
10871                     },
10872                     {
10873                         cls : "",
10874                         cn: [
10875                             inputblock
10876                         ]
10877                     }
10878
10879                 ];
10880                 
10881                 labelCfg = cfg.cn[0];
10882                 contentCfg = cfg.cn[1];
10883             
10884             }
10885             
10886             if(this.labelWidth > 12){
10887                 labelCfg.style = "width: " + this.labelWidth + 'px';
10888             }
10889             
10890             if(this.labelWidth < 13 && this.labelmd == 0){
10891                 this.labelmd = this.labelWidth;
10892             }
10893             
10894             if(this.labellg > 0){
10895                 labelCfg.cls += ' col-lg-' + this.labellg;
10896                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10897             }
10898             
10899             if(this.labelmd > 0){
10900                 labelCfg.cls += ' col-md-' + this.labelmd;
10901                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10902             }
10903             
10904             if(this.labelsm > 0){
10905                 labelCfg.cls += ' col-sm-' + this.labelsm;
10906                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10907             }
10908             
10909             if(this.labelxs > 0){
10910                 labelCfg.cls += ' col-xs-' + this.labelxs;
10911                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10912             }
10913             
10914             
10915         } else if ( this.fieldLabel.length) {
10916                 
10917             
10918             
10919             cfg.cn = [
10920                 {
10921                     tag : 'i',
10922                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10923                     tooltip : 'This field is required',
10924                     style : this.allowBlank ? ' display:none' : '' 
10925                 },
10926                 {
10927                     tag: 'label',
10928                    //cls : 'input-group-addon',
10929                     html : this.fieldLabel
10930
10931                 },
10932
10933                inputblock
10934
10935            ];
10936            
10937            if(this.indicatorpos == 'right'){
10938        
10939                 cfg.cn = [
10940                     {
10941                         tag: 'label',
10942                        //cls : 'input-group-addon',
10943                         html : this.fieldLabel
10944
10945                     },
10946                     {
10947                         tag : 'i',
10948                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10949                         tooltip : 'This field is required',
10950                         style : this.allowBlank ? ' display:none' : '' 
10951                     },
10952
10953                    inputblock
10954
10955                ];
10956
10957             }
10958
10959         } else {
10960             
10961             cfg.cn = [
10962
10963                     inputblock
10964
10965             ];
10966                 
10967                 
10968         };
10969         
10970         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10971            cfg.cls += ' navbar-form';
10972         }
10973         
10974         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10975             // on BS4 we do this only if not form 
10976             cfg.cls += ' navbar-form';
10977             cfg.tag = 'li';
10978         }
10979         
10980         return cfg;
10981         
10982     },
10983     /**
10984      * return the real input element.
10985      */
10986     inputEl: function ()
10987     {
10988         return this.el.select('input.form-control',true).first();
10989     },
10990     
10991     tooltipEl : function()
10992     {
10993         return this.inputEl();
10994     },
10995     
10996     indicatorEl : function()
10997     {
10998         if (Roo.bootstrap.version == 4) {
10999             return false; // not enabled in v4 yet.
11000         }
11001         
11002         var indicator = this.el.select('i.roo-required-indicator',true).first();
11003         
11004         if(!indicator){
11005             return false;
11006         }
11007         
11008         return indicator;
11009         
11010     },
11011     
11012     setDisabled : function(v)
11013     {
11014         var i  = this.inputEl().dom;
11015         if (!v) {
11016             i.removeAttribute('disabled');
11017             return;
11018             
11019         }
11020         i.setAttribute('disabled','true');
11021     },
11022     initEvents : function()
11023     {
11024           
11025         this.inputEl().on("keydown" , this.fireKey,  this);
11026         this.inputEl().on("focus", this.onFocus,  this);
11027         this.inputEl().on("blur", this.onBlur,  this);
11028         
11029         this.inputEl().relayEvent('keyup', this);
11030         this.inputEl().relayEvent('paste', this);
11031         
11032         this.indicator = this.indicatorEl();
11033         
11034         if(this.indicator){
11035             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11036         }
11037  
11038         // reference to original value for reset
11039         this.originalValue = this.getValue();
11040         //Roo.form.TextField.superclass.initEvents.call(this);
11041         if(this.validationEvent == 'keyup'){
11042             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11043             this.inputEl().on('keyup', this.filterValidation, this);
11044         }
11045         else if(this.validationEvent !== false){
11046             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11047         }
11048         
11049         if(this.selectOnFocus){
11050             this.on("focus", this.preFocus, this);
11051             
11052         }
11053         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11054             this.inputEl().on("keypress", this.filterKeys, this);
11055         } else {
11056             this.inputEl().relayEvent('keypress', this);
11057         }
11058        /* if(this.grow){
11059             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11060             this.el.on("click", this.autoSize,  this);
11061         }
11062         */
11063         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11064             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11065         }
11066         
11067         if (typeof(this.before) == 'object') {
11068             this.before.render(this.el.select('.roo-input-before',true).first());
11069         }
11070         if (typeof(this.after) == 'object') {
11071             this.after.render(this.el.select('.roo-input-after',true).first());
11072         }
11073         
11074         this.inputEl().on('change', this.onChange, this);
11075         
11076     },
11077     filterValidation : function(e){
11078         if(!e.isNavKeyPress()){
11079             this.validationTask.delay(this.validationDelay);
11080         }
11081     },
11082      /**
11083      * Validates the field value
11084      * @return {Boolean} True if the value is valid, else false
11085      */
11086     validate : function(){
11087         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11088         if(this.disabled || this.validateValue(this.getRawValue())){
11089             this.markValid();
11090             return true;
11091         }
11092         
11093         this.markInvalid();
11094         return false;
11095     },
11096     
11097     
11098     /**
11099      * Validates a value according to the field's validation rules and marks the field as invalid
11100      * if the validation fails
11101      * @param {Mixed} value The value to validate
11102      * @return {Boolean} True if the value is valid, else false
11103      */
11104     validateValue : function(value)
11105     {
11106         if(this.getVisibilityEl().hasClass('hidden')){
11107             return true;
11108         }
11109         
11110         if(value.length < 1)  { // if it's blank
11111             if(this.allowBlank){
11112                 return true;
11113             }
11114             return false;
11115         }
11116         
11117         if(value.length < this.minLength){
11118             return false;
11119         }
11120         if(value.length > this.maxLength){
11121             return false;
11122         }
11123         if(this.vtype){
11124             var vt = Roo.form.VTypes;
11125             if(!vt[this.vtype](value, this)){
11126                 return false;
11127             }
11128         }
11129         if(typeof this.validator == "function"){
11130             var msg = this.validator(value);
11131             if(msg !== true){
11132                 return false;
11133             }
11134             if (typeof(msg) == 'string') {
11135                 this.invalidText = msg;
11136             }
11137         }
11138         
11139         if(this.regex && !this.regex.test(value)){
11140             return false;
11141         }
11142         
11143         return true;
11144     },
11145     
11146      // private
11147     fireKey : function(e){
11148         //Roo.log('field ' + e.getKey());
11149         if(e.isNavKeyPress()){
11150             this.fireEvent("specialkey", this, e);
11151         }
11152     },
11153     focus : function (selectText){
11154         if(this.rendered){
11155             this.inputEl().focus();
11156             if(selectText === true){
11157                 this.inputEl().dom.select();
11158             }
11159         }
11160         return this;
11161     } ,
11162     
11163     onFocus : function(){
11164         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11165            // this.el.addClass(this.focusClass);
11166         }
11167         if(!this.hasFocus){
11168             this.hasFocus = true;
11169             this.startValue = this.getValue();
11170             this.fireEvent("focus", this);
11171         }
11172     },
11173     
11174     beforeBlur : Roo.emptyFn,
11175
11176     
11177     // private
11178     onBlur : function(){
11179         this.beforeBlur();
11180         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11181             //this.el.removeClass(this.focusClass);
11182         }
11183         this.hasFocus = false;
11184         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11185             this.validate();
11186         }
11187         var v = this.getValue();
11188         if(String(v) !== String(this.startValue)){
11189             this.fireEvent('change', this, v, this.startValue);
11190         }
11191         this.fireEvent("blur", this);
11192     },
11193     
11194     onChange : function(e)
11195     {
11196         var v = this.getValue();
11197         if(String(v) !== String(this.startValue)){
11198             this.fireEvent('change', this, v, this.startValue);
11199         }
11200         
11201     },
11202     
11203     /**
11204      * Resets the current field value to the originally loaded value and clears any validation messages
11205      */
11206     reset : function(){
11207         this.setValue(this.originalValue);
11208         this.validate();
11209     },
11210      /**
11211      * Returns the name of the field
11212      * @return {Mixed} name The name field
11213      */
11214     getName: function(){
11215         return this.name;
11216     },
11217      /**
11218      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11219      * @return {Mixed} value The field value
11220      */
11221     getValue : function(){
11222         
11223         var v = this.inputEl().getValue();
11224         
11225         return v;
11226     },
11227     /**
11228      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11229      * @return {Mixed} value The field value
11230      */
11231     getRawValue : function(){
11232         var v = this.inputEl().getValue();
11233         
11234         return v;
11235     },
11236     
11237     /**
11238      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11239      * @param {Mixed} value The value to set
11240      */
11241     setRawValue : function(v){
11242         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11243     },
11244     
11245     selectText : function(start, end){
11246         var v = this.getRawValue();
11247         if(v.length > 0){
11248             start = start === undefined ? 0 : start;
11249             end = end === undefined ? v.length : end;
11250             var d = this.inputEl().dom;
11251             if(d.setSelectionRange){
11252                 d.setSelectionRange(start, end);
11253             }else if(d.createTextRange){
11254                 var range = d.createTextRange();
11255                 range.moveStart("character", start);
11256                 range.moveEnd("character", v.length-end);
11257                 range.select();
11258             }
11259         }
11260     },
11261     
11262     /**
11263      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11264      * @param {Mixed} value The value to set
11265      */
11266     setValue : function(v){
11267         this.value = v;
11268         if(this.rendered){
11269             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11270             this.validate();
11271         }
11272     },
11273     
11274     /*
11275     processValue : function(value){
11276         if(this.stripCharsRe){
11277             var newValue = value.replace(this.stripCharsRe, '');
11278             if(newValue !== value){
11279                 this.setRawValue(newValue);
11280                 return newValue;
11281             }
11282         }
11283         return value;
11284     },
11285   */
11286     preFocus : function(){
11287         
11288         if(this.selectOnFocus){
11289             this.inputEl().dom.select();
11290         }
11291     },
11292     filterKeys : function(e){
11293         var k = e.getKey();
11294         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11295             return;
11296         }
11297         var c = e.getCharCode(), cc = String.fromCharCode(c);
11298         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11299             return;
11300         }
11301         if(!this.maskRe.test(cc)){
11302             e.stopEvent();
11303         }
11304     },
11305      /**
11306      * Clear any invalid styles/messages for this field
11307      */
11308     clearInvalid : function(){
11309         
11310         if(!this.el || this.preventMark){ // not rendered
11311             return;
11312         }
11313         
11314         
11315         this.el.removeClass([this.invalidClass, 'is-invalid']);
11316         
11317         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11318             
11319             var feedback = this.el.select('.form-control-feedback', true).first();
11320             
11321             if(feedback){
11322                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11323             }
11324             
11325         }
11326         
11327         if(this.indicator){
11328             this.indicator.removeClass('visible');
11329             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11330         }
11331         
11332         this.fireEvent('valid', this);
11333     },
11334     
11335      /**
11336      * Mark this field as valid
11337      */
11338     markValid : function()
11339     {
11340         if(!this.el  || this.preventMark){ // not rendered...
11341             return;
11342         }
11343         
11344         this.el.removeClass([this.invalidClass, this.validClass]);
11345         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11346
11347         var feedback = this.el.select('.form-control-feedback', true).first();
11348             
11349         if(feedback){
11350             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11351         }
11352         
11353         if(this.indicator){
11354             this.indicator.removeClass('visible');
11355             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11356         }
11357         
11358         if(this.disabled){
11359             return;
11360         }
11361         
11362            
11363         if(this.allowBlank && !this.getRawValue().length){
11364             return;
11365         }
11366         if (Roo.bootstrap.version == 3) {
11367             this.el.addClass(this.validClass);
11368         } else {
11369             this.inputEl().addClass('is-valid');
11370         }
11371
11372         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11373             
11374             var feedback = this.el.select('.form-control-feedback', true).first();
11375             
11376             if(feedback){
11377                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11378                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11379             }
11380             
11381         }
11382         
11383         this.fireEvent('valid', this);
11384     },
11385     
11386      /**
11387      * Mark this field as invalid
11388      * @param {String} msg The validation message
11389      */
11390     markInvalid : function(msg)
11391     {
11392         if(!this.el  || this.preventMark){ // not rendered
11393             return;
11394         }
11395         
11396         this.el.removeClass([this.invalidClass, this.validClass]);
11397         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11398         
11399         var feedback = this.el.select('.form-control-feedback', true).first();
11400             
11401         if(feedback){
11402             this.el.select('.form-control-feedback', true).first().removeClass(
11403                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11404         }
11405
11406         if(this.disabled){
11407             return;
11408         }
11409         
11410         if(this.allowBlank && !this.getRawValue().length){
11411             return;
11412         }
11413         
11414         if(this.indicator){
11415             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11416             this.indicator.addClass('visible');
11417         }
11418         if (Roo.bootstrap.version == 3) {
11419             this.el.addClass(this.invalidClass);
11420         } else {
11421             this.inputEl().addClass('is-invalid');
11422         }
11423         
11424         
11425         
11426         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11427             
11428             var feedback = this.el.select('.form-control-feedback', true).first();
11429             
11430             if(feedback){
11431                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11432                 
11433                 if(this.getValue().length || this.forceFeedback){
11434                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11435                 }
11436                 
11437             }
11438             
11439         }
11440         
11441         this.fireEvent('invalid', this, msg);
11442     },
11443     // private
11444     SafariOnKeyDown : function(event)
11445     {
11446         // this is a workaround for a password hang bug on chrome/ webkit.
11447         if (this.inputEl().dom.type != 'password') {
11448             return;
11449         }
11450         
11451         var isSelectAll = false;
11452         
11453         if(this.inputEl().dom.selectionEnd > 0){
11454             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11455         }
11456         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11457             event.preventDefault();
11458             this.setValue('');
11459             return;
11460         }
11461         
11462         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11463             
11464             event.preventDefault();
11465             // this is very hacky as keydown always get's upper case.
11466             //
11467             var cc = String.fromCharCode(event.getCharCode());
11468             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11469             
11470         }
11471     },
11472     adjustWidth : function(tag, w){
11473         tag = tag.toLowerCase();
11474         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11475             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11476                 if(tag == 'input'){
11477                     return w + 2;
11478                 }
11479                 if(tag == 'textarea'){
11480                     return w-2;
11481                 }
11482             }else if(Roo.isOpera){
11483                 if(tag == 'input'){
11484                     return w + 2;
11485                 }
11486                 if(tag == 'textarea'){
11487                     return w-2;
11488                 }
11489             }
11490         }
11491         return w;
11492     },
11493     
11494     setFieldLabel : function(v)
11495     {
11496         if(!this.rendered){
11497             return;
11498         }
11499         
11500         if(this.indicatorEl()){
11501             var ar = this.el.select('label > span',true);
11502             
11503             if (ar.elements.length) {
11504                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11505                 this.fieldLabel = v;
11506                 return;
11507             }
11508             
11509             var br = this.el.select('label',true);
11510             
11511             if(br.elements.length) {
11512                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11513                 this.fieldLabel = v;
11514                 return;
11515             }
11516             
11517             Roo.log('Cannot Found any of label > span || label in input');
11518             return;
11519         }
11520         
11521         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11522         this.fieldLabel = v;
11523         
11524         
11525     }
11526 });
11527
11528  
11529 /*
11530  * - LGPL
11531  *
11532  * Input
11533  * 
11534  */
11535
11536 /**
11537  * @class Roo.bootstrap.TextArea
11538  * @extends Roo.bootstrap.Input
11539  * Bootstrap TextArea class
11540  * @cfg {Number} cols Specifies the visible width of a text area
11541  * @cfg {Number} rows Specifies the visible number of lines in a text area
11542  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11543  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11544  * @cfg {string} html text
11545  * 
11546  * @constructor
11547  * Create a new TextArea
11548  * @param {Object} config The config object
11549  */
11550
11551 Roo.bootstrap.TextArea = function(config){
11552     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11553    
11554 };
11555
11556 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11557      
11558     cols : false,
11559     rows : 5,
11560     readOnly : false,
11561     warp : 'soft',
11562     resize : false,
11563     value: false,
11564     html: false,
11565     
11566     getAutoCreate : function(){
11567         
11568         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11569         
11570         var id = Roo.id();
11571         
11572         var cfg = {};
11573         
11574         if(this.inputType != 'hidden'){
11575             cfg.cls = 'form-group' //input-group
11576         }
11577         
11578         var input =  {
11579             tag: 'textarea',
11580             id : id,
11581             warp : this.warp,
11582             rows : this.rows,
11583             value : this.value || '',
11584             html: this.html || '',
11585             cls : 'form-control',
11586             placeholder : this.placeholder || '' 
11587             
11588         };
11589         
11590         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11591             input.maxLength = this.maxLength;
11592         }
11593         
11594         if(this.resize){
11595             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11596         }
11597         
11598         if(this.cols){
11599             input.cols = this.cols;
11600         }
11601         
11602         if (this.readOnly) {
11603             input.readonly = true;
11604         }
11605         
11606         if (this.name) {
11607             input.name = this.name;
11608         }
11609         
11610         if (this.size) {
11611             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11612         }
11613         
11614         var settings=this;
11615         ['xs','sm','md','lg'].map(function(size){
11616             if (settings[size]) {
11617                 cfg.cls += ' col-' + size + '-' + settings[size];
11618             }
11619         });
11620         
11621         var inputblock = input;
11622         
11623         if(this.hasFeedback && !this.allowBlank){
11624             
11625             var feedback = {
11626                 tag: 'span',
11627                 cls: 'glyphicon form-control-feedback'
11628             };
11629
11630             inputblock = {
11631                 cls : 'has-feedback',
11632                 cn :  [
11633                     input,
11634                     feedback
11635                 ] 
11636             };  
11637         }
11638         
11639         
11640         if (this.before || this.after) {
11641             
11642             inputblock = {
11643                 cls : 'input-group',
11644                 cn :  [] 
11645             };
11646             if (this.before) {
11647                 inputblock.cn.push({
11648                     tag :'span',
11649                     cls : 'input-group-addon',
11650                     html : this.before
11651                 });
11652             }
11653             
11654             inputblock.cn.push(input);
11655             
11656             if(this.hasFeedback && !this.allowBlank){
11657                 inputblock.cls += ' has-feedback';
11658                 inputblock.cn.push(feedback);
11659             }
11660             
11661             if (this.after) {
11662                 inputblock.cn.push({
11663                     tag :'span',
11664                     cls : 'input-group-addon',
11665                     html : this.after
11666                 });
11667             }
11668             
11669         }
11670         
11671         if (align ==='left' && this.fieldLabel.length) {
11672             cfg.cn = [
11673                 {
11674                     tag: 'label',
11675                     'for' :  id,
11676                     cls : 'control-label',
11677                     html : this.fieldLabel
11678                 },
11679                 {
11680                     cls : "",
11681                     cn: [
11682                         inputblock
11683                     ]
11684                 }
11685
11686             ];
11687             
11688             if(this.labelWidth > 12){
11689                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11690             }
11691
11692             if(this.labelWidth < 13 && this.labelmd == 0){
11693                 this.labelmd = this.labelWidth;
11694             }
11695
11696             if(this.labellg > 0){
11697                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11698                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11699             }
11700
11701             if(this.labelmd > 0){
11702                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11703                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11704             }
11705
11706             if(this.labelsm > 0){
11707                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11708                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11709             }
11710
11711             if(this.labelxs > 0){
11712                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11713                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11714             }
11715             
11716         } else if ( this.fieldLabel.length) {
11717             cfg.cn = [
11718
11719                {
11720                    tag: 'label',
11721                    //cls : 'input-group-addon',
11722                    html : this.fieldLabel
11723
11724                },
11725
11726                inputblock
11727
11728            ];
11729
11730         } else {
11731
11732             cfg.cn = [
11733
11734                 inputblock
11735
11736             ];
11737                 
11738         }
11739         
11740         if (this.disabled) {
11741             input.disabled=true;
11742         }
11743         
11744         return cfg;
11745         
11746     },
11747     /**
11748      * return the real textarea element.
11749      */
11750     inputEl: function ()
11751     {
11752         return this.el.select('textarea.form-control',true).first();
11753     },
11754     
11755     /**
11756      * Clear any invalid styles/messages for this field
11757      */
11758     clearInvalid : function()
11759     {
11760         
11761         if(!this.el || this.preventMark){ // not rendered
11762             return;
11763         }
11764         
11765         var label = this.el.select('label', true).first();
11766         var icon = this.el.select('i.fa-star', true).first();
11767         
11768         if(label && icon){
11769             icon.remove();
11770         }
11771         this.el.removeClass( this.validClass);
11772         this.inputEl().removeClass('is-invalid');
11773          
11774         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11775             
11776             var feedback = this.el.select('.form-control-feedback', true).first();
11777             
11778             if(feedback){
11779                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11780             }
11781             
11782         }
11783         
11784         this.fireEvent('valid', this);
11785     },
11786     
11787      /**
11788      * Mark this field as valid
11789      */
11790     markValid : function()
11791     {
11792         if(!this.el  || this.preventMark){ // not rendered
11793             return;
11794         }
11795         
11796         this.el.removeClass([this.invalidClass, this.validClass]);
11797         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11798         
11799         var feedback = this.el.select('.form-control-feedback', true).first();
11800             
11801         if(feedback){
11802             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11803         }
11804
11805         if(this.disabled || this.allowBlank){
11806             return;
11807         }
11808         
11809         var label = this.el.select('label', true).first();
11810         var icon = this.el.select('i.fa-star', true).first();
11811         
11812         if(label && icon){
11813             icon.remove();
11814         }
11815         if (Roo.bootstrap.version == 3) {
11816             this.el.addClass(this.validClass);
11817         } else {
11818             this.inputEl().addClass('is-valid');
11819         }
11820         
11821         
11822         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11823             
11824             var feedback = this.el.select('.form-control-feedback', true).first();
11825             
11826             if(feedback){
11827                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11828                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11829             }
11830             
11831         }
11832         
11833         this.fireEvent('valid', this);
11834     },
11835     
11836      /**
11837      * Mark this field as invalid
11838      * @param {String} msg The validation message
11839      */
11840     markInvalid : function(msg)
11841     {
11842         if(!this.el  || this.preventMark){ // not rendered
11843             return;
11844         }
11845         
11846         this.el.removeClass([this.invalidClass, this.validClass]);
11847         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11848         
11849         var feedback = this.el.select('.form-control-feedback', true).first();
11850             
11851         if(feedback){
11852             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11853         }
11854
11855         if(this.disabled || this.allowBlank){
11856             return;
11857         }
11858         
11859         var label = this.el.select('label', true).first();
11860         var icon = this.el.select('i.fa-star', true).first();
11861         
11862         if(!this.getValue().length && label && !icon){
11863             this.el.createChild({
11864                 tag : 'i',
11865                 cls : 'text-danger fa fa-lg fa-star',
11866                 tooltip : 'This field is required',
11867                 style : 'margin-right:5px;'
11868             }, label, true);
11869         }
11870         
11871         if (Roo.bootstrap.version == 3) {
11872             this.el.addClass(this.invalidClass);
11873         } else {
11874             this.inputEl().addClass('is-invalid');
11875         }
11876         
11877         // fixme ... this may be depricated need to test..
11878         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11879             
11880             var feedback = this.el.select('.form-control-feedback', true).first();
11881             
11882             if(feedback){
11883                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11884                 
11885                 if(this.getValue().length || this.forceFeedback){
11886                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11887                 }
11888                 
11889             }
11890             
11891         }
11892         
11893         this.fireEvent('invalid', this, msg);
11894     }
11895 });
11896
11897  
11898 /*
11899  * - LGPL
11900  *
11901  * trigger field - base class for combo..
11902  * 
11903  */
11904  
11905 /**
11906  * @class Roo.bootstrap.TriggerField
11907  * @extends Roo.bootstrap.Input
11908  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11909  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11910  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11911  * for which you can provide a custom implementation.  For example:
11912  * <pre><code>
11913 var trigger = new Roo.bootstrap.TriggerField();
11914 trigger.onTriggerClick = myTriggerFn;
11915 trigger.applyTo('my-field');
11916 </code></pre>
11917  *
11918  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11919  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11920  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11921  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11922  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11923
11924  * @constructor
11925  * Create a new TriggerField.
11926  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11927  * to the base TextField)
11928  */
11929 Roo.bootstrap.TriggerField = function(config){
11930     this.mimicing = false;
11931     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11932 };
11933
11934 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11935     /**
11936      * @cfg {String} triggerClass A CSS class to apply to the trigger
11937      */
11938      /**
11939      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11940      */
11941     hideTrigger:false,
11942
11943     /**
11944      * @cfg {Boolean} removable (true|false) special filter default false
11945      */
11946     removable : false,
11947     
11948     /** @cfg {Boolean} grow @hide */
11949     /** @cfg {Number} growMin @hide */
11950     /** @cfg {Number} growMax @hide */
11951
11952     /**
11953      * @hide 
11954      * @method
11955      */
11956     autoSize: Roo.emptyFn,
11957     // private
11958     monitorTab : true,
11959     // private
11960     deferHeight : true,
11961
11962     
11963     actionMode : 'wrap',
11964     
11965     caret : false,
11966     
11967     
11968     getAutoCreate : function(){
11969        
11970         var align = this.labelAlign || this.parentLabelAlign();
11971         
11972         var id = Roo.id();
11973         
11974         var cfg = {
11975             cls: 'form-group' //input-group
11976         };
11977         
11978         
11979         var input =  {
11980             tag: 'input',
11981             id : id,
11982             type : this.inputType,
11983             cls : 'form-control',
11984             autocomplete: 'new-password',
11985             placeholder : this.placeholder || '' 
11986             
11987         };
11988         if (this.name) {
11989             input.name = this.name;
11990         }
11991         if (this.size) {
11992             input.cls += ' input-' + this.size;
11993         }
11994         
11995         if (this.disabled) {
11996             input.disabled=true;
11997         }
11998         
11999         var inputblock = input;
12000         
12001         if(this.hasFeedback && !this.allowBlank){
12002             
12003             var feedback = {
12004                 tag: 'span',
12005                 cls: 'glyphicon form-control-feedback'
12006             };
12007             
12008             if(this.removable && !this.editable  ){
12009                 inputblock = {
12010                     cls : 'has-feedback',
12011                     cn :  [
12012                         inputblock,
12013                         {
12014                             tag: 'button',
12015                             html : 'x',
12016                             cls : 'roo-combo-removable-btn close'
12017                         },
12018                         feedback
12019                     ] 
12020                 };
12021             } else {
12022                 inputblock = {
12023                     cls : 'has-feedback',
12024                     cn :  [
12025                         inputblock,
12026                         feedback
12027                     ] 
12028                 };
12029             }
12030
12031         } else {
12032             if(this.removable && !this.editable ){
12033                 inputblock = {
12034                     cls : 'roo-removable',
12035                     cn :  [
12036                         inputblock,
12037                         {
12038                             tag: 'button',
12039                             html : 'x',
12040                             cls : 'roo-combo-removable-btn close'
12041                         }
12042                     ] 
12043                 };
12044             }
12045         }
12046         
12047         if (this.before || this.after) {
12048             
12049             inputblock = {
12050                 cls : 'input-group',
12051                 cn :  [] 
12052             };
12053             if (this.before) {
12054                 inputblock.cn.push({
12055                     tag :'span',
12056                     cls : 'input-group-addon input-group-prepend input-group-text',
12057                     html : this.before
12058                 });
12059             }
12060             
12061             inputblock.cn.push(input);
12062             
12063             if(this.hasFeedback && !this.allowBlank){
12064                 inputblock.cls += ' has-feedback';
12065                 inputblock.cn.push(feedback);
12066             }
12067             
12068             if (this.after) {
12069                 inputblock.cn.push({
12070                     tag :'span',
12071                     cls : 'input-group-addon input-group-append input-group-text',
12072                     html : this.after
12073                 });
12074             }
12075             
12076         };
12077         
12078       
12079         
12080         var ibwrap = inputblock;
12081         
12082         if(this.multiple){
12083             ibwrap = {
12084                 tag: 'ul',
12085                 cls: 'roo-select2-choices',
12086                 cn:[
12087                     {
12088                         tag: 'li',
12089                         cls: 'roo-select2-search-field',
12090                         cn: [
12091
12092                             inputblock
12093                         ]
12094                     }
12095                 ]
12096             };
12097                 
12098         }
12099         
12100         var combobox = {
12101             cls: 'roo-select2-container input-group',
12102             cn: [
12103                  {
12104                     tag: 'input',
12105                     type : 'hidden',
12106                     cls: 'form-hidden-field'
12107                 },
12108                 ibwrap
12109             ]
12110         };
12111         
12112         if(!this.multiple && this.showToggleBtn){
12113             
12114             var caret = {
12115                         tag: 'span',
12116                         cls: 'caret'
12117              };
12118             if (this.caret != false) {
12119                 caret = {
12120                      tag: 'i',
12121                      cls: 'fa fa-' + this.caret
12122                 };
12123                 
12124             }
12125             
12126             combobox.cn.push({
12127                 tag :'span',
12128                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12129                 cn : [
12130                     Roo.bootstrap.version == 3 ? caret : '',
12131                     {
12132                         tag: 'span',
12133                         cls: 'combobox-clear',
12134                         cn  : [
12135                             {
12136                                 tag : 'i',
12137                                 cls: 'icon-remove'
12138                             }
12139                         ]
12140                     }
12141                 ]
12142
12143             })
12144         }
12145         
12146         if(this.multiple){
12147             combobox.cls += ' roo-select2-container-multi';
12148         }
12149          var indicator = {
12150             tag : 'i',
12151             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12152             tooltip : 'This field is required'
12153         };
12154         if (Roo.bootstrap.version == 4) {
12155             indicator = {
12156                 tag : 'i',
12157                 style : 'display:none'
12158             };
12159         }
12160         
12161         
12162         if (align ==='left' && this.fieldLabel.length) {
12163             
12164             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12165
12166             cfg.cn = [
12167                 indicator,
12168                 {
12169                     tag: 'label',
12170                     'for' :  id,
12171                     cls : 'control-label',
12172                     html : this.fieldLabel
12173
12174                 },
12175                 {
12176                     cls : "", 
12177                     cn: [
12178                         combobox
12179                     ]
12180                 }
12181
12182             ];
12183             
12184             var labelCfg = cfg.cn[1];
12185             var contentCfg = cfg.cn[2];
12186             
12187             if(this.indicatorpos == 'right'){
12188                 cfg.cn = [
12189                     {
12190                         tag: 'label',
12191                         'for' :  id,
12192                         cls : 'control-label',
12193                         cn : [
12194                             {
12195                                 tag : 'span',
12196                                 html : this.fieldLabel
12197                             },
12198                             indicator
12199                         ]
12200                     },
12201                     {
12202                         cls : "", 
12203                         cn: [
12204                             combobox
12205                         ]
12206                     }
12207
12208                 ];
12209                 
12210                 labelCfg = cfg.cn[0];
12211                 contentCfg = cfg.cn[1];
12212             }
12213             
12214             if(this.labelWidth > 12){
12215                 labelCfg.style = "width: " + this.labelWidth + 'px';
12216             }
12217             
12218             if(this.labelWidth < 13 && this.labelmd == 0){
12219                 this.labelmd = this.labelWidth;
12220             }
12221             
12222             if(this.labellg > 0){
12223                 labelCfg.cls += ' col-lg-' + this.labellg;
12224                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12225             }
12226             
12227             if(this.labelmd > 0){
12228                 labelCfg.cls += ' col-md-' + this.labelmd;
12229                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12230             }
12231             
12232             if(this.labelsm > 0){
12233                 labelCfg.cls += ' col-sm-' + this.labelsm;
12234                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12235             }
12236             
12237             if(this.labelxs > 0){
12238                 labelCfg.cls += ' col-xs-' + this.labelxs;
12239                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12240             }
12241             
12242         } else if ( this.fieldLabel.length) {
12243 //                Roo.log(" label");
12244             cfg.cn = [
12245                 indicator,
12246                {
12247                    tag: 'label',
12248                    //cls : 'input-group-addon',
12249                    html : this.fieldLabel
12250
12251                },
12252
12253                combobox
12254
12255             ];
12256             
12257             if(this.indicatorpos == 'right'){
12258                 
12259                 cfg.cn = [
12260                     {
12261                        tag: 'label',
12262                        cn : [
12263                            {
12264                                tag : 'span',
12265                                html : this.fieldLabel
12266                            },
12267                            indicator
12268                        ]
12269
12270                     },
12271                     combobox
12272
12273                 ];
12274
12275             }
12276
12277         } else {
12278             
12279 //                Roo.log(" no label && no align");
12280                 cfg = combobox
12281                      
12282                 
12283         }
12284         
12285         var settings=this;
12286         ['xs','sm','md','lg'].map(function(size){
12287             if (settings[size]) {
12288                 cfg.cls += ' col-' + size + '-' + settings[size];
12289             }
12290         });
12291         
12292         return cfg;
12293         
12294     },
12295     
12296     
12297     
12298     // private
12299     onResize : function(w, h){
12300 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12301 //        if(typeof w == 'number'){
12302 //            var x = w - this.trigger.getWidth();
12303 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12304 //            this.trigger.setStyle('left', x+'px');
12305 //        }
12306     },
12307
12308     // private
12309     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12310
12311     // private
12312     getResizeEl : function(){
12313         return this.inputEl();
12314     },
12315
12316     // private
12317     getPositionEl : function(){
12318         return this.inputEl();
12319     },
12320
12321     // private
12322     alignErrorIcon : function(){
12323         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12324     },
12325
12326     // private
12327     initEvents : function(){
12328         
12329         this.createList();
12330         
12331         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12332         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12333         if(!this.multiple && this.showToggleBtn){
12334             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12335             if(this.hideTrigger){
12336                 this.trigger.setDisplayed(false);
12337             }
12338             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12339         }
12340         
12341         if(this.multiple){
12342             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12343         }
12344         
12345         if(this.removable && !this.editable && !this.tickable){
12346             var close = this.closeTriggerEl();
12347             
12348             if(close){
12349                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12350                 close.on('click', this.removeBtnClick, this, close);
12351             }
12352         }
12353         
12354         //this.trigger.addClassOnOver('x-form-trigger-over');
12355         //this.trigger.addClassOnClick('x-form-trigger-click');
12356         
12357         //if(!this.width){
12358         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12359         //}
12360     },
12361     
12362     closeTriggerEl : function()
12363     {
12364         var close = this.el.select('.roo-combo-removable-btn', true).first();
12365         return close ? close : false;
12366     },
12367     
12368     removeBtnClick : function(e, h, el)
12369     {
12370         e.preventDefault();
12371         
12372         if(this.fireEvent("remove", this) !== false){
12373             this.reset();
12374             this.fireEvent("afterremove", this)
12375         }
12376     },
12377     
12378     createList : function()
12379     {
12380         this.list = Roo.get(document.body).createChild({
12381             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12382             cls: 'typeahead typeahead-long dropdown-menu shadow',
12383             style: 'display:none'
12384         });
12385         
12386         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12387         
12388     },
12389
12390     // private
12391     initTrigger : function(){
12392        
12393     },
12394
12395     // private
12396     onDestroy : function(){
12397         if(this.trigger){
12398             this.trigger.removeAllListeners();
12399           //  this.trigger.remove();
12400         }
12401         //if(this.wrap){
12402         //    this.wrap.remove();
12403         //}
12404         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12405     },
12406
12407     // private
12408     onFocus : function(){
12409         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12410         /*
12411         if(!this.mimicing){
12412             this.wrap.addClass('x-trigger-wrap-focus');
12413             this.mimicing = true;
12414             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12415             if(this.monitorTab){
12416                 this.el.on("keydown", this.checkTab, this);
12417             }
12418         }
12419         */
12420     },
12421
12422     // private
12423     checkTab : function(e){
12424         if(e.getKey() == e.TAB){
12425             this.triggerBlur();
12426         }
12427     },
12428
12429     // private
12430     onBlur : function(){
12431         // do nothing
12432     },
12433
12434     // private
12435     mimicBlur : function(e, t){
12436         /*
12437         if(!this.wrap.contains(t) && this.validateBlur()){
12438             this.triggerBlur();
12439         }
12440         */
12441     },
12442
12443     // private
12444     triggerBlur : function(){
12445         this.mimicing = false;
12446         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12447         if(this.monitorTab){
12448             this.el.un("keydown", this.checkTab, this);
12449         }
12450         //this.wrap.removeClass('x-trigger-wrap-focus');
12451         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12452     },
12453
12454     // private
12455     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12456     validateBlur : function(e, t){
12457         return true;
12458     },
12459
12460     // private
12461     onDisable : function(){
12462         this.inputEl().dom.disabled = true;
12463         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12464         //if(this.wrap){
12465         //    this.wrap.addClass('x-item-disabled');
12466         //}
12467     },
12468
12469     // private
12470     onEnable : function(){
12471         this.inputEl().dom.disabled = false;
12472         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12473         //if(this.wrap){
12474         //    this.el.removeClass('x-item-disabled');
12475         //}
12476     },
12477
12478     // private
12479     onShow : function(){
12480         var ae = this.getActionEl();
12481         
12482         if(ae){
12483             ae.dom.style.display = '';
12484             ae.dom.style.visibility = 'visible';
12485         }
12486     },
12487
12488     // private
12489     
12490     onHide : function(){
12491         var ae = this.getActionEl();
12492         ae.dom.style.display = 'none';
12493     },
12494
12495     /**
12496      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12497      * by an implementing function.
12498      * @method
12499      * @param {EventObject} e
12500      */
12501     onTriggerClick : Roo.emptyFn
12502 });
12503  
12504 /*
12505 * Licence: LGPL
12506 */
12507
12508 /**
12509  * @class Roo.bootstrap.CardUploader
12510  * @extends Roo.bootstrap.Button
12511  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12512  * @cfg {Number} errorTimeout default 3000
12513  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12514  * @cfg {Array}  html The button text.
12515
12516  *
12517  * @constructor
12518  * Create a new CardUploader
12519  * @param {Object} config The config object
12520  */
12521
12522 Roo.bootstrap.CardUploader = function(config){
12523     
12524  
12525     
12526     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12527     
12528     
12529     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12530         return r.data.id
12531      });
12532     
12533      this.addEvents({
12534          // raw events
12535         /**
12536          * @event preview
12537          * When a image is clicked on - and needs to display a slideshow or similar..
12538          * @param {Roo.bootstrap.Card} this
12539          * @param {Object} The image information data 
12540          *
12541          */
12542         'preview' : true,
12543          /**
12544          * @event download
12545          * When a the download link is clicked
12546          * @param {Roo.bootstrap.Card} this
12547          * @param {Object} The image information data  contains 
12548          */
12549         'download' : true
12550         
12551     });
12552 };
12553  
12554 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12555     
12556      
12557     errorTimeout : 3000,
12558      
12559     images : false,
12560    
12561     fileCollection : false,
12562     allowBlank : true,
12563     
12564     getAutoCreate : function()
12565     {
12566         
12567         var cfg =  {
12568             cls :'form-group' ,
12569             cn : [
12570                
12571                 {
12572                     tag: 'label',
12573                    //cls : 'input-group-addon',
12574                     html : this.fieldLabel
12575
12576                 },
12577
12578                 {
12579                     tag: 'input',
12580                     type : 'hidden',
12581                     name : this.name,
12582                     value : this.value,
12583                     cls : 'd-none  form-control'
12584                 },
12585                 
12586                 {
12587                     tag: 'input',
12588                     multiple : 'multiple',
12589                     type : 'file',
12590                     cls : 'd-none  roo-card-upload-selector'
12591                 },
12592                 
12593                 {
12594                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12595                 },
12596                 {
12597                     cls : 'card-columns roo-card-uploader-container'
12598                 }
12599
12600             ]
12601         };
12602            
12603          
12604         return cfg;
12605     },
12606     
12607     getChildContainer : function() /// what children are added to.
12608     {
12609         return this.containerEl;
12610     },
12611    
12612     getButtonContainer : function() /// what children are added to.
12613     {
12614         return this.el.select(".roo-card-uploader-button-container").first();
12615     },
12616    
12617     initEvents : function()
12618     {
12619         
12620         Roo.bootstrap.Input.prototype.initEvents.call(this);
12621         
12622         var t = this;
12623         this.addxtype({
12624             xns: Roo.bootstrap,
12625
12626             xtype : 'Button',
12627             container_method : 'getButtonContainer' ,            
12628             html :  this.html, // fix changable?
12629             cls : 'w-100 ',
12630             listeners : {
12631                 'click' : function(btn, e) {
12632                     t.onClick(e);
12633                 }
12634             }
12635         });
12636         
12637         
12638         
12639         
12640         this.urlAPI = (window.createObjectURL && window) || 
12641                                 (window.URL && URL.revokeObjectURL && URL) || 
12642                                 (window.webkitURL && webkitURL);
12643                         
12644          
12645          
12646          
12647         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12648         
12649         this.selectorEl.on('change', this.onFileSelected, this);
12650         if (this.images) {
12651             var t = this;
12652             this.images.forEach(function(img) {
12653                 t.addCard(img)
12654             });
12655             this.images = false;
12656         }
12657         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12658          
12659        
12660     },
12661     
12662    
12663     onClick : function(e)
12664     {
12665         e.preventDefault();
12666          
12667         this.selectorEl.dom.click();
12668          
12669     },
12670     
12671     onFileSelected : function(e)
12672     {
12673         e.preventDefault();
12674         
12675         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12676             return;
12677         }
12678         
12679         Roo.each(this.selectorEl.dom.files, function(file){    
12680             this.addFile(file);
12681         }, this);
12682          
12683     },
12684     
12685       
12686     
12687       
12688     
12689     addFile : function(file)
12690     {
12691            
12692         if(typeof(file) === 'string'){
12693             throw "Add file by name?"; // should not happen
12694             return;
12695         }
12696         
12697         if(!file || !this.urlAPI){
12698             return;
12699         }
12700         
12701         // file;
12702         // file.type;
12703         
12704         var _this = this;
12705         
12706         
12707         var url = _this.urlAPI.createObjectURL( file);
12708            
12709         this.addCard({
12710             id : Roo.bootstrap.CardUploader.ID--,
12711             is_uploaded : false,
12712             src : url,
12713             srcfile : file,
12714             title : file.name,
12715             mimetype : file.type,
12716             preview : false,
12717             is_deleted : 0
12718         });
12719         
12720     },
12721     
12722     /**
12723      * addCard - add an Attachment to the uploader
12724      * @param data - the data about the image to upload
12725      *
12726      * {
12727           id : 123
12728           title : "Title of file",
12729           is_uploaded : false,
12730           src : "http://.....",
12731           srcfile : { the File upload object },
12732           mimetype : file.type,
12733           preview : false,
12734           is_deleted : 0
12735           .. any other data...
12736         }
12737      *
12738      * 
12739     */
12740     
12741     addCard : function (data)
12742     {
12743         // hidden input element?
12744         // if the file is not an image...
12745         //then we need to use something other that and header_image
12746         var t = this;
12747         //   remove.....
12748         var footer = [
12749             {
12750                 xns : Roo.bootstrap,
12751                 xtype : 'CardFooter',
12752                  items: [
12753                     {
12754                         xns : Roo.bootstrap,
12755                         xtype : 'Element',
12756                         cls : 'd-flex',
12757                         items : [
12758                             
12759                             {
12760                                 xns : Roo.bootstrap,
12761                                 xtype : 'Button',
12762                                 html : String.format("<small>{0}</small>", data.title),
12763                                 cls : 'col-10 text-left',
12764                                 size: 'sm',
12765                                 weight: 'link',
12766                                 fa : 'download',
12767                                 listeners : {
12768                                     click : function() {
12769                                      
12770                                         t.fireEvent( "download", t, data );
12771                                     }
12772                                 }
12773                             },
12774                           
12775                             {
12776                                 xns : Roo.bootstrap,
12777                                 xtype : 'Button',
12778                                 style: 'max-height: 28px; ',
12779                                 size : 'sm',
12780                                 weight: 'danger',
12781                                 cls : 'col-2',
12782                                 fa : 'times',
12783                                 listeners : {
12784                                     click : function() {
12785                                         t.removeCard(data.id)
12786                                     }
12787                                 }
12788                             }
12789                         ]
12790                     }
12791                     
12792                 ] 
12793             }
12794             
12795         ];
12796         
12797         var cn = this.addxtype(
12798             {
12799                  
12800                 xns : Roo.bootstrap,
12801                 xtype : 'Card',
12802                 closeable : true,
12803                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12804                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12805                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12806                 data : data,
12807                 html : false,
12808                  
12809                 items : footer,
12810                 initEvents : function() {
12811                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12812                     var card = this;
12813                     this.imgEl = this.el.select('.card-img-top').first();
12814                     if (this.imgEl) {
12815                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12816                         this.imgEl.set({ 'pointer' : 'cursor' });
12817                                   
12818                     }
12819                     this.getCardFooter().addClass('p-1');
12820                     
12821                   
12822                 }
12823                 
12824             }
12825         );
12826         // dont' really need ot update items.
12827         // this.items.push(cn);
12828         this.fileCollection.add(cn);
12829         
12830         if (!data.srcfile) {
12831             this.updateInput();
12832             return;
12833         }
12834             
12835         var _t = this;
12836         var reader = new FileReader();
12837         reader.addEventListener("load", function() {  
12838             data.srcdata =  reader.result;
12839             _t.updateInput();
12840         });
12841         reader.readAsDataURL(data.srcfile);
12842         
12843         
12844         
12845     },
12846     removeCard : function(id)
12847     {
12848         
12849         var card  = this.fileCollection.get(id);
12850         card.data.is_deleted = 1;
12851         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12852         //this.fileCollection.remove(card);
12853         //this.items = this.items.filter(function(e) { return e != card });
12854         // dont' really need ot update items.
12855         card.el.dom.parentNode.removeChild(card.el.dom);
12856         this.updateInput();
12857
12858         
12859     },
12860     reset: function()
12861     {
12862         this.fileCollection.each(function(card) {
12863             if (card.el.dom && card.el.dom.parentNode) {
12864                 card.el.dom.parentNode.removeChild(card.el.dom);
12865             }
12866         });
12867         this.fileCollection.clear();
12868         this.updateInput();
12869     },
12870     
12871     updateInput : function()
12872     {
12873          var data = [];
12874         this.fileCollection.each(function(e) {
12875             data.push(e.data);
12876             
12877         });
12878         this.inputEl().dom.value = JSON.stringify(data);
12879         
12880         
12881         
12882     }
12883     
12884     
12885 });
12886
12887
12888 Roo.bootstrap.CardUploader.ID = -1;/*
12889  * Based on:
12890  * Ext JS Library 1.1.1
12891  * Copyright(c) 2006-2007, Ext JS, LLC.
12892  *
12893  * Originally Released Under LGPL - original licence link has changed is not relivant.
12894  *
12895  * Fork - LGPL
12896  * <script type="text/javascript">
12897  */
12898
12899
12900 /**
12901  * @class Roo.data.SortTypes
12902  * @singleton
12903  * Defines the default sorting (casting?) comparison functions used when sorting data.
12904  */
12905 Roo.data.SortTypes = {
12906     /**
12907      * Default sort that does nothing
12908      * @param {Mixed} s The value being converted
12909      * @return {Mixed} The comparison value
12910      */
12911     none : function(s){
12912         return s;
12913     },
12914     
12915     /**
12916      * The regular expression used to strip tags
12917      * @type {RegExp}
12918      * @property
12919      */
12920     stripTagsRE : /<\/?[^>]+>/gi,
12921     
12922     /**
12923      * Strips all HTML tags to sort on text only
12924      * @param {Mixed} s The value being converted
12925      * @return {String} The comparison value
12926      */
12927     asText : function(s){
12928         return String(s).replace(this.stripTagsRE, "");
12929     },
12930     
12931     /**
12932      * Strips all HTML tags to sort on text only - Case insensitive
12933      * @param {Mixed} s The value being converted
12934      * @return {String} The comparison value
12935      */
12936     asUCText : function(s){
12937         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12938     },
12939     
12940     /**
12941      * Case insensitive string
12942      * @param {Mixed} s The value being converted
12943      * @return {String} The comparison value
12944      */
12945     asUCString : function(s) {
12946         return String(s).toUpperCase();
12947     },
12948     
12949     /**
12950      * Date sorting
12951      * @param {Mixed} s The value being converted
12952      * @return {Number} The comparison value
12953      */
12954     asDate : function(s) {
12955         if(!s){
12956             return 0;
12957         }
12958         if(s instanceof Date){
12959             return s.getTime();
12960         }
12961         return Date.parse(String(s));
12962     },
12963     
12964     /**
12965      * Float sorting
12966      * @param {Mixed} s The value being converted
12967      * @return {Float} The comparison value
12968      */
12969     asFloat : function(s) {
12970         var val = parseFloat(String(s).replace(/,/g, ""));
12971         if(isNaN(val)) {
12972             val = 0;
12973         }
12974         return val;
12975     },
12976     
12977     /**
12978      * Integer sorting
12979      * @param {Mixed} s The value being converted
12980      * @return {Number} The comparison value
12981      */
12982     asInt : function(s) {
12983         var val = parseInt(String(s).replace(/,/g, ""));
12984         if(isNaN(val)) {
12985             val = 0;
12986         }
12987         return val;
12988     }
12989 };/*
12990  * Based on:
12991  * Ext JS Library 1.1.1
12992  * Copyright(c) 2006-2007, Ext JS, LLC.
12993  *
12994  * Originally Released Under LGPL - original licence link has changed is not relivant.
12995  *
12996  * Fork - LGPL
12997  * <script type="text/javascript">
12998  */
12999
13000 /**
13001 * @class Roo.data.Record
13002  * Instances of this class encapsulate both record <em>definition</em> information, and record
13003  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13004  * to access Records cached in an {@link Roo.data.Store} object.<br>
13005  * <p>
13006  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13007  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13008  * objects.<br>
13009  * <p>
13010  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13011  * @constructor
13012  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13013  * {@link #create}. The parameters are the same.
13014  * @param {Array} data An associative Array of data values keyed by the field name.
13015  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13016  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13017  * not specified an integer id is generated.
13018  */
13019 Roo.data.Record = function(data, id){
13020     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13021     this.data = data;
13022 };
13023
13024 /**
13025  * Generate a constructor for a specific record layout.
13026  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13027  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13028  * Each field definition object may contain the following properties: <ul>
13029  * <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,
13030  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13031  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13032  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13033  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13034  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13035  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13036  * this may be omitted.</p></li>
13037  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13038  * <ul><li>auto (Default, implies no conversion)</li>
13039  * <li>string</li>
13040  * <li>int</li>
13041  * <li>float</li>
13042  * <li>boolean</li>
13043  * <li>date</li></ul></p></li>
13044  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13045  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13046  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13047  * by the Reader into an object that will be stored in the Record. It is passed the
13048  * following parameters:<ul>
13049  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13050  * </ul></p></li>
13051  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13052  * </ul>
13053  * <br>usage:<br><pre><code>
13054 var TopicRecord = Roo.data.Record.create(
13055     {name: 'title', mapping: 'topic_title'},
13056     {name: 'author', mapping: 'username'},
13057     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13058     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13059     {name: 'lastPoster', mapping: 'user2'},
13060     {name: 'excerpt', mapping: 'post_text'}
13061 );
13062
13063 var myNewRecord = new TopicRecord({
13064     title: 'Do my job please',
13065     author: 'noobie',
13066     totalPosts: 1,
13067     lastPost: new Date(),
13068     lastPoster: 'Animal',
13069     excerpt: 'No way dude!'
13070 });
13071 myStore.add(myNewRecord);
13072 </code></pre>
13073  * @method create
13074  * @static
13075  */
13076 Roo.data.Record.create = function(o){
13077     var f = function(){
13078         f.superclass.constructor.apply(this, arguments);
13079     };
13080     Roo.extend(f, Roo.data.Record);
13081     var p = f.prototype;
13082     p.fields = new Roo.util.MixedCollection(false, function(field){
13083         return field.name;
13084     });
13085     for(var i = 0, len = o.length; i < len; i++){
13086         p.fields.add(new Roo.data.Field(o[i]));
13087     }
13088     f.getField = function(name){
13089         return p.fields.get(name);  
13090     };
13091     return f;
13092 };
13093
13094 Roo.data.Record.AUTO_ID = 1000;
13095 Roo.data.Record.EDIT = 'edit';
13096 Roo.data.Record.REJECT = 'reject';
13097 Roo.data.Record.COMMIT = 'commit';
13098
13099 Roo.data.Record.prototype = {
13100     /**
13101      * Readonly flag - true if this record has been modified.
13102      * @type Boolean
13103      */
13104     dirty : false,
13105     editing : false,
13106     error: null,
13107     modified: null,
13108
13109     // private
13110     join : function(store){
13111         this.store = store;
13112     },
13113
13114     /**
13115      * Set the named field to the specified value.
13116      * @param {String} name The name of the field to set.
13117      * @param {Object} value The value to set the field to.
13118      */
13119     set : function(name, value){
13120         if(this.data[name] == value){
13121             return;
13122         }
13123         this.dirty = true;
13124         if(!this.modified){
13125             this.modified = {};
13126         }
13127         if(typeof this.modified[name] == 'undefined'){
13128             this.modified[name] = this.data[name];
13129         }
13130         this.data[name] = value;
13131         if(!this.editing && this.store){
13132             this.store.afterEdit(this);
13133         }       
13134     },
13135
13136     /**
13137      * Get the value of the named field.
13138      * @param {String} name The name of the field to get the value of.
13139      * @return {Object} The value of the field.
13140      */
13141     get : function(name){
13142         return this.data[name]; 
13143     },
13144
13145     // private
13146     beginEdit : function(){
13147         this.editing = true;
13148         this.modified = {}; 
13149     },
13150
13151     // private
13152     cancelEdit : function(){
13153         this.editing = false;
13154         delete this.modified;
13155     },
13156
13157     // private
13158     endEdit : function(){
13159         this.editing = false;
13160         if(this.dirty && this.store){
13161             this.store.afterEdit(this);
13162         }
13163     },
13164
13165     /**
13166      * Usually called by the {@link Roo.data.Store} which owns the Record.
13167      * Rejects all changes made to the Record since either creation, or the last commit operation.
13168      * Modified fields are reverted to their original values.
13169      * <p>
13170      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13171      * of reject operations.
13172      */
13173     reject : function(){
13174         var m = this.modified;
13175         for(var n in m){
13176             if(typeof m[n] != "function"){
13177                 this.data[n] = m[n];
13178             }
13179         }
13180         this.dirty = false;
13181         delete this.modified;
13182         this.editing = false;
13183         if(this.store){
13184             this.store.afterReject(this);
13185         }
13186     },
13187
13188     /**
13189      * Usually called by the {@link Roo.data.Store} which owns the Record.
13190      * Commits all changes made to the Record since either creation, or the last commit operation.
13191      * <p>
13192      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13193      * of commit operations.
13194      */
13195     commit : function(){
13196         this.dirty = false;
13197         delete this.modified;
13198         this.editing = false;
13199         if(this.store){
13200             this.store.afterCommit(this);
13201         }
13202     },
13203
13204     // private
13205     hasError : function(){
13206         return this.error != null;
13207     },
13208
13209     // private
13210     clearError : function(){
13211         this.error = null;
13212     },
13213
13214     /**
13215      * Creates a copy of this record.
13216      * @param {String} id (optional) A new record id if you don't want to use this record's id
13217      * @return {Record}
13218      */
13219     copy : function(newId) {
13220         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13221     }
13222 };/*
13223  * Based on:
13224  * Ext JS Library 1.1.1
13225  * Copyright(c) 2006-2007, Ext JS, LLC.
13226  *
13227  * Originally Released Under LGPL - original licence link has changed is not relivant.
13228  *
13229  * Fork - LGPL
13230  * <script type="text/javascript">
13231  */
13232
13233
13234
13235 /**
13236  * @class Roo.data.Store
13237  * @extends Roo.util.Observable
13238  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13239  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13240  * <p>
13241  * 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
13242  * has no knowledge of the format of the data returned by the Proxy.<br>
13243  * <p>
13244  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13245  * instances from the data object. These records are cached and made available through accessor functions.
13246  * @constructor
13247  * Creates a new Store.
13248  * @param {Object} config A config object containing the objects needed for the Store to access data,
13249  * and read the data into Records.
13250  */
13251 Roo.data.Store = function(config){
13252     this.data = new Roo.util.MixedCollection(false);
13253     this.data.getKey = function(o){
13254         return o.id;
13255     };
13256     this.baseParams = {};
13257     // private
13258     this.paramNames = {
13259         "start" : "start",
13260         "limit" : "limit",
13261         "sort" : "sort",
13262         "dir" : "dir",
13263         "multisort" : "_multisort"
13264     };
13265
13266     if(config && config.data){
13267         this.inlineData = config.data;
13268         delete config.data;
13269     }
13270
13271     Roo.apply(this, config);
13272     
13273     if(this.reader){ // reader passed
13274         this.reader = Roo.factory(this.reader, Roo.data);
13275         this.reader.xmodule = this.xmodule || false;
13276         if(!this.recordType){
13277             this.recordType = this.reader.recordType;
13278         }
13279         if(this.reader.onMetaChange){
13280             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13281         }
13282     }
13283
13284     if(this.recordType){
13285         this.fields = this.recordType.prototype.fields;
13286     }
13287     this.modified = [];
13288
13289     this.addEvents({
13290         /**
13291          * @event datachanged
13292          * Fires when the data cache has changed, and a widget which is using this Store
13293          * as a Record cache should refresh its view.
13294          * @param {Store} this
13295          */
13296         datachanged : true,
13297         /**
13298          * @event metachange
13299          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13300          * @param {Store} this
13301          * @param {Object} meta The JSON metadata
13302          */
13303         metachange : true,
13304         /**
13305          * @event add
13306          * Fires when Records have been added to the Store
13307          * @param {Store} this
13308          * @param {Roo.data.Record[]} records The array of Records added
13309          * @param {Number} index The index at which the record(s) were added
13310          */
13311         add : true,
13312         /**
13313          * @event remove
13314          * Fires when a Record has been removed from the Store
13315          * @param {Store} this
13316          * @param {Roo.data.Record} record The Record that was removed
13317          * @param {Number} index The index at which the record was removed
13318          */
13319         remove : true,
13320         /**
13321          * @event update
13322          * Fires when a Record has been updated
13323          * @param {Store} this
13324          * @param {Roo.data.Record} record The Record that was updated
13325          * @param {String} operation The update operation being performed.  Value may be one of:
13326          * <pre><code>
13327  Roo.data.Record.EDIT
13328  Roo.data.Record.REJECT
13329  Roo.data.Record.COMMIT
13330          * </code></pre>
13331          */
13332         update : true,
13333         /**
13334          * @event clear
13335          * Fires when the data cache has been cleared.
13336          * @param {Store} this
13337          */
13338         clear : true,
13339         /**
13340          * @event beforeload
13341          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13342          * the load action will be canceled.
13343          * @param {Store} this
13344          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13345          */
13346         beforeload : true,
13347         /**
13348          * @event beforeloadadd
13349          * Fires after a new set of Records has been loaded.
13350          * @param {Store} this
13351          * @param {Roo.data.Record[]} records The Records that were loaded
13352          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13353          */
13354         beforeloadadd : true,
13355         /**
13356          * @event load
13357          * Fires after a new set of Records has been loaded, before they are added to the store.
13358          * @param {Store} this
13359          * @param {Roo.data.Record[]} records The Records that were loaded
13360          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13361          * @params {Object} return from reader
13362          */
13363         load : true,
13364         /**
13365          * @event loadexception
13366          * Fires if an exception occurs in the Proxy during loading.
13367          * Called with the signature of the Proxy's "loadexception" event.
13368          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13369          * 
13370          * @param {Proxy} 
13371          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13372          * @param {Object} load options 
13373          * @param {Object} jsonData from your request (normally this contains the Exception)
13374          */
13375         loadexception : true
13376     });
13377     
13378     if(this.proxy){
13379         this.proxy = Roo.factory(this.proxy, Roo.data);
13380         this.proxy.xmodule = this.xmodule || false;
13381         this.relayEvents(this.proxy,  ["loadexception"]);
13382     }
13383     this.sortToggle = {};
13384     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13385
13386     Roo.data.Store.superclass.constructor.call(this);
13387
13388     if(this.inlineData){
13389         this.loadData(this.inlineData);
13390         delete this.inlineData;
13391     }
13392 };
13393
13394 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13395      /**
13396     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13397     * without a remote query - used by combo/forms at present.
13398     */
13399     
13400     /**
13401     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13402     */
13403     /**
13404     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13405     */
13406     /**
13407     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13408     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13409     */
13410     /**
13411     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13412     * on any HTTP request
13413     */
13414     /**
13415     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13416     */
13417     /**
13418     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13419     */
13420     multiSort: false,
13421     /**
13422     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13423     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13424     */
13425     remoteSort : false,
13426
13427     /**
13428     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13429      * loaded or when a record is removed. (defaults to false).
13430     */
13431     pruneModifiedRecords : false,
13432
13433     // private
13434     lastOptions : null,
13435
13436     /**
13437      * Add Records to the Store and fires the add event.
13438      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13439      */
13440     add : function(records){
13441         records = [].concat(records);
13442         for(var i = 0, len = records.length; i < len; i++){
13443             records[i].join(this);
13444         }
13445         var index = this.data.length;
13446         this.data.addAll(records);
13447         this.fireEvent("add", this, records, index);
13448     },
13449
13450     /**
13451      * Remove a Record from the Store and fires the remove event.
13452      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13453      */
13454     remove : function(record){
13455         var index = this.data.indexOf(record);
13456         this.data.removeAt(index);
13457  
13458         if(this.pruneModifiedRecords){
13459             this.modified.remove(record);
13460         }
13461         this.fireEvent("remove", this, record, index);
13462     },
13463
13464     /**
13465      * Remove all Records from the Store and fires the clear event.
13466      */
13467     removeAll : function(){
13468         this.data.clear();
13469         if(this.pruneModifiedRecords){
13470             this.modified = [];
13471         }
13472         this.fireEvent("clear", this);
13473     },
13474
13475     /**
13476      * Inserts Records to the Store at the given index and fires the add event.
13477      * @param {Number} index The start index at which to insert the passed Records.
13478      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13479      */
13480     insert : function(index, records){
13481         records = [].concat(records);
13482         for(var i = 0, len = records.length; i < len; i++){
13483             this.data.insert(index, records[i]);
13484             records[i].join(this);
13485         }
13486         this.fireEvent("add", this, records, index);
13487     },
13488
13489     /**
13490      * Get the index within the cache of the passed Record.
13491      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13492      * @return {Number} The index of the passed Record. Returns -1 if not found.
13493      */
13494     indexOf : function(record){
13495         return this.data.indexOf(record);
13496     },
13497
13498     /**
13499      * Get the index within the cache of the Record with the passed id.
13500      * @param {String} id The id of the Record to find.
13501      * @return {Number} The index of the Record. Returns -1 if not found.
13502      */
13503     indexOfId : function(id){
13504         return this.data.indexOfKey(id);
13505     },
13506
13507     /**
13508      * Get the Record with the specified id.
13509      * @param {String} id The id of the Record to find.
13510      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13511      */
13512     getById : function(id){
13513         return this.data.key(id);
13514     },
13515
13516     /**
13517      * Get the Record at the specified index.
13518      * @param {Number} index The index of the Record to find.
13519      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13520      */
13521     getAt : function(index){
13522         return this.data.itemAt(index);
13523     },
13524
13525     /**
13526      * Returns a range of Records between specified indices.
13527      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13528      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13529      * @return {Roo.data.Record[]} An array of Records
13530      */
13531     getRange : function(start, end){
13532         return this.data.getRange(start, end);
13533     },
13534
13535     // private
13536     storeOptions : function(o){
13537         o = Roo.apply({}, o);
13538         delete o.callback;
13539         delete o.scope;
13540         this.lastOptions = o;
13541     },
13542
13543     /**
13544      * Loads the Record cache from the configured Proxy using the configured Reader.
13545      * <p>
13546      * If using remote paging, then the first load call must specify the <em>start</em>
13547      * and <em>limit</em> properties in the options.params property to establish the initial
13548      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13549      * <p>
13550      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13551      * and this call will return before the new data has been loaded. Perform any post-processing
13552      * in a callback function, or in a "load" event handler.</strong>
13553      * <p>
13554      * @param {Object} options An object containing properties which control loading options:<ul>
13555      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13556      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13557      * passed the following arguments:<ul>
13558      * <li>r : Roo.data.Record[]</li>
13559      * <li>options: Options object from the load call</li>
13560      * <li>success: Boolean success indicator</li></ul></li>
13561      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13562      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13563      * </ul>
13564      */
13565     load : function(options){
13566         options = options || {};
13567         if(this.fireEvent("beforeload", this, options) !== false){
13568             this.storeOptions(options);
13569             var p = Roo.apply(options.params || {}, this.baseParams);
13570             // if meta was not loaded from remote source.. try requesting it.
13571             if (!this.reader.metaFromRemote) {
13572                 p._requestMeta = 1;
13573             }
13574             if(this.sortInfo && this.remoteSort){
13575                 var pn = this.paramNames;
13576                 p[pn["sort"]] = this.sortInfo.field;
13577                 p[pn["dir"]] = this.sortInfo.direction;
13578             }
13579             if (this.multiSort) {
13580                 var pn = this.paramNames;
13581                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13582             }
13583             
13584             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13585         }
13586     },
13587
13588     /**
13589      * Reloads the Record cache from the configured Proxy using the configured Reader and
13590      * the options from the last load operation performed.
13591      * @param {Object} options (optional) An object containing properties which may override the options
13592      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13593      * the most recently used options are reused).
13594      */
13595     reload : function(options){
13596         this.load(Roo.applyIf(options||{}, this.lastOptions));
13597     },
13598
13599     // private
13600     // Called as a callback by the Reader during a load operation.
13601     loadRecords : function(o, options, success){
13602         if(!o || success === false){
13603             if(success !== false){
13604                 this.fireEvent("load", this, [], options, o);
13605             }
13606             if(options.callback){
13607                 options.callback.call(options.scope || this, [], options, false);
13608             }
13609             return;
13610         }
13611         // if data returned failure - throw an exception.
13612         if (o.success === false) {
13613             // show a message if no listener is registered.
13614             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13615                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13616             }
13617             // loadmask wil be hooked into this..
13618             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13619             return;
13620         }
13621         var r = o.records, t = o.totalRecords || r.length;
13622         
13623         this.fireEvent("beforeloadadd", this, r, options, o);
13624         
13625         if(!options || options.add !== true){
13626             if(this.pruneModifiedRecords){
13627                 this.modified = [];
13628             }
13629             for(var i = 0, len = r.length; i < len; i++){
13630                 r[i].join(this);
13631             }
13632             if(this.snapshot){
13633                 this.data = this.snapshot;
13634                 delete this.snapshot;
13635             }
13636             this.data.clear();
13637             this.data.addAll(r);
13638             this.totalLength = t;
13639             this.applySort();
13640             this.fireEvent("datachanged", this);
13641         }else{
13642             this.totalLength = Math.max(t, this.data.length+r.length);
13643             this.add(r);
13644         }
13645         
13646         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13647                 
13648             var e = new Roo.data.Record({});
13649
13650             e.set(this.parent.displayField, this.parent.emptyTitle);
13651             e.set(this.parent.valueField, '');
13652
13653             this.insert(0, e);
13654         }
13655             
13656         this.fireEvent("load", this, r, options, o);
13657         if(options.callback){
13658             options.callback.call(options.scope || this, r, options, true);
13659         }
13660     },
13661
13662
13663     /**
13664      * Loads data from a passed data block. A Reader which understands the format of the data
13665      * must have been configured in the constructor.
13666      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13667      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13668      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13669      */
13670     loadData : function(o, append){
13671         var r = this.reader.readRecords(o);
13672         this.loadRecords(r, {add: append}, true);
13673     },
13674     
13675      /**
13676      * using 'cn' the nested child reader read the child array into it's child stores.
13677      * @param {Object} rec The record with a 'children array
13678      */
13679     loadDataFromChildren : function(rec)
13680     {
13681         this.loadData(this.reader.toLoadData(rec));
13682     },
13683     
13684
13685     /**
13686      * Gets the number of cached records.
13687      * <p>
13688      * <em>If using paging, this may not be the total size of the dataset. If the data object
13689      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13690      * the data set size</em>
13691      */
13692     getCount : function(){
13693         return this.data.length || 0;
13694     },
13695
13696     /**
13697      * Gets the total number of records in the dataset as returned by the server.
13698      * <p>
13699      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13700      * the dataset size</em>
13701      */
13702     getTotalCount : function(){
13703         return this.totalLength || 0;
13704     },
13705
13706     /**
13707      * Returns the sort state of the Store as an object with two properties:
13708      * <pre><code>
13709  field {String} The name of the field by which the Records are sorted
13710  direction {String} The sort order, "ASC" or "DESC"
13711      * </code></pre>
13712      */
13713     getSortState : function(){
13714         return this.sortInfo;
13715     },
13716
13717     // private
13718     applySort : function(){
13719         if(this.sortInfo && !this.remoteSort){
13720             var s = this.sortInfo, f = s.field;
13721             var st = this.fields.get(f).sortType;
13722             var fn = function(r1, r2){
13723                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13724                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13725             };
13726             this.data.sort(s.direction, fn);
13727             if(this.snapshot && this.snapshot != this.data){
13728                 this.snapshot.sort(s.direction, fn);
13729             }
13730         }
13731     },
13732
13733     /**
13734      * Sets the default sort column and order to be used by the next load operation.
13735      * @param {String} fieldName The name of the field to sort by.
13736      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13737      */
13738     setDefaultSort : function(field, dir){
13739         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13740     },
13741
13742     /**
13743      * Sort the Records.
13744      * If remote sorting is used, the sort is performed on the server, and the cache is
13745      * reloaded. If local sorting is used, the cache is sorted internally.
13746      * @param {String} fieldName The name of the field to sort by.
13747      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13748      */
13749     sort : function(fieldName, dir){
13750         var f = this.fields.get(fieldName);
13751         if(!dir){
13752             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13753             
13754             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13755                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13756             }else{
13757                 dir = f.sortDir;
13758             }
13759         }
13760         this.sortToggle[f.name] = dir;
13761         this.sortInfo = {field: f.name, direction: dir};
13762         if(!this.remoteSort){
13763             this.applySort();
13764             this.fireEvent("datachanged", this);
13765         }else{
13766             this.load(this.lastOptions);
13767         }
13768     },
13769
13770     /**
13771      * Calls the specified function for each of the Records in the cache.
13772      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13773      * Returning <em>false</em> aborts and exits the iteration.
13774      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13775      */
13776     each : function(fn, scope){
13777         this.data.each(fn, scope);
13778     },
13779
13780     /**
13781      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13782      * (e.g., during paging).
13783      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13784      */
13785     getModifiedRecords : function(){
13786         return this.modified;
13787     },
13788
13789     // private
13790     createFilterFn : function(property, value, anyMatch){
13791         if(!value.exec){ // not a regex
13792             value = String(value);
13793             if(value.length == 0){
13794                 return false;
13795             }
13796             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13797         }
13798         return function(r){
13799             return value.test(r.data[property]);
13800         };
13801     },
13802
13803     /**
13804      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13805      * @param {String} property A field on your records
13806      * @param {Number} start The record index to start at (defaults to 0)
13807      * @param {Number} end The last record index to include (defaults to length - 1)
13808      * @return {Number} The sum
13809      */
13810     sum : function(property, start, end){
13811         var rs = this.data.items, v = 0;
13812         start = start || 0;
13813         end = (end || end === 0) ? end : rs.length-1;
13814
13815         for(var i = start; i <= end; i++){
13816             v += (rs[i].data[property] || 0);
13817         }
13818         return v;
13819     },
13820
13821     /**
13822      * Filter the records by a specified property.
13823      * @param {String} field A field on your records
13824      * @param {String/RegExp} value Either a string that the field
13825      * should start with or a RegExp to test against the field
13826      * @param {Boolean} anyMatch True to match any part not just the beginning
13827      */
13828     filter : function(property, value, anyMatch){
13829         var fn = this.createFilterFn(property, value, anyMatch);
13830         return fn ? this.filterBy(fn) : this.clearFilter();
13831     },
13832
13833     /**
13834      * Filter by a function. The specified function will be called with each
13835      * record in this data source. If the function returns true the record is included,
13836      * otherwise it is filtered.
13837      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13838      * @param {Object} scope (optional) The scope of the function (defaults to this)
13839      */
13840     filterBy : function(fn, scope){
13841         this.snapshot = this.snapshot || this.data;
13842         this.data = this.queryBy(fn, scope||this);
13843         this.fireEvent("datachanged", this);
13844     },
13845
13846     /**
13847      * Query the records by a specified property.
13848      * @param {String} field A field on your records
13849      * @param {String/RegExp} value Either a string that the field
13850      * should start with or a RegExp to test against the field
13851      * @param {Boolean} anyMatch True to match any part not just the beginning
13852      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13853      */
13854     query : function(property, value, anyMatch){
13855         var fn = this.createFilterFn(property, value, anyMatch);
13856         return fn ? this.queryBy(fn) : this.data.clone();
13857     },
13858
13859     /**
13860      * Query by a function. The specified function will be called with each
13861      * record in this data source. If the function returns true the record is included
13862      * in the results.
13863      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13864      * @param {Object} scope (optional) The scope of the function (defaults to this)
13865       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13866      **/
13867     queryBy : function(fn, scope){
13868         var data = this.snapshot || this.data;
13869         return data.filterBy(fn, scope||this);
13870     },
13871
13872     /**
13873      * Collects unique values for a particular dataIndex from this store.
13874      * @param {String} dataIndex The property to collect
13875      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13876      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13877      * @return {Array} An array of the unique values
13878      **/
13879     collect : function(dataIndex, allowNull, bypassFilter){
13880         var d = (bypassFilter === true && this.snapshot) ?
13881                 this.snapshot.items : this.data.items;
13882         var v, sv, r = [], l = {};
13883         for(var i = 0, len = d.length; i < len; i++){
13884             v = d[i].data[dataIndex];
13885             sv = String(v);
13886             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13887                 l[sv] = true;
13888                 r[r.length] = v;
13889             }
13890         }
13891         return r;
13892     },
13893
13894     /**
13895      * Revert to a view of the Record cache with no filtering applied.
13896      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13897      */
13898     clearFilter : function(suppressEvent){
13899         if(this.snapshot && this.snapshot != this.data){
13900             this.data = this.snapshot;
13901             delete this.snapshot;
13902             if(suppressEvent !== true){
13903                 this.fireEvent("datachanged", this);
13904             }
13905         }
13906     },
13907
13908     // private
13909     afterEdit : function(record){
13910         if(this.modified.indexOf(record) == -1){
13911             this.modified.push(record);
13912         }
13913         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13914     },
13915     
13916     // private
13917     afterReject : function(record){
13918         this.modified.remove(record);
13919         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13920     },
13921
13922     // private
13923     afterCommit : function(record){
13924         this.modified.remove(record);
13925         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13926     },
13927
13928     /**
13929      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13930      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13931      */
13932     commitChanges : function(){
13933         var m = this.modified.slice(0);
13934         this.modified = [];
13935         for(var i = 0, len = m.length; i < len; i++){
13936             m[i].commit();
13937         }
13938     },
13939
13940     /**
13941      * Cancel outstanding changes on all changed records.
13942      */
13943     rejectChanges : function(){
13944         var m = this.modified.slice(0);
13945         this.modified = [];
13946         for(var i = 0, len = m.length; i < len; i++){
13947             m[i].reject();
13948         }
13949     },
13950
13951     onMetaChange : function(meta, rtype, o){
13952         this.recordType = rtype;
13953         this.fields = rtype.prototype.fields;
13954         delete this.snapshot;
13955         this.sortInfo = meta.sortInfo || this.sortInfo;
13956         this.modified = [];
13957         this.fireEvent('metachange', this, this.reader.meta);
13958     },
13959     
13960     moveIndex : function(data, type)
13961     {
13962         var index = this.indexOf(data);
13963         
13964         var newIndex = index + type;
13965         
13966         this.remove(data);
13967         
13968         this.insert(newIndex, data);
13969         
13970     }
13971 });/*
13972  * Based on:
13973  * Ext JS Library 1.1.1
13974  * Copyright(c) 2006-2007, Ext JS, LLC.
13975  *
13976  * Originally Released Under LGPL - original licence link has changed is not relivant.
13977  *
13978  * Fork - LGPL
13979  * <script type="text/javascript">
13980  */
13981
13982 /**
13983  * @class Roo.data.SimpleStore
13984  * @extends Roo.data.Store
13985  * Small helper class to make creating Stores from Array data easier.
13986  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13987  * @cfg {Array} fields An array of field definition objects, or field name strings.
13988  * @cfg {Object} an existing reader (eg. copied from another store)
13989  * @cfg {Array} data The multi-dimensional array of data
13990  * @constructor
13991  * @param {Object} config
13992  */
13993 Roo.data.SimpleStore = function(config)
13994 {
13995     Roo.data.SimpleStore.superclass.constructor.call(this, {
13996         isLocal : true,
13997         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13998                 id: config.id
13999             },
14000             Roo.data.Record.create(config.fields)
14001         ),
14002         proxy : new Roo.data.MemoryProxy(config.data)
14003     });
14004     this.load();
14005 };
14006 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14007  * Based on:
14008  * Ext JS Library 1.1.1
14009  * Copyright(c) 2006-2007, Ext JS, LLC.
14010  *
14011  * Originally Released Under LGPL - original licence link has changed is not relivant.
14012  *
14013  * Fork - LGPL
14014  * <script type="text/javascript">
14015  */
14016
14017 /**
14018 /**
14019  * @extends Roo.data.Store
14020  * @class Roo.data.JsonStore
14021  * Small helper class to make creating Stores for JSON data easier. <br/>
14022 <pre><code>
14023 var store = new Roo.data.JsonStore({
14024     url: 'get-images.php',
14025     root: 'images',
14026     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14027 });
14028 </code></pre>
14029  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14030  * JsonReader and HttpProxy (unless inline data is provided).</b>
14031  * @cfg {Array} fields An array of field definition objects, or field name strings.
14032  * @constructor
14033  * @param {Object} config
14034  */
14035 Roo.data.JsonStore = function(c){
14036     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14037         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14038         reader: new Roo.data.JsonReader(c, c.fields)
14039     }));
14040 };
14041 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14042  * Based on:
14043  * Ext JS Library 1.1.1
14044  * Copyright(c) 2006-2007, Ext JS, LLC.
14045  *
14046  * Originally Released Under LGPL - original licence link has changed is not relivant.
14047  *
14048  * Fork - LGPL
14049  * <script type="text/javascript">
14050  */
14051
14052  
14053 Roo.data.Field = function(config){
14054     if(typeof config == "string"){
14055         config = {name: config};
14056     }
14057     Roo.apply(this, config);
14058     
14059     if(!this.type){
14060         this.type = "auto";
14061     }
14062     
14063     var st = Roo.data.SortTypes;
14064     // named sortTypes are supported, here we look them up
14065     if(typeof this.sortType == "string"){
14066         this.sortType = st[this.sortType];
14067     }
14068     
14069     // set default sortType for strings and dates
14070     if(!this.sortType){
14071         switch(this.type){
14072             case "string":
14073                 this.sortType = st.asUCString;
14074                 break;
14075             case "date":
14076                 this.sortType = st.asDate;
14077                 break;
14078             default:
14079                 this.sortType = st.none;
14080         }
14081     }
14082
14083     // define once
14084     var stripRe = /[\$,%]/g;
14085
14086     // prebuilt conversion function for this field, instead of
14087     // switching every time we're reading a value
14088     if(!this.convert){
14089         var cv, dateFormat = this.dateFormat;
14090         switch(this.type){
14091             case "":
14092             case "auto":
14093             case undefined:
14094                 cv = function(v){ return v; };
14095                 break;
14096             case "string":
14097                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14098                 break;
14099             case "int":
14100                 cv = function(v){
14101                     return v !== undefined && v !== null && v !== '' ?
14102                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14103                     };
14104                 break;
14105             case "float":
14106                 cv = function(v){
14107                     return v !== undefined && v !== null && v !== '' ?
14108                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14109                     };
14110                 break;
14111             case "bool":
14112             case "boolean":
14113                 cv = function(v){ return v === true || v === "true" || v == 1; };
14114                 break;
14115             case "date":
14116                 cv = function(v){
14117                     if(!v){
14118                         return '';
14119                     }
14120                     if(v instanceof Date){
14121                         return v;
14122                     }
14123                     if(dateFormat){
14124                         if(dateFormat == "timestamp"){
14125                             return new Date(v*1000);
14126                         }
14127                         return Date.parseDate(v, dateFormat);
14128                     }
14129                     var parsed = Date.parse(v);
14130                     return parsed ? new Date(parsed) : null;
14131                 };
14132              break;
14133             
14134         }
14135         this.convert = cv;
14136     }
14137 };
14138
14139 Roo.data.Field.prototype = {
14140     dateFormat: null,
14141     defaultValue: "",
14142     mapping: null,
14143     sortType : null,
14144     sortDir : "ASC"
14145 };/*
14146  * Based on:
14147  * Ext JS Library 1.1.1
14148  * Copyright(c) 2006-2007, Ext JS, LLC.
14149  *
14150  * Originally Released Under LGPL - original licence link has changed is not relivant.
14151  *
14152  * Fork - LGPL
14153  * <script type="text/javascript">
14154  */
14155  
14156 // Base class for reading structured data from a data source.  This class is intended to be
14157 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14158
14159 /**
14160  * @class Roo.data.DataReader
14161  * Base class for reading structured data from a data source.  This class is intended to be
14162  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14163  */
14164
14165 Roo.data.DataReader = function(meta, recordType){
14166     
14167     this.meta = meta;
14168     
14169     this.recordType = recordType instanceof Array ? 
14170         Roo.data.Record.create(recordType) : recordType;
14171 };
14172
14173 Roo.data.DataReader.prototype = {
14174     
14175     
14176     readerType : 'Data',
14177      /**
14178      * Create an empty record
14179      * @param {Object} data (optional) - overlay some values
14180      * @return {Roo.data.Record} record created.
14181      */
14182     newRow :  function(d) {
14183         var da =  {};
14184         this.recordType.prototype.fields.each(function(c) {
14185             switch( c.type) {
14186                 case 'int' : da[c.name] = 0; break;
14187                 case 'date' : da[c.name] = new Date(); break;
14188                 case 'float' : da[c.name] = 0.0; break;
14189                 case 'boolean' : da[c.name] = false; break;
14190                 default : da[c.name] = ""; break;
14191             }
14192             
14193         });
14194         return new this.recordType(Roo.apply(da, d));
14195     }
14196     
14197     
14198 };/*
14199  * Based on:
14200  * Ext JS Library 1.1.1
14201  * Copyright(c) 2006-2007, Ext JS, LLC.
14202  *
14203  * Originally Released Under LGPL - original licence link has changed is not relivant.
14204  *
14205  * Fork - LGPL
14206  * <script type="text/javascript">
14207  */
14208
14209 /**
14210  * @class Roo.data.DataProxy
14211  * @extends Roo.data.Observable
14212  * This class is an abstract base class for implementations which provide retrieval of
14213  * unformatted data objects.<br>
14214  * <p>
14215  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14216  * (of the appropriate type which knows how to parse the data object) to provide a block of
14217  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14218  * <p>
14219  * Custom implementations must implement the load method as described in
14220  * {@link Roo.data.HttpProxy#load}.
14221  */
14222 Roo.data.DataProxy = function(){
14223     this.addEvents({
14224         /**
14225          * @event beforeload
14226          * Fires before a network request is made to retrieve a data object.
14227          * @param {Object} This DataProxy object.
14228          * @param {Object} params The params parameter to the load function.
14229          */
14230         beforeload : true,
14231         /**
14232          * @event load
14233          * Fires before the load method's callback is called.
14234          * @param {Object} This DataProxy object.
14235          * @param {Object} o The data object.
14236          * @param {Object} arg The callback argument object passed to the load function.
14237          */
14238         load : true,
14239         /**
14240          * @event loadexception
14241          * Fires if an Exception occurs during data retrieval.
14242          * @param {Object} This DataProxy object.
14243          * @param {Object} o The data object.
14244          * @param {Object} arg The callback argument object passed to the load function.
14245          * @param {Object} e The Exception.
14246          */
14247         loadexception : true
14248     });
14249     Roo.data.DataProxy.superclass.constructor.call(this);
14250 };
14251
14252 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14253
14254     /**
14255      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14256      */
14257 /*
14258  * Based on:
14259  * Ext JS Library 1.1.1
14260  * Copyright(c) 2006-2007, Ext JS, LLC.
14261  *
14262  * Originally Released Under LGPL - original licence link has changed is not relivant.
14263  *
14264  * Fork - LGPL
14265  * <script type="text/javascript">
14266  */
14267 /**
14268  * @class Roo.data.MemoryProxy
14269  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14270  * to the Reader when its load method is called.
14271  * @constructor
14272  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14273  */
14274 Roo.data.MemoryProxy = function(data){
14275     if (data.data) {
14276         data = data.data;
14277     }
14278     Roo.data.MemoryProxy.superclass.constructor.call(this);
14279     this.data = data;
14280 };
14281
14282 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14283     
14284     /**
14285      * Load data from the requested source (in this case an in-memory
14286      * data object passed to the constructor), read the data object into
14287      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14288      * process that block using the passed callback.
14289      * @param {Object} params This parameter is not used by the MemoryProxy class.
14290      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14291      * object into a block of Roo.data.Records.
14292      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14293      * The function must be passed <ul>
14294      * <li>The Record block object</li>
14295      * <li>The "arg" argument from the load function</li>
14296      * <li>A boolean success indicator</li>
14297      * </ul>
14298      * @param {Object} scope The scope in which to call the callback
14299      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14300      */
14301     load : function(params, reader, callback, scope, arg){
14302         params = params || {};
14303         var result;
14304         try {
14305             result = reader.readRecords(params.data ? params.data :this.data);
14306         }catch(e){
14307             this.fireEvent("loadexception", this, arg, null, e);
14308             callback.call(scope, null, arg, false);
14309             return;
14310         }
14311         callback.call(scope, result, arg, true);
14312     },
14313     
14314     // private
14315     update : function(params, records){
14316         
14317     }
14318 });/*
14319  * Based on:
14320  * Ext JS Library 1.1.1
14321  * Copyright(c) 2006-2007, Ext JS, LLC.
14322  *
14323  * Originally Released Under LGPL - original licence link has changed is not relivant.
14324  *
14325  * Fork - LGPL
14326  * <script type="text/javascript">
14327  */
14328 /**
14329  * @class Roo.data.HttpProxy
14330  * @extends Roo.data.DataProxy
14331  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14332  * configured to reference a certain URL.<br><br>
14333  * <p>
14334  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14335  * from which the running page was served.<br><br>
14336  * <p>
14337  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14338  * <p>
14339  * Be aware that to enable the browser to parse an XML document, the server must set
14340  * the Content-Type header in the HTTP response to "text/xml".
14341  * @constructor
14342  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14343  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14344  * will be used to make the request.
14345  */
14346 Roo.data.HttpProxy = function(conn){
14347     Roo.data.HttpProxy.superclass.constructor.call(this);
14348     // is conn a conn config or a real conn?
14349     this.conn = conn;
14350     this.useAjax = !conn || !conn.events;
14351   
14352 };
14353
14354 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14355     // thse are take from connection...
14356     
14357     /**
14358      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14359      */
14360     /**
14361      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14362      * extra parameters to each request made by this object. (defaults to undefined)
14363      */
14364     /**
14365      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14366      *  to each request made by this object. (defaults to undefined)
14367      */
14368     /**
14369      * @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)
14370      */
14371     /**
14372      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14373      */
14374      /**
14375      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14376      * @type Boolean
14377      */
14378   
14379
14380     /**
14381      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14382      * @type Boolean
14383      */
14384     /**
14385      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14386      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14387      * a finer-grained basis than the DataProxy events.
14388      */
14389     getConnection : function(){
14390         return this.useAjax ? Roo.Ajax : this.conn;
14391     },
14392
14393     /**
14394      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14395      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14396      * process that block using the passed callback.
14397      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14398      * for the request to the remote server.
14399      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14400      * object into a block of Roo.data.Records.
14401      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14402      * The function must be passed <ul>
14403      * <li>The Record block object</li>
14404      * <li>The "arg" argument from the load function</li>
14405      * <li>A boolean success indicator</li>
14406      * </ul>
14407      * @param {Object} scope The scope in which to call the callback
14408      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14409      */
14410     load : function(params, reader, callback, scope, arg){
14411         if(this.fireEvent("beforeload", this, params) !== false){
14412             var  o = {
14413                 params : params || {},
14414                 request: {
14415                     callback : callback,
14416                     scope : scope,
14417                     arg : arg
14418                 },
14419                 reader: reader,
14420                 callback : this.loadResponse,
14421                 scope: this
14422             };
14423             if(this.useAjax){
14424                 Roo.applyIf(o, this.conn);
14425                 if(this.activeRequest){
14426                     Roo.Ajax.abort(this.activeRequest);
14427                 }
14428                 this.activeRequest = Roo.Ajax.request(o);
14429             }else{
14430                 this.conn.request(o);
14431             }
14432         }else{
14433             callback.call(scope||this, null, arg, false);
14434         }
14435     },
14436
14437     // private
14438     loadResponse : function(o, success, response){
14439         delete this.activeRequest;
14440         if(!success){
14441             this.fireEvent("loadexception", this, o, response);
14442             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14443             return;
14444         }
14445         var result;
14446         try {
14447             result = o.reader.read(response);
14448         }catch(e){
14449             this.fireEvent("loadexception", this, o, response, e);
14450             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14451             return;
14452         }
14453         
14454         this.fireEvent("load", this, o, o.request.arg);
14455         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14456     },
14457
14458     // private
14459     update : function(dataSet){
14460
14461     },
14462
14463     // private
14464     updateResponse : function(dataSet){
14465
14466     }
14467 });/*
14468  * Based on:
14469  * Ext JS Library 1.1.1
14470  * Copyright(c) 2006-2007, Ext JS, LLC.
14471  *
14472  * Originally Released Under LGPL - original licence link has changed is not relivant.
14473  *
14474  * Fork - LGPL
14475  * <script type="text/javascript">
14476  */
14477
14478 /**
14479  * @class Roo.data.ScriptTagProxy
14480  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14481  * other than the originating domain of the running page.<br><br>
14482  * <p>
14483  * <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
14484  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14485  * <p>
14486  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14487  * source code that is used as the source inside a &lt;script> tag.<br><br>
14488  * <p>
14489  * In order for the browser to process the returned data, the server must wrap the data object
14490  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14491  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14492  * depending on whether the callback name was passed:
14493  * <p>
14494  * <pre><code>
14495 boolean scriptTag = false;
14496 String cb = request.getParameter("callback");
14497 if (cb != null) {
14498     scriptTag = true;
14499     response.setContentType("text/javascript");
14500 } else {
14501     response.setContentType("application/x-json");
14502 }
14503 Writer out = response.getWriter();
14504 if (scriptTag) {
14505     out.write(cb + "(");
14506 }
14507 out.print(dataBlock.toJsonString());
14508 if (scriptTag) {
14509     out.write(");");
14510 }
14511 </pre></code>
14512  *
14513  * @constructor
14514  * @param {Object} config A configuration object.
14515  */
14516 Roo.data.ScriptTagProxy = function(config){
14517     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14518     Roo.apply(this, config);
14519     this.head = document.getElementsByTagName("head")[0];
14520 };
14521
14522 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14523
14524 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14525     /**
14526      * @cfg {String} url The URL from which to request the data object.
14527      */
14528     /**
14529      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14530      */
14531     timeout : 30000,
14532     /**
14533      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14534      * the server the name of the callback function set up by the load call to process the returned data object.
14535      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14536      * javascript output which calls this named function passing the data object as its only parameter.
14537      */
14538     callbackParam : "callback",
14539     /**
14540      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14541      * name to the request.
14542      */
14543     nocache : true,
14544
14545     /**
14546      * Load data from the configured URL, read the data object into
14547      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14548      * process that block using the passed callback.
14549      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14550      * for the request to the remote server.
14551      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14552      * object into a block of Roo.data.Records.
14553      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14554      * The function must be passed <ul>
14555      * <li>The Record block object</li>
14556      * <li>The "arg" argument from the load function</li>
14557      * <li>A boolean success indicator</li>
14558      * </ul>
14559      * @param {Object} scope The scope in which to call the callback
14560      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14561      */
14562     load : function(params, reader, callback, scope, arg){
14563         if(this.fireEvent("beforeload", this, params) !== false){
14564
14565             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14566
14567             var url = this.url;
14568             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14569             if(this.nocache){
14570                 url += "&_dc=" + (new Date().getTime());
14571             }
14572             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14573             var trans = {
14574                 id : transId,
14575                 cb : "stcCallback"+transId,
14576                 scriptId : "stcScript"+transId,
14577                 params : params,
14578                 arg : arg,
14579                 url : url,
14580                 callback : callback,
14581                 scope : scope,
14582                 reader : reader
14583             };
14584             var conn = this;
14585
14586             window[trans.cb] = function(o){
14587                 conn.handleResponse(o, trans);
14588             };
14589
14590             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14591
14592             if(this.autoAbort !== false){
14593                 this.abort();
14594             }
14595
14596             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14597
14598             var script = document.createElement("script");
14599             script.setAttribute("src", url);
14600             script.setAttribute("type", "text/javascript");
14601             script.setAttribute("id", trans.scriptId);
14602             this.head.appendChild(script);
14603
14604             this.trans = trans;
14605         }else{
14606             callback.call(scope||this, null, arg, false);
14607         }
14608     },
14609
14610     // private
14611     isLoading : function(){
14612         return this.trans ? true : false;
14613     },
14614
14615     /**
14616      * Abort the current server request.
14617      */
14618     abort : function(){
14619         if(this.isLoading()){
14620             this.destroyTrans(this.trans);
14621         }
14622     },
14623
14624     // private
14625     destroyTrans : function(trans, isLoaded){
14626         this.head.removeChild(document.getElementById(trans.scriptId));
14627         clearTimeout(trans.timeoutId);
14628         if(isLoaded){
14629             window[trans.cb] = undefined;
14630             try{
14631                 delete window[trans.cb];
14632             }catch(e){}
14633         }else{
14634             // if hasn't been loaded, wait for load to remove it to prevent script error
14635             window[trans.cb] = function(){
14636                 window[trans.cb] = undefined;
14637                 try{
14638                     delete window[trans.cb];
14639                 }catch(e){}
14640             };
14641         }
14642     },
14643
14644     // private
14645     handleResponse : function(o, trans){
14646         this.trans = false;
14647         this.destroyTrans(trans, true);
14648         var result;
14649         try {
14650             result = trans.reader.readRecords(o);
14651         }catch(e){
14652             this.fireEvent("loadexception", this, o, trans.arg, e);
14653             trans.callback.call(trans.scope||window, null, trans.arg, false);
14654             return;
14655         }
14656         this.fireEvent("load", this, o, trans.arg);
14657         trans.callback.call(trans.scope||window, result, trans.arg, true);
14658     },
14659
14660     // private
14661     handleFailure : function(trans){
14662         this.trans = false;
14663         this.destroyTrans(trans, false);
14664         this.fireEvent("loadexception", this, null, trans.arg);
14665         trans.callback.call(trans.scope||window, null, trans.arg, false);
14666     }
14667 });/*
14668  * Based on:
14669  * Ext JS Library 1.1.1
14670  * Copyright(c) 2006-2007, Ext JS, LLC.
14671  *
14672  * Originally Released Under LGPL - original licence link has changed is not relivant.
14673  *
14674  * Fork - LGPL
14675  * <script type="text/javascript">
14676  */
14677
14678 /**
14679  * @class Roo.data.JsonReader
14680  * @extends Roo.data.DataReader
14681  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14682  * based on mappings in a provided Roo.data.Record constructor.
14683  * 
14684  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14685  * in the reply previously. 
14686  * 
14687  * <p>
14688  * Example code:
14689  * <pre><code>
14690 var RecordDef = Roo.data.Record.create([
14691     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14692     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14693 ]);
14694 var myReader = new Roo.data.JsonReader({
14695     totalProperty: "results",    // The property which contains the total dataset size (optional)
14696     root: "rows",                // The property which contains an Array of row objects
14697     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14698 }, RecordDef);
14699 </code></pre>
14700  * <p>
14701  * This would consume a JSON file like this:
14702  * <pre><code>
14703 { 'results': 2, 'rows': [
14704     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14705     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14706 }
14707 </code></pre>
14708  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14709  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14710  * paged from the remote server.
14711  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14712  * @cfg {String} root name of the property which contains the Array of row objects.
14713  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14714  * @cfg {Array} fields Array of field definition objects
14715  * @constructor
14716  * Create a new JsonReader
14717  * @param {Object} meta Metadata configuration options
14718  * @param {Object} recordType Either an Array of field definition objects,
14719  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14720  */
14721 Roo.data.JsonReader = function(meta, recordType){
14722     
14723     meta = meta || {};
14724     // set some defaults:
14725     Roo.applyIf(meta, {
14726         totalProperty: 'total',
14727         successProperty : 'success',
14728         root : 'data',
14729         id : 'id'
14730     });
14731     
14732     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14733 };
14734 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14735     
14736     readerType : 'Json',
14737     
14738     /**
14739      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14740      * Used by Store query builder to append _requestMeta to params.
14741      * 
14742      */
14743     metaFromRemote : false,
14744     /**
14745      * This method is only used by a DataProxy which has retrieved data from a remote server.
14746      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14747      * @return {Object} data A data block which is used by an Roo.data.Store object as
14748      * a cache of Roo.data.Records.
14749      */
14750     read : function(response){
14751         var json = response.responseText;
14752        
14753         var o = /* eval:var:o */ eval("("+json+")");
14754         if(!o) {
14755             throw {message: "JsonReader.read: Json object not found"};
14756         }
14757         
14758         if(o.metaData){
14759             
14760             delete this.ef;
14761             this.metaFromRemote = true;
14762             this.meta = o.metaData;
14763             this.recordType = Roo.data.Record.create(o.metaData.fields);
14764             this.onMetaChange(this.meta, this.recordType, o);
14765         }
14766         return this.readRecords(o);
14767     },
14768
14769     // private function a store will implement
14770     onMetaChange : function(meta, recordType, o){
14771
14772     },
14773
14774     /**
14775          * @ignore
14776          */
14777     simpleAccess: function(obj, subsc) {
14778         return obj[subsc];
14779     },
14780
14781         /**
14782          * @ignore
14783          */
14784     getJsonAccessor: function(){
14785         var re = /[\[\.]/;
14786         return function(expr) {
14787             try {
14788                 return(re.test(expr))
14789                     ? new Function("obj", "return obj." + expr)
14790                     : function(obj){
14791                         return obj[expr];
14792                     };
14793             } catch(e){}
14794             return Roo.emptyFn;
14795         };
14796     }(),
14797
14798     /**
14799      * Create a data block containing Roo.data.Records from an XML document.
14800      * @param {Object} o An object which contains an Array of row objects in the property specified
14801      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14802      * which contains the total size of the dataset.
14803      * @return {Object} data A data block which is used by an Roo.data.Store object as
14804      * a cache of Roo.data.Records.
14805      */
14806     readRecords : function(o){
14807         /**
14808          * After any data loads, the raw JSON data is available for further custom processing.
14809          * @type Object
14810          */
14811         this.o = o;
14812         var s = this.meta, Record = this.recordType,
14813             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14814
14815 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14816         if (!this.ef) {
14817             if(s.totalProperty) {
14818                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14819                 }
14820                 if(s.successProperty) {
14821                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14822                 }
14823                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14824                 if (s.id) {
14825                         var g = this.getJsonAccessor(s.id);
14826                         this.getId = function(rec) {
14827                                 var r = g(rec);  
14828                                 return (r === undefined || r === "") ? null : r;
14829                         };
14830                 } else {
14831                         this.getId = function(){return null;};
14832                 }
14833             this.ef = [];
14834             for(var jj = 0; jj < fl; jj++){
14835                 f = fi[jj];
14836                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14837                 this.ef[jj] = this.getJsonAccessor(map);
14838             }
14839         }
14840
14841         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14842         if(s.totalProperty){
14843             var vt = parseInt(this.getTotal(o), 10);
14844             if(!isNaN(vt)){
14845                 totalRecords = vt;
14846             }
14847         }
14848         if(s.successProperty){
14849             var vs = this.getSuccess(o);
14850             if(vs === false || vs === 'false'){
14851                 success = false;
14852             }
14853         }
14854         var records = [];
14855         for(var i = 0; i < c; i++){
14856                 var n = root[i];
14857             var values = {};
14858             var id = this.getId(n);
14859             for(var j = 0; j < fl; j++){
14860                 f = fi[j];
14861             var v = this.ef[j](n);
14862             if (!f.convert) {
14863                 Roo.log('missing convert for ' + f.name);
14864                 Roo.log(f);
14865                 continue;
14866             }
14867             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14868             }
14869             var record = new Record(values, id);
14870             record.json = n;
14871             records[i] = record;
14872         }
14873         return {
14874             raw : o,
14875             success : success,
14876             records : records,
14877             totalRecords : totalRecords
14878         };
14879     },
14880     // used when loading children.. @see loadDataFromChildren
14881     toLoadData: function(rec)
14882     {
14883         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14885         return { data : data, total : data.length };
14886         
14887     }
14888 });/*
14889  * Based on:
14890  * Ext JS Library 1.1.1
14891  * Copyright(c) 2006-2007, Ext JS, LLC.
14892  *
14893  * Originally Released Under LGPL - original licence link has changed is not relivant.
14894  *
14895  * Fork - LGPL
14896  * <script type="text/javascript">
14897  */
14898
14899 /**
14900  * @class Roo.data.ArrayReader
14901  * @extends Roo.data.DataReader
14902  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14903  * Each element of that Array represents a row of data fields. The
14904  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14905  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14906  * <p>
14907  * Example code:.
14908  * <pre><code>
14909 var RecordDef = Roo.data.Record.create([
14910     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14911     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14912 ]);
14913 var myReader = new Roo.data.ArrayReader({
14914     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14915 }, RecordDef);
14916 </code></pre>
14917  * <p>
14918  * This would consume an Array like this:
14919  * <pre><code>
14920 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14921   </code></pre>
14922  
14923  * @constructor
14924  * Create a new JsonReader
14925  * @param {Object} meta Metadata configuration options.
14926  * @param {Object|Array} recordType Either an Array of field definition objects
14927  * 
14928  * @cfg {Array} fields Array of field definition objects
14929  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14930  * as specified to {@link Roo.data.Record#create},
14931  * or an {@link Roo.data.Record} object
14932  *
14933  * 
14934  * created using {@link Roo.data.Record#create}.
14935  */
14936 Roo.data.ArrayReader = function(meta, recordType)
14937 {    
14938     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14939 };
14940
14941 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14942     
14943       /**
14944      * Create a data block containing Roo.data.Records from an XML document.
14945      * @param {Object} o An Array of row objects which represents the dataset.
14946      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14947      * a cache of Roo.data.Records.
14948      */
14949     readRecords : function(o)
14950     {
14951         var sid = this.meta ? this.meta.id : null;
14952         var recordType = this.recordType, fields = recordType.prototype.fields;
14953         var records = [];
14954         var root = o;
14955         for(var i = 0; i < root.length; i++){
14956                 var n = root[i];
14957             var values = {};
14958             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14959             for(var j = 0, jlen = fields.length; j < jlen; j++){
14960                 var f = fields.items[j];
14961                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14962                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14963                 v = f.convert(v);
14964                 values[f.name] = v;
14965             }
14966             var record = new recordType(values, id);
14967             record.json = n;
14968             records[records.length] = record;
14969         }
14970         return {
14971             records : records,
14972             totalRecords : records.length
14973         };
14974     },
14975     // used when loading children.. @see loadDataFromChildren
14976     toLoadData: function(rec)
14977     {
14978         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14979         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14980         
14981     }
14982     
14983     
14984 });/*
14985  * - LGPL
14986  * * 
14987  */
14988
14989 /**
14990  * @class Roo.bootstrap.ComboBox
14991  * @extends Roo.bootstrap.TriggerField
14992  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14993  * @cfg {Boolean} append (true|false) default false
14994  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14995  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14996  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14997  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14998  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14999  * @cfg {Boolean} animate default true
15000  * @cfg {Boolean} emptyResultText only for touch device
15001  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15002  * @cfg {String} emptyTitle default ''
15003  * @cfg {Number} width fixed with? experimental
15004  * @constructor
15005  * Create a new ComboBox.
15006  * @param {Object} config Configuration options
15007  */
15008 Roo.bootstrap.ComboBox = function(config){
15009     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15010     this.addEvents({
15011         /**
15012          * @event expand
15013          * Fires when the dropdown list is expanded
15014         * @param {Roo.bootstrap.ComboBox} combo This combo box
15015         */
15016         'expand' : true,
15017         /**
15018          * @event collapse
15019          * Fires when the dropdown list is collapsed
15020         * @param {Roo.bootstrap.ComboBox} combo This combo box
15021         */
15022         'collapse' : true,
15023         /**
15024          * @event beforeselect
15025          * Fires before a list item is selected. Return false to cancel the selection.
15026         * @param {Roo.bootstrap.ComboBox} combo This combo box
15027         * @param {Roo.data.Record} record The data record returned from the underlying store
15028         * @param {Number} index The index of the selected item in the dropdown list
15029         */
15030         'beforeselect' : true,
15031         /**
15032          * @event select
15033          * Fires when a list item is selected
15034         * @param {Roo.bootstrap.ComboBox} combo This combo box
15035         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15036         * @param {Number} index The index of the selected item in the dropdown list
15037         */
15038         'select' : true,
15039         /**
15040          * @event beforequery
15041          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15042          * The event object passed has these properties:
15043         * @param {Roo.bootstrap.ComboBox} combo This combo box
15044         * @param {String} query The query
15045         * @param {Boolean} forceAll true to force "all" query
15046         * @param {Boolean} cancel true to cancel the query
15047         * @param {Object} e The query event object
15048         */
15049         'beforequery': true,
15050          /**
15051          * @event add
15052          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15053         * @param {Roo.bootstrap.ComboBox} combo This combo box
15054         */
15055         'add' : true,
15056         /**
15057          * @event edit
15058          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15059         * @param {Roo.bootstrap.ComboBox} combo This combo box
15060         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15061         */
15062         'edit' : true,
15063         /**
15064          * @event remove
15065          * Fires when the remove value from the combobox array
15066         * @param {Roo.bootstrap.ComboBox} combo This combo box
15067         */
15068         'remove' : true,
15069         /**
15070          * @event afterremove
15071          * Fires when the remove value from the combobox array
15072         * @param {Roo.bootstrap.ComboBox} combo This combo box
15073         */
15074         'afterremove' : true,
15075         /**
15076          * @event specialfilter
15077          * Fires when specialfilter
15078             * @param {Roo.bootstrap.ComboBox} combo This combo box
15079             */
15080         'specialfilter' : true,
15081         /**
15082          * @event tick
15083          * Fires when tick the element
15084             * @param {Roo.bootstrap.ComboBox} combo This combo box
15085             */
15086         'tick' : true,
15087         /**
15088          * @event touchviewdisplay
15089          * Fires when touch view require special display (default is using displayField)
15090             * @param {Roo.bootstrap.ComboBox} combo This combo box
15091             * @param {Object} cfg set html .
15092             */
15093         'touchviewdisplay' : true
15094         
15095     });
15096     
15097     this.item = [];
15098     this.tickItems = [];
15099     
15100     this.selectedIndex = -1;
15101     if(this.mode == 'local'){
15102         if(config.queryDelay === undefined){
15103             this.queryDelay = 10;
15104         }
15105         if(config.minChars === undefined){
15106             this.minChars = 0;
15107         }
15108     }
15109 };
15110
15111 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15112      
15113     /**
15114      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15115      * rendering into an Roo.Editor, defaults to false)
15116      */
15117     /**
15118      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15119      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15120      */
15121     /**
15122      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15123      */
15124     /**
15125      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15126      * the dropdown list (defaults to undefined, with no header element)
15127      */
15128
15129      /**
15130      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15131      */
15132      
15133      /**
15134      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15135      */
15136     listWidth: undefined,
15137     /**
15138      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15139      * mode = 'remote' or 'text' if mode = 'local')
15140      */
15141     displayField: undefined,
15142     
15143     /**
15144      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15145      * mode = 'remote' or 'value' if mode = 'local'). 
15146      * Note: use of a valueField requires the user make a selection
15147      * in order for a value to be mapped.
15148      */
15149     valueField: undefined,
15150     /**
15151      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15152      */
15153     modalTitle : '',
15154     
15155     /**
15156      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15157      * field's data value (defaults to the underlying DOM element's name)
15158      */
15159     hiddenName: undefined,
15160     /**
15161      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15162      */
15163     listClass: '',
15164     /**
15165      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15166      */
15167     selectedClass: 'active',
15168     
15169     /**
15170      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15171      */
15172     shadow:'sides',
15173     /**
15174      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15175      * anchor positions (defaults to 'tl-bl')
15176      */
15177     listAlign: 'tl-bl?',
15178     /**
15179      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15180      */
15181     maxHeight: 300,
15182     /**
15183      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15184      * query specified by the allQuery config option (defaults to 'query')
15185      */
15186     triggerAction: 'query',
15187     /**
15188      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15189      * (defaults to 4, does not apply if editable = false)
15190      */
15191     minChars : 4,
15192     /**
15193      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15194      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15195      */
15196     typeAhead: false,
15197     /**
15198      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15199      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15200      */
15201     queryDelay: 500,
15202     /**
15203      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15204      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15205      */
15206     pageSize: 0,
15207     /**
15208      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15209      * when editable = true (defaults to false)
15210      */
15211     selectOnFocus:false,
15212     /**
15213      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15214      */
15215     queryParam: 'query',
15216     /**
15217      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15218      * when mode = 'remote' (defaults to 'Loading...')
15219      */
15220     loadingText: 'Loading...',
15221     /**
15222      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15223      */
15224     resizable: false,
15225     /**
15226      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15227      */
15228     handleHeight : 8,
15229     /**
15230      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15231      * traditional select (defaults to true)
15232      */
15233     editable: true,
15234     /**
15235      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15236      */
15237     allQuery: '',
15238     /**
15239      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15240      */
15241     mode: 'remote',
15242     /**
15243      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15244      * listWidth has a higher value)
15245      */
15246     minListWidth : 70,
15247     /**
15248      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15249      * allow the user to set arbitrary text into the field (defaults to false)
15250      */
15251     forceSelection:false,
15252     /**
15253      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15254      * if typeAhead = true (defaults to 250)
15255      */
15256     typeAheadDelay : 250,
15257     /**
15258      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15259      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15260      */
15261     valueNotFoundText : undefined,
15262     /**
15263      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15264      */
15265     blockFocus : false,
15266     
15267     /**
15268      * @cfg {Boolean} disableClear Disable showing of clear button.
15269      */
15270     disableClear : false,
15271     /**
15272      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15273      */
15274     alwaysQuery : false,
15275     
15276     /**
15277      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15278      */
15279     multiple : false,
15280     
15281     /**
15282      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15283      */
15284     invalidClass : "has-warning",
15285     
15286     /**
15287      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15288      */
15289     validClass : "has-success",
15290     
15291     /**
15292      * @cfg {Boolean} specialFilter (true|false) special filter default false
15293      */
15294     specialFilter : false,
15295     
15296     /**
15297      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15298      */
15299     mobileTouchView : true,
15300     
15301     /**
15302      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15303      */
15304     useNativeIOS : false,
15305     
15306     /**
15307      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15308      */
15309     mobile_restrict_height : false,
15310     
15311     ios_options : false,
15312     
15313     //private
15314     addicon : false,
15315     editicon: false,
15316     
15317     page: 0,
15318     hasQuery: false,
15319     append: false,
15320     loadNext: false,
15321     autoFocus : true,
15322     tickable : false,
15323     btnPosition : 'right',
15324     triggerList : true,
15325     showToggleBtn : true,
15326     animate : true,
15327     emptyResultText: 'Empty',
15328     triggerText : 'Select',
15329     emptyTitle : '',
15330     width : false,
15331     
15332     // element that contains real text value.. (when hidden is used..)
15333     
15334     getAutoCreate : function()
15335     {   
15336         var cfg = false;
15337         //render
15338         /*
15339          * Render classic select for iso
15340          */
15341         
15342         if(Roo.isIOS && this.useNativeIOS){
15343             cfg = this.getAutoCreateNativeIOS();
15344             return cfg;
15345         }
15346         
15347         /*
15348          * Touch Devices
15349          */
15350         
15351         if(Roo.isTouch && this.mobileTouchView){
15352             cfg = this.getAutoCreateTouchView();
15353             return cfg;;
15354         }
15355         
15356         /*
15357          *  Normal ComboBox
15358          */
15359         if(!this.tickable){
15360             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15361             return cfg;
15362         }
15363         
15364         /*
15365          *  ComboBox with tickable selections
15366          */
15367              
15368         var align = this.labelAlign || this.parentLabelAlign();
15369         
15370         cfg = {
15371             cls : 'form-group roo-combobox-tickable' //input-group
15372         };
15373         
15374         var btn_text_select = '';
15375         var btn_text_done = '';
15376         var btn_text_cancel = '';
15377         
15378         if (this.btn_text_show) {
15379             btn_text_select = 'Select';
15380             btn_text_done = 'Done';
15381             btn_text_cancel = 'Cancel'; 
15382         }
15383         
15384         var buttons = {
15385             tag : 'div',
15386             cls : 'tickable-buttons',
15387             cn : [
15388                 {
15389                     tag : 'button',
15390                     type : 'button',
15391                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15392                     //html : this.triggerText
15393                     html: btn_text_select
15394                 },
15395                 {
15396                     tag : 'button',
15397                     type : 'button',
15398                     name : 'ok',
15399                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15400                     //html : 'Done'
15401                     html: btn_text_done
15402                 },
15403                 {
15404                     tag : 'button',
15405                     type : 'button',
15406                     name : 'cancel',
15407                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15408                     //html : 'Cancel'
15409                     html: btn_text_cancel
15410                 }
15411             ]
15412         };
15413         
15414         if(this.editable){
15415             buttons.cn.unshift({
15416                 tag: 'input',
15417                 cls: 'roo-select2-search-field-input'
15418             });
15419         }
15420         
15421         var _this = this;
15422         
15423         Roo.each(buttons.cn, function(c){
15424             if (_this.size) {
15425                 c.cls += ' btn-' + _this.size;
15426             }
15427
15428             if (_this.disabled) {
15429                 c.disabled = true;
15430             }
15431         });
15432         
15433         var box = {
15434             tag: 'div',
15435             style : 'display: contents',
15436             cn: [
15437                 {
15438                     tag: 'input',
15439                     type : 'hidden',
15440                     cls: 'form-hidden-field'
15441                 },
15442                 {
15443                     tag: 'ul',
15444                     cls: 'roo-select2-choices',
15445                     cn:[
15446                         {
15447                             tag: 'li',
15448                             cls: 'roo-select2-search-field',
15449                             cn: [
15450                                 buttons
15451                             ]
15452                         }
15453                     ]
15454                 }
15455             ]
15456         };
15457         
15458         var combobox = {
15459             cls: 'roo-select2-container input-group roo-select2-container-multi',
15460             cn: [
15461                 
15462                 box
15463 //                {
15464 //                    tag: 'ul',
15465 //                    cls: 'typeahead typeahead-long dropdown-menu',
15466 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15467 //                }
15468             ]
15469         };
15470         
15471         if(this.hasFeedback && !this.allowBlank){
15472             
15473             var feedback = {
15474                 tag: 'span',
15475                 cls: 'glyphicon form-control-feedback'
15476             };
15477
15478             combobox.cn.push(feedback);
15479         }
15480         
15481         
15482         
15483         var indicator = {
15484             tag : 'i',
15485             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15486             tooltip : 'This field is required'
15487         };
15488         if (Roo.bootstrap.version == 4) {
15489             indicator = {
15490                 tag : 'i',
15491                 style : 'display:none'
15492             };
15493         }
15494         if (align ==='left' && this.fieldLabel.length) {
15495             
15496             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15497             
15498             cfg.cn = [
15499                 indicator,
15500                 {
15501                     tag: 'label',
15502                     'for' :  id,
15503                     cls : 'control-label col-form-label',
15504                     html : this.fieldLabel
15505
15506                 },
15507                 {
15508                     cls : "", 
15509                     cn: [
15510                         combobox
15511                     ]
15512                 }
15513
15514             ];
15515             
15516             var labelCfg = cfg.cn[1];
15517             var contentCfg = cfg.cn[2];
15518             
15519
15520             if(this.indicatorpos == 'right'){
15521                 
15522                 cfg.cn = [
15523                     {
15524                         tag: 'label',
15525                         'for' :  id,
15526                         cls : 'control-label col-form-label',
15527                         cn : [
15528                             {
15529                                 tag : 'span',
15530                                 html : this.fieldLabel
15531                             },
15532                             indicator
15533                         ]
15534                     },
15535                     {
15536                         cls : "",
15537                         cn: [
15538                             combobox
15539                         ]
15540                     }
15541
15542                 ];
15543                 
15544                 
15545                 
15546                 labelCfg = cfg.cn[0];
15547                 contentCfg = cfg.cn[1];
15548             
15549             }
15550             
15551             if(this.labelWidth > 12){
15552                 labelCfg.style = "width: " + this.labelWidth + 'px';
15553             }
15554             if(this.width * 1 > 0){
15555                 contentCfg.style = "width: " + this.width + 'px';
15556             }
15557             if(this.labelWidth < 13 && this.labelmd == 0){
15558                 this.labelmd = this.labelWidth;
15559             }
15560             
15561             if(this.labellg > 0){
15562                 labelCfg.cls += ' col-lg-' + this.labellg;
15563                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15564             }
15565             
15566             if(this.labelmd > 0){
15567                 labelCfg.cls += ' col-md-' + this.labelmd;
15568                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15569             }
15570             
15571             if(this.labelsm > 0){
15572                 labelCfg.cls += ' col-sm-' + this.labelsm;
15573                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15574             }
15575             
15576             if(this.labelxs > 0){
15577                 labelCfg.cls += ' col-xs-' + this.labelxs;
15578                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15579             }
15580                 
15581                 
15582         } else if ( this.fieldLabel.length) {
15583 //                Roo.log(" label");
15584                  cfg.cn = [
15585                    indicator,
15586                     {
15587                         tag: 'label',
15588                         //cls : 'input-group-addon',
15589                         html : this.fieldLabel
15590                     },
15591                     combobox
15592                 ];
15593                 
15594                 if(this.indicatorpos == 'right'){
15595                     cfg.cn = [
15596                         {
15597                             tag: 'label',
15598                             //cls : 'input-group-addon',
15599                             html : this.fieldLabel
15600                         },
15601                         indicator,
15602                         combobox
15603                     ];
15604                     
15605                 }
15606
15607         } else {
15608             
15609 //                Roo.log(" no label && no align");
15610                 cfg = combobox
15611                      
15612                 
15613         }
15614          
15615         var settings=this;
15616         ['xs','sm','md','lg'].map(function(size){
15617             if (settings[size]) {
15618                 cfg.cls += ' col-' + size + '-' + settings[size];
15619             }
15620         });
15621         
15622         return cfg;
15623         
15624     },
15625     
15626     _initEventsCalled : false,
15627     
15628     // private
15629     initEvents: function()
15630     {   
15631         if (this._initEventsCalled) { // as we call render... prevent looping...
15632             return;
15633         }
15634         this._initEventsCalled = true;
15635         
15636         if (!this.store) {
15637             throw "can not find store for combo";
15638         }
15639         
15640         this.indicator = this.indicatorEl();
15641         
15642         this.store = Roo.factory(this.store, Roo.data);
15643         this.store.parent = this;
15644         
15645         // if we are building from html. then this element is so complex, that we can not really
15646         // use the rendered HTML.
15647         // so we have to trash and replace the previous code.
15648         if (Roo.XComponent.build_from_html) {
15649             // remove this element....
15650             var e = this.el.dom, k=0;
15651             while (e ) { e = e.previousSibling;  ++k;}
15652
15653             this.el.remove();
15654             
15655             this.el=false;
15656             this.rendered = false;
15657             
15658             this.render(this.parent().getChildContainer(true), k);
15659         }
15660         
15661         if(Roo.isIOS && this.useNativeIOS){
15662             this.initIOSView();
15663             return;
15664         }
15665         
15666         /*
15667          * Touch Devices
15668          */
15669         
15670         if(Roo.isTouch && this.mobileTouchView){
15671             this.initTouchView();
15672             return;
15673         }
15674         
15675         if(this.tickable){
15676             this.initTickableEvents();
15677             return;
15678         }
15679         
15680         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15681         
15682         if(this.hiddenName){
15683             
15684             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15685             
15686             this.hiddenField.dom.value =
15687                 this.hiddenValue !== undefined ? this.hiddenValue :
15688                 this.value !== undefined ? this.value : '';
15689
15690             // prevent input submission
15691             this.el.dom.removeAttribute('name');
15692             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15693              
15694              
15695         }
15696         //if(Roo.isGecko){
15697         //    this.el.dom.setAttribute('autocomplete', 'off');
15698         //}
15699         
15700         var cls = 'x-combo-list';
15701         
15702         //this.list = new Roo.Layer({
15703         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15704         //});
15705         
15706         var _this = this;
15707         
15708         (function(){
15709             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15710             _this.list.setWidth(lw);
15711         }).defer(100);
15712         
15713         this.list.on('mouseover', this.onViewOver, this);
15714         this.list.on('mousemove', this.onViewMove, this);
15715         this.list.on('scroll', this.onViewScroll, this);
15716         
15717         /*
15718         this.list.swallowEvent('mousewheel');
15719         this.assetHeight = 0;
15720
15721         if(this.title){
15722             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15723             this.assetHeight += this.header.getHeight();
15724         }
15725
15726         this.innerList = this.list.createChild({cls:cls+'-inner'});
15727         this.innerList.on('mouseover', this.onViewOver, this);
15728         this.innerList.on('mousemove', this.onViewMove, this);
15729         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15730         
15731         if(this.allowBlank && !this.pageSize && !this.disableClear){
15732             this.footer = this.list.createChild({cls:cls+'-ft'});
15733             this.pageTb = new Roo.Toolbar(this.footer);
15734            
15735         }
15736         if(this.pageSize){
15737             this.footer = this.list.createChild({cls:cls+'-ft'});
15738             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15739                     {pageSize: this.pageSize});
15740             
15741         }
15742         
15743         if (this.pageTb && this.allowBlank && !this.disableClear) {
15744             var _this = this;
15745             this.pageTb.add(new Roo.Toolbar.Fill(), {
15746                 cls: 'x-btn-icon x-btn-clear',
15747                 text: '&#160;',
15748                 handler: function()
15749                 {
15750                     _this.collapse();
15751                     _this.clearValue();
15752                     _this.onSelect(false, -1);
15753                 }
15754             });
15755         }
15756         if (this.footer) {
15757             this.assetHeight += this.footer.getHeight();
15758         }
15759         */
15760             
15761         if(!this.tpl){
15762             this.tpl = Roo.bootstrap.version == 4 ?
15763                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15764                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15765         }
15766
15767         this.view = new Roo.View(this.list, this.tpl, {
15768             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15769         });
15770         //this.view.wrapEl.setDisplayed(false);
15771         this.view.on('click', this.onViewClick, this);
15772         
15773         
15774         this.store.on('beforeload', this.onBeforeLoad, this);
15775         this.store.on('load', this.onLoad, this);
15776         this.store.on('loadexception', this.onLoadException, this);
15777         /*
15778         if(this.resizable){
15779             this.resizer = new Roo.Resizable(this.list,  {
15780                pinned:true, handles:'se'
15781             });
15782             this.resizer.on('resize', function(r, w, h){
15783                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15784                 this.listWidth = w;
15785                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15786                 this.restrictHeight();
15787             }, this);
15788             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15789         }
15790         */
15791         if(!this.editable){
15792             this.editable = true;
15793             this.setEditable(false);
15794         }
15795         
15796         /*
15797         
15798         if (typeof(this.events.add.listeners) != 'undefined') {
15799             
15800             this.addicon = this.wrap.createChild(
15801                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15802        
15803             this.addicon.on('click', function(e) {
15804                 this.fireEvent('add', this);
15805             }, this);
15806         }
15807         if (typeof(this.events.edit.listeners) != 'undefined') {
15808             
15809             this.editicon = this.wrap.createChild(
15810                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15811             if (this.addicon) {
15812                 this.editicon.setStyle('margin-left', '40px');
15813             }
15814             this.editicon.on('click', function(e) {
15815                 
15816                 // we fire even  if inothing is selected..
15817                 this.fireEvent('edit', this, this.lastData );
15818                 
15819             }, this);
15820         }
15821         */
15822         
15823         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15824             "up" : function(e){
15825                 this.inKeyMode = true;
15826                 this.selectPrev();
15827             },
15828
15829             "down" : function(e){
15830                 if(!this.isExpanded()){
15831                     this.onTriggerClick();
15832                 }else{
15833                     this.inKeyMode = true;
15834                     this.selectNext();
15835                 }
15836             },
15837
15838             "enter" : function(e){
15839 //                this.onViewClick();
15840                 //return true;
15841                 this.collapse();
15842                 
15843                 if(this.fireEvent("specialkey", this, e)){
15844                     this.onViewClick(false);
15845                 }
15846                 
15847                 return true;
15848             },
15849
15850             "esc" : function(e){
15851                 this.collapse();
15852             },
15853
15854             "tab" : function(e){
15855                 this.collapse();
15856                 
15857                 if(this.fireEvent("specialkey", this, e)){
15858                     this.onViewClick(false);
15859                 }
15860                 
15861                 return true;
15862             },
15863
15864             scope : this,
15865
15866             doRelay : function(foo, bar, hname){
15867                 if(hname == 'down' || this.scope.isExpanded()){
15868                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15869                 }
15870                 return true;
15871             },
15872
15873             forceKeyDown: true
15874         });
15875         
15876         
15877         this.queryDelay = Math.max(this.queryDelay || 10,
15878                 this.mode == 'local' ? 10 : 250);
15879         
15880         
15881         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15882         
15883         if(this.typeAhead){
15884             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15885         }
15886         if(this.editable !== false){
15887             this.inputEl().on("keyup", this.onKeyUp, this);
15888         }
15889         if(this.forceSelection){
15890             this.inputEl().on('blur', this.doForce, this);
15891         }
15892         
15893         if(this.multiple){
15894             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15895             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15896         }
15897     },
15898     
15899     initTickableEvents: function()
15900     {   
15901         this.createList();
15902         
15903         if(this.hiddenName){
15904             
15905             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15906             
15907             this.hiddenField.dom.value =
15908                 this.hiddenValue !== undefined ? this.hiddenValue :
15909                 this.value !== undefined ? this.value : '';
15910
15911             // prevent input submission
15912             this.el.dom.removeAttribute('name');
15913             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15914              
15915              
15916         }
15917         
15918 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15919         
15920         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15921         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15922         if(this.triggerList){
15923             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15924         }
15925          
15926         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15927         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15928         
15929         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15930         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15931         
15932         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15933         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15934         
15935         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15936         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15937         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15938         
15939         this.okBtn.hide();
15940         this.cancelBtn.hide();
15941         
15942         var _this = this;
15943         
15944         (function(){
15945             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15946             _this.list.setWidth(lw);
15947         }).defer(100);
15948         
15949         this.list.on('mouseover', this.onViewOver, this);
15950         this.list.on('mousemove', this.onViewMove, this);
15951         
15952         this.list.on('scroll', this.onViewScroll, this);
15953         
15954         if(!this.tpl){
15955             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15956                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15957         }
15958
15959         this.view = new Roo.View(this.list, this.tpl, {
15960             singleSelect:true,
15961             tickable:true,
15962             parent:this,
15963             store: this.store,
15964             selectedClass: this.selectedClass
15965         });
15966         
15967         //this.view.wrapEl.setDisplayed(false);
15968         this.view.on('click', this.onViewClick, this);
15969         
15970         
15971         
15972         this.store.on('beforeload', this.onBeforeLoad, this);
15973         this.store.on('load', this.onLoad, this);
15974         this.store.on('loadexception', this.onLoadException, this);
15975         
15976         if(this.editable){
15977             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15978                 "up" : function(e){
15979                     this.inKeyMode = true;
15980                     this.selectPrev();
15981                 },
15982
15983                 "down" : function(e){
15984                     this.inKeyMode = true;
15985                     this.selectNext();
15986                 },
15987
15988                 "enter" : function(e){
15989                     if(this.fireEvent("specialkey", this, e)){
15990                         this.onViewClick(false);
15991                     }
15992                     
15993                     return true;
15994                 },
15995
15996                 "esc" : function(e){
15997                     this.onTickableFooterButtonClick(e, false, false);
15998                 },
15999
16000                 "tab" : function(e){
16001                     this.fireEvent("specialkey", this, e);
16002                     
16003                     this.onTickableFooterButtonClick(e, false, false);
16004                     
16005                     return true;
16006                 },
16007
16008                 scope : this,
16009
16010                 doRelay : function(e, fn, key){
16011                     if(this.scope.isExpanded()){
16012                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16013                     }
16014                     return true;
16015                 },
16016
16017                 forceKeyDown: true
16018             });
16019         }
16020         
16021         this.queryDelay = Math.max(this.queryDelay || 10,
16022                 this.mode == 'local' ? 10 : 250);
16023         
16024         
16025         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16026         
16027         if(this.typeAhead){
16028             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16029         }
16030         
16031         if(this.editable !== false){
16032             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16033         }
16034         
16035         this.indicator = this.indicatorEl();
16036         
16037         if(this.indicator){
16038             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16039             this.indicator.hide();
16040         }
16041         
16042     },
16043
16044     onDestroy : function(){
16045         if(this.view){
16046             this.view.setStore(null);
16047             this.view.el.removeAllListeners();
16048             this.view.el.remove();
16049             this.view.purgeListeners();
16050         }
16051         if(this.list){
16052             this.list.dom.innerHTML  = '';
16053         }
16054         
16055         if(this.store){
16056             this.store.un('beforeload', this.onBeforeLoad, this);
16057             this.store.un('load', this.onLoad, this);
16058             this.store.un('loadexception', this.onLoadException, this);
16059         }
16060         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16061     },
16062
16063     // private
16064     fireKey : function(e){
16065         if(e.isNavKeyPress() && !this.list.isVisible()){
16066             this.fireEvent("specialkey", this, e);
16067         }
16068     },
16069
16070     // private
16071     onResize: function(w, h)
16072     {
16073         
16074         
16075 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16076 //        
16077 //        if(typeof w != 'number'){
16078 //            // we do not handle it!?!?
16079 //            return;
16080 //        }
16081 //        var tw = this.trigger.getWidth();
16082 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16083 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16084 //        var x = w - tw;
16085 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16086 //            
16087 //        //this.trigger.setStyle('left', x+'px');
16088 //        
16089 //        if(this.list && this.listWidth === undefined){
16090 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16091 //            this.list.setWidth(lw);
16092 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16093 //        }
16094         
16095     
16096         
16097     },
16098
16099     /**
16100      * Allow or prevent the user from directly editing the field text.  If false is passed,
16101      * the user will only be able to select from the items defined in the dropdown list.  This method
16102      * is the runtime equivalent of setting the 'editable' config option at config time.
16103      * @param {Boolean} value True to allow the user to directly edit the field text
16104      */
16105     setEditable : function(value){
16106         if(value == this.editable){
16107             return;
16108         }
16109         this.editable = value;
16110         if(!value){
16111             this.inputEl().dom.setAttribute('readOnly', true);
16112             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16113             this.inputEl().addClass('x-combo-noedit');
16114         }else{
16115             this.inputEl().dom.setAttribute('readOnly', false);
16116             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16117             this.inputEl().removeClass('x-combo-noedit');
16118         }
16119     },
16120
16121     // private
16122     
16123     onBeforeLoad : function(combo,opts){
16124         if(!this.hasFocus){
16125             return;
16126         }
16127          if (!opts.add) {
16128             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16129          }
16130         this.restrictHeight();
16131         this.selectedIndex = -1;
16132     },
16133
16134     // private
16135     onLoad : function(){
16136         
16137         this.hasQuery = false;
16138         
16139         if(!this.hasFocus){
16140             return;
16141         }
16142         
16143         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16144             this.loading.hide();
16145         }
16146         
16147         if(this.store.getCount() > 0){
16148             
16149             this.expand();
16150             this.restrictHeight();
16151             if(this.lastQuery == this.allQuery){
16152                 if(this.editable && !this.tickable){
16153                     this.inputEl().dom.select();
16154                 }
16155                 
16156                 if(
16157                     !this.selectByValue(this.value, true) &&
16158                     this.autoFocus && 
16159                     (
16160                         !this.store.lastOptions ||
16161                         typeof(this.store.lastOptions.add) == 'undefined' || 
16162                         this.store.lastOptions.add != true
16163                     )
16164                 ){
16165                     this.select(0, true);
16166                 }
16167             }else{
16168                 if(this.autoFocus){
16169                     this.selectNext();
16170                 }
16171                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16172                     this.taTask.delay(this.typeAheadDelay);
16173                 }
16174             }
16175         }else{
16176             this.onEmptyResults();
16177         }
16178         
16179         //this.el.focus();
16180     },
16181     // private
16182     onLoadException : function()
16183     {
16184         this.hasQuery = false;
16185         
16186         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16187             this.loading.hide();
16188         }
16189         
16190         if(this.tickable && this.editable){
16191             return;
16192         }
16193         
16194         this.collapse();
16195         // only causes errors at present
16196         //Roo.log(this.store.reader.jsonData);
16197         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16198             // fixme
16199             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16200         //}
16201         
16202         
16203     },
16204     // private
16205     onTypeAhead : function(){
16206         if(this.store.getCount() > 0){
16207             var r = this.store.getAt(0);
16208             var newValue = r.data[this.displayField];
16209             var len = newValue.length;
16210             var selStart = this.getRawValue().length;
16211             
16212             if(selStart != len){
16213                 this.setRawValue(newValue);
16214                 this.selectText(selStart, newValue.length);
16215             }
16216         }
16217     },
16218
16219     // private
16220     onSelect : function(record, index){
16221         
16222         if(this.fireEvent('beforeselect', this, record, index) !== false){
16223         
16224             this.setFromData(index > -1 ? record.data : false);
16225             
16226             this.collapse();
16227             this.fireEvent('select', this, record, index);
16228         }
16229     },
16230
16231     /**
16232      * Returns the currently selected field value or empty string if no value is set.
16233      * @return {String} value The selected value
16234      */
16235     getValue : function()
16236     {
16237         if(Roo.isIOS && this.useNativeIOS){
16238             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16239         }
16240         
16241         if(this.multiple){
16242             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16243         }
16244         
16245         if(this.valueField){
16246             return typeof this.value != 'undefined' ? this.value : '';
16247         }else{
16248             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16249         }
16250     },
16251     
16252     getRawValue : function()
16253     {
16254         if(Roo.isIOS && this.useNativeIOS){
16255             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16256         }
16257         
16258         var v = this.inputEl().getValue();
16259         
16260         return v;
16261     },
16262
16263     /**
16264      * Clears any text/value currently set in the field
16265      */
16266     clearValue : function(){
16267         
16268         if(this.hiddenField){
16269             this.hiddenField.dom.value = '';
16270         }
16271         this.value = '';
16272         this.setRawValue('');
16273         this.lastSelectionText = '';
16274         this.lastData = false;
16275         
16276         var close = this.closeTriggerEl();
16277         
16278         if(close){
16279             close.hide();
16280         }
16281         
16282         this.validate();
16283         
16284     },
16285
16286     /**
16287      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16288      * will be displayed in the field.  If the value does not match the data value of an existing item,
16289      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16290      * Otherwise the field will be blank (although the value will still be set).
16291      * @param {String} value The value to match
16292      */
16293     setValue : function(v)
16294     {
16295         if(Roo.isIOS && this.useNativeIOS){
16296             this.setIOSValue(v);
16297             return;
16298         }
16299         
16300         if(this.multiple){
16301             this.syncValue();
16302             return;
16303         }
16304         
16305         var text = v;
16306         if(this.valueField){
16307             var r = this.findRecord(this.valueField, v);
16308             if(r){
16309                 text = r.data[this.displayField];
16310             }else if(this.valueNotFoundText !== undefined){
16311                 text = this.valueNotFoundText;
16312             }
16313         }
16314         this.lastSelectionText = text;
16315         if(this.hiddenField){
16316             this.hiddenField.dom.value = v;
16317         }
16318         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16319         this.value = v;
16320         
16321         var close = this.closeTriggerEl();
16322         
16323         if(close){
16324             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16325         }
16326         
16327         this.validate();
16328     },
16329     /**
16330      * @property {Object} the last set data for the element
16331      */
16332     
16333     lastData : false,
16334     /**
16335      * Sets the value of the field based on a object which is related to the record format for the store.
16336      * @param {Object} value the value to set as. or false on reset?
16337      */
16338     setFromData : function(o){
16339         
16340         if(this.multiple){
16341             this.addItem(o);
16342             return;
16343         }
16344             
16345         var dv = ''; // display value
16346         var vv = ''; // value value..
16347         this.lastData = o;
16348         if (this.displayField) {
16349             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16350         } else {
16351             // this is an error condition!!!
16352             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16353         }
16354         
16355         if(this.valueField){
16356             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16357         }
16358         
16359         var close = this.closeTriggerEl();
16360         
16361         if(close){
16362             if(dv.length || vv * 1 > 0){
16363                 close.show() ;
16364                 this.blockFocus=true;
16365             } else {
16366                 close.hide();
16367             }             
16368         }
16369         
16370         if(this.hiddenField){
16371             this.hiddenField.dom.value = vv;
16372             
16373             this.lastSelectionText = dv;
16374             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16375             this.value = vv;
16376             return;
16377         }
16378         // no hidden field.. - we store the value in 'value', but still display
16379         // display field!!!!
16380         this.lastSelectionText = dv;
16381         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16382         this.value = vv;
16383         
16384         
16385         
16386     },
16387     // private
16388     reset : function(){
16389         // overridden so that last data is reset..
16390         
16391         if(this.multiple){
16392             this.clearItem();
16393             return;
16394         }
16395         
16396         this.setValue(this.originalValue);
16397         //this.clearInvalid();
16398         this.lastData = false;
16399         if (this.view) {
16400             this.view.clearSelections();
16401         }
16402         
16403         this.validate();
16404     },
16405     // private
16406     findRecord : function(prop, value){
16407         var record;
16408         if(this.store.getCount() > 0){
16409             this.store.each(function(r){
16410                 if(r.data[prop] == value){
16411                     record = r;
16412                     return false;
16413                 }
16414                 return true;
16415             });
16416         }
16417         return record;
16418     },
16419     
16420     getName: function()
16421     {
16422         // returns hidden if it's set..
16423         if (!this.rendered) {return ''};
16424         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16425         
16426     },
16427     // private
16428     onViewMove : function(e, t){
16429         this.inKeyMode = false;
16430     },
16431
16432     // private
16433     onViewOver : function(e, t){
16434         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16435             return;
16436         }
16437         var item = this.view.findItemFromChild(t);
16438         
16439         if(item){
16440             var index = this.view.indexOf(item);
16441             this.select(index, false);
16442         }
16443     },
16444
16445     // private
16446     onViewClick : function(view, doFocus, el, e)
16447     {
16448         var index = this.view.getSelectedIndexes()[0];
16449         
16450         var r = this.store.getAt(index);
16451         
16452         if(this.tickable){
16453             
16454             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16455                 return;
16456             }
16457             
16458             var rm = false;
16459             var _this = this;
16460             
16461             Roo.each(this.tickItems, function(v,k){
16462                 
16463                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16464                     Roo.log(v);
16465                     _this.tickItems.splice(k, 1);
16466                     
16467                     if(typeof(e) == 'undefined' && view == false){
16468                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16469                     }
16470                     
16471                     rm = true;
16472                     return;
16473                 }
16474             });
16475             
16476             if(rm){
16477                 return;
16478             }
16479             
16480             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16481                 this.tickItems.push(r.data);
16482             }
16483             
16484             if(typeof(e) == 'undefined' && view == false){
16485                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16486             }
16487                     
16488             return;
16489         }
16490         
16491         if(r){
16492             this.onSelect(r, index);
16493         }
16494         if(doFocus !== false && !this.blockFocus){
16495             this.inputEl().focus();
16496         }
16497     },
16498
16499     // private
16500     restrictHeight : function(){
16501         //this.innerList.dom.style.height = '';
16502         //var inner = this.innerList.dom;
16503         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16504         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16505         //this.list.beginUpdate();
16506         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16507         this.list.alignTo(this.inputEl(), this.listAlign);
16508         this.list.alignTo(this.inputEl(), this.listAlign);
16509         //this.list.endUpdate();
16510     },
16511
16512     // private
16513     onEmptyResults : function(){
16514         
16515         if(this.tickable && this.editable){
16516             this.hasFocus = false;
16517             this.restrictHeight();
16518             return;
16519         }
16520         
16521         this.collapse();
16522     },
16523
16524     /**
16525      * Returns true if the dropdown list is expanded, else false.
16526      */
16527     isExpanded : function(){
16528         return this.list.isVisible();
16529     },
16530
16531     /**
16532      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16533      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16534      * @param {String} value The data value of the item to select
16535      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16536      * selected item if it is not currently in view (defaults to true)
16537      * @return {Boolean} True if the value matched an item in the list, else false
16538      */
16539     selectByValue : function(v, scrollIntoView){
16540         if(v !== undefined && v !== null){
16541             var r = this.findRecord(this.valueField || this.displayField, v);
16542             if(r){
16543                 this.select(this.store.indexOf(r), scrollIntoView);
16544                 return true;
16545             }
16546         }
16547         return false;
16548     },
16549
16550     /**
16551      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16552      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16553      * @param {Number} index The zero-based index of the list item to select
16554      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16555      * selected item if it is not currently in view (defaults to true)
16556      */
16557     select : function(index, scrollIntoView){
16558         this.selectedIndex = index;
16559         this.view.select(index);
16560         if(scrollIntoView !== false){
16561             var el = this.view.getNode(index);
16562             /*
16563              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16564              */
16565             if(el){
16566                 this.list.scrollChildIntoView(el, false);
16567             }
16568         }
16569     },
16570
16571     // private
16572     selectNext : function(){
16573         var ct = this.store.getCount();
16574         if(ct > 0){
16575             if(this.selectedIndex == -1){
16576                 this.select(0);
16577             }else if(this.selectedIndex < ct-1){
16578                 this.select(this.selectedIndex+1);
16579             }
16580         }
16581     },
16582
16583     // private
16584     selectPrev : function(){
16585         var ct = this.store.getCount();
16586         if(ct > 0){
16587             if(this.selectedIndex == -1){
16588                 this.select(0);
16589             }else if(this.selectedIndex != 0){
16590                 this.select(this.selectedIndex-1);
16591             }
16592         }
16593     },
16594
16595     // private
16596     onKeyUp : function(e){
16597         if(this.editable !== false && !e.isSpecialKey()){
16598             this.lastKey = e.getKey();
16599             this.dqTask.delay(this.queryDelay);
16600         }
16601     },
16602
16603     // private
16604     validateBlur : function(){
16605         return !this.list || !this.list.isVisible();   
16606     },
16607
16608     // private
16609     initQuery : function(){
16610         
16611         var v = this.getRawValue();
16612         
16613         if(this.tickable && this.editable){
16614             v = this.tickableInputEl().getValue();
16615         }
16616         
16617         this.doQuery(v);
16618     },
16619
16620     // private
16621     doForce : function(){
16622         if(this.inputEl().dom.value.length > 0){
16623             this.inputEl().dom.value =
16624                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16625              
16626         }
16627     },
16628
16629     /**
16630      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16631      * query allowing the query action to be canceled if needed.
16632      * @param {String} query The SQL query to execute
16633      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16634      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16635      * saved in the current store (defaults to false)
16636      */
16637     doQuery : function(q, forceAll){
16638         
16639         if(q === undefined || q === null){
16640             q = '';
16641         }
16642         var qe = {
16643             query: q,
16644             forceAll: forceAll,
16645             combo: this,
16646             cancel:false
16647         };
16648         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16649             return false;
16650         }
16651         q = qe.query;
16652         
16653         forceAll = qe.forceAll;
16654         if(forceAll === true || (q.length >= this.minChars)){
16655             
16656             this.hasQuery = true;
16657             
16658             if(this.lastQuery != q || this.alwaysQuery){
16659                 this.lastQuery = q;
16660                 if(this.mode == 'local'){
16661                     this.selectedIndex = -1;
16662                     if(forceAll){
16663                         this.store.clearFilter();
16664                     }else{
16665                         
16666                         if(this.specialFilter){
16667                             this.fireEvent('specialfilter', this);
16668                             this.onLoad();
16669                             return;
16670                         }
16671                         
16672                         this.store.filter(this.displayField, q);
16673                     }
16674                     
16675                     this.store.fireEvent("datachanged", this.store);
16676                     
16677                     this.onLoad();
16678                     
16679                     
16680                 }else{
16681                     
16682                     this.store.baseParams[this.queryParam] = q;
16683                     
16684                     var options = {params : this.getParams(q)};
16685                     
16686                     if(this.loadNext){
16687                         options.add = true;
16688                         options.params.start = this.page * this.pageSize;
16689                     }
16690                     
16691                     this.store.load(options);
16692                     
16693                     /*
16694                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16695                      *  we should expand the list on onLoad
16696                      *  so command out it
16697                      */
16698 //                    this.expand();
16699                 }
16700             }else{
16701                 this.selectedIndex = -1;
16702                 this.onLoad();   
16703             }
16704         }
16705         
16706         this.loadNext = false;
16707     },
16708     
16709     // private
16710     getParams : function(q){
16711         var p = {};
16712         //p[this.queryParam] = q;
16713         
16714         if(this.pageSize){
16715             p.start = 0;
16716             p.limit = this.pageSize;
16717         }
16718         return p;
16719     },
16720
16721     /**
16722      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16723      */
16724     collapse : function(){
16725         if(!this.isExpanded()){
16726             return;
16727         }
16728         
16729         this.list.hide();
16730         
16731         this.hasFocus = false;
16732         
16733         if(this.tickable){
16734             this.okBtn.hide();
16735             this.cancelBtn.hide();
16736             this.trigger.show();
16737             
16738             if(this.editable){
16739                 this.tickableInputEl().dom.value = '';
16740                 this.tickableInputEl().blur();
16741             }
16742             
16743         }
16744         
16745         Roo.get(document).un('mousedown', this.collapseIf, this);
16746         Roo.get(document).un('mousewheel', this.collapseIf, this);
16747         if (!this.editable) {
16748             Roo.get(document).un('keydown', this.listKeyPress, this);
16749         }
16750         this.fireEvent('collapse', this);
16751         
16752         this.validate();
16753     },
16754
16755     // private
16756     collapseIf : function(e){
16757         var in_combo  = e.within(this.el);
16758         var in_list =  e.within(this.list);
16759         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16760         
16761         if (in_combo || in_list || is_list) {
16762             //e.stopPropagation();
16763             return;
16764         }
16765         
16766         if(this.tickable){
16767             this.onTickableFooterButtonClick(e, false, false);
16768         }
16769
16770         this.collapse();
16771         
16772     },
16773
16774     /**
16775      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16776      */
16777     expand : function(){
16778        
16779         if(this.isExpanded() || !this.hasFocus){
16780             return;
16781         }
16782         
16783         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16784         this.list.setWidth(lw);
16785         
16786         Roo.log('expand');
16787         
16788         this.list.show();
16789         
16790         this.restrictHeight();
16791         
16792         if(this.tickable){
16793             
16794             this.tickItems = Roo.apply([], this.item);
16795             
16796             this.okBtn.show();
16797             this.cancelBtn.show();
16798             this.trigger.hide();
16799             
16800             if(this.editable){
16801                 this.tickableInputEl().focus();
16802             }
16803             
16804         }
16805         
16806         Roo.get(document).on('mousedown', this.collapseIf, this);
16807         Roo.get(document).on('mousewheel', this.collapseIf, this);
16808         if (!this.editable) {
16809             Roo.get(document).on('keydown', this.listKeyPress, this);
16810         }
16811         
16812         this.fireEvent('expand', this);
16813     },
16814
16815     // private
16816     // Implements the default empty TriggerField.onTriggerClick function
16817     onTriggerClick : function(e)
16818     {
16819         Roo.log('trigger click');
16820         
16821         if(this.disabled || !this.triggerList){
16822             return;
16823         }
16824         
16825         this.page = 0;
16826         this.loadNext = false;
16827         
16828         if(this.isExpanded()){
16829             this.collapse();
16830             if (!this.blockFocus) {
16831                 this.inputEl().focus();
16832             }
16833             
16834         }else {
16835             this.hasFocus = true;
16836             if(this.triggerAction == 'all') {
16837                 this.doQuery(this.allQuery, true);
16838             } else {
16839                 this.doQuery(this.getRawValue());
16840             }
16841             if (!this.blockFocus) {
16842                 this.inputEl().focus();
16843             }
16844         }
16845     },
16846     
16847     onTickableTriggerClick : function(e)
16848     {
16849         if(this.disabled){
16850             return;
16851         }
16852         
16853         this.page = 0;
16854         this.loadNext = false;
16855         this.hasFocus = true;
16856         
16857         if(this.triggerAction == 'all') {
16858             this.doQuery(this.allQuery, true);
16859         } else {
16860             this.doQuery(this.getRawValue());
16861         }
16862     },
16863     
16864     onSearchFieldClick : function(e)
16865     {
16866         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16867             this.onTickableFooterButtonClick(e, false, false);
16868             return;
16869         }
16870         
16871         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16872             return;
16873         }
16874         
16875         this.page = 0;
16876         this.loadNext = false;
16877         this.hasFocus = true;
16878         
16879         if(this.triggerAction == 'all') {
16880             this.doQuery(this.allQuery, true);
16881         } else {
16882             this.doQuery(this.getRawValue());
16883         }
16884     },
16885     
16886     listKeyPress : function(e)
16887     {
16888         //Roo.log('listkeypress');
16889         // scroll to first matching element based on key pres..
16890         if (e.isSpecialKey()) {
16891             return false;
16892         }
16893         var k = String.fromCharCode(e.getKey()).toUpperCase();
16894         //Roo.log(k);
16895         var match  = false;
16896         var csel = this.view.getSelectedNodes();
16897         var cselitem = false;
16898         if (csel.length) {
16899             var ix = this.view.indexOf(csel[0]);
16900             cselitem  = this.store.getAt(ix);
16901             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16902                 cselitem = false;
16903             }
16904             
16905         }
16906         
16907         this.store.each(function(v) { 
16908             if (cselitem) {
16909                 // start at existing selection.
16910                 if (cselitem.id == v.id) {
16911                     cselitem = false;
16912                 }
16913                 return true;
16914             }
16915                 
16916             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16917                 match = this.store.indexOf(v);
16918                 return false;
16919             }
16920             return true;
16921         }, this);
16922         
16923         if (match === false) {
16924             return true; // no more action?
16925         }
16926         // scroll to?
16927         this.view.select(match);
16928         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16929         sn.scrollIntoView(sn.dom.parentNode, false);
16930     },
16931     
16932     onViewScroll : function(e, t){
16933         
16934         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){
16935             return;
16936         }
16937         
16938         this.hasQuery = true;
16939         
16940         this.loading = this.list.select('.loading', true).first();
16941         
16942         if(this.loading === null){
16943             this.list.createChild({
16944                 tag: 'div',
16945                 cls: 'loading roo-select2-more-results roo-select2-active',
16946                 html: 'Loading more results...'
16947             });
16948             
16949             this.loading = this.list.select('.loading', true).first();
16950             
16951             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16952             
16953             this.loading.hide();
16954         }
16955         
16956         this.loading.show();
16957         
16958         var _combo = this;
16959         
16960         this.page++;
16961         this.loadNext = true;
16962         
16963         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16964         
16965         return;
16966     },
16967     
16968     addItem : function(o)
16969     {   
16970         var dv = ''; // display value
16971         
16972         if (this.displayField) {
16973             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16974         } else {
16975             // this is an error condition!!!
16976             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16977         }
16978         
16979         if(!dv.length){
16980             return;
16981         }
16982         
16983         var choice = this.choices.createChild({
16984             tag: 'li',
16985             cls: 'roo-select2-search-choice',
16986             cn: [
16987                 {
16988                     tag: 'div',
16989                     html: dv
16990                 },
16991                 {
16992                     tag: 'a',
16993                     href: '#',
16994                     cls: 'roo-select2-search-choice-close fa fa-times',
16995                     tabindex: '-1'
16996                 }
16997             ]
16998             
16999         }, this.searchField);
17000         
17001         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17002         
17003         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17004         
17005         this.item.push(o);
17006         
17007         this.lastData = o;
17008         
17009         this.syncValue();
17010         
17011         this.inputEl().dom.value = '';
17012         
17013         this.validate();
17014     },
17015     
17016     onRemoveItem : function(e, _self, o)
17017     {
17018         e.preventDefault();
17019         
17020         this.lastItem = Roo.apply([], this.item);
17021         
17022         var index = this.item.indexOf(o.data) * 1;
17023         
17024         if( index < 0){
17025             Roo.log('not this item?!');
17026             return;
17027         }
17028         
17029         this.item.splice(index, 1);
17030         o.item.remove();
17031         
17032         this.syncValue();
17033         
17034         this.fireEvent('remove', this, e);
17035         
17036         this.validate();
17037         
17038     },
17039     
17040     syncValue : function()
17041     {
17042         if(!this.item.length){
17043             this.clearValue();
17044             return;
17045         }
17046             
17047         var value = [];
17048         var _this = this;
17049         Roo.each(this.item, function(i){
17050             if(_this.valueField){
17051                 value.push(i[_this.valueField]);
17052                 return;
17053             }
17054
17055             value.push(i);
17056         });
17057
17058         this.value = value.join(',');
17059
17060         if(this.hiddenField){
17061             this.hiddenField.dom.value = this.value;
17062         }
17063         
17064         this.store.fireEvent("datachanged", this.store);
17065         
17066         this.validate();
17067     },
17068     
17069     clearItem : function()
17070     {
17071         if(!this.multiple){
17072             return;
17073         }
17074         
17075         this.item = [];
17076         
17077         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17078            c.remove();
17079         });
17080         
17081         this.syncValue();
17082         
17083         this.validate();
17084         
17085         if(this.tickable && !Roo.isTouch){
17086             this.view.refresh();
17087         }
17088     },
17089     
17090     inputEl: function ()
17091     {
17092         if(Roo.isIOS && this.useNativeIOS){
17093             return this.el.select('select.roo-ios-select', true).first();
17094         }
17095         
17096         if(Roo.isTouch && this.mobileTouchView){
17097             return this.el.select('input.form-control',true).first();
17098         }
17099         
17100         if(this.tickable){
17101             return this.searchField;
17102         }
17103         
17104         return this.el.select('input.form-control',true).first();
17105     },
17106     
17107     onTickableFooterButtonClick : function(e, btn, el)
17108     {
17109         e.preventDefault();
17110         
17111         this.lastItem = Roo.apply([], this.item);
17112         
17113         if(btn && btn.name == 'cancel'){
17114             this.tickItems = Roo.apply([], this.item);
17115             this.collapse();
17116             return;
17117         }
17118         
17119         this.clearItem();
17120         
17121         var _this = this;
17122         
17123         Roo.each(this.tickItems, function(o){
17124             _this.addItem(o);
17125         });
17126         
17127         this.collapse();
17128         
17129     },
17130     
17131     validate : function()
17132     {
17133         if(this.getVisibilityEl().hasClass('hidden')){
17134             return true;
17135         }
17136         
17137         var v = this.getRawValue();
17138         
17139         if(this.multiple){
17140             v = this.getValue();
17141         }
17142         
17143         if(this.disabled || this.allowBlank || v.length){
17144             this.markValid();
17145             return true;
17146         }
17147         
17148         this.markInvalid();
17149         return false;
17150     },
17151     
17152     tickableInputEl : function()
17153     {
17154         if(!this.tickable || !this.editable){
17155             return this.inputEl();
17156         }
17157         
17158         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17159     },
17160     
17161     
17162     getAutoCreateTouchView : function()
17163     {
17164         var id = Roo.id();
17165         
17166         var cfg = {
17167             cls: 'form-group' //input-group
17168         };
17169         
17170         var input =  {
17171             tag: 'input',
17172             id : id,
17173             type : this.inputType,
17174             cls : 'form-control x-combo-noedit',
17175             autocomplete: 'new-password',
17176             placeholder : this.placeholder || '',
17177             readonly : true
17178         };
17179         
17180         if (this.name) {
17181             input.name = this.name;
17182         }
17183         
17184         if (this.size) {
17185             input.cls += ' input-' + this.size;
17186         }
17187         
17188         if (this.disabled) {
17189             input.disabled = true;
17190         }
17191         
17192         var inputblock = {
17193             cls : 'roo-combobox-wrap',
17194             cn : [
17195                 input
17196             ]
17197         };
17198         
17199         if(this.before){
17200             inputblock.cls += ' input-group';
17201             
17202             inputblock.cn.unshift({
17203                 tag :'span',
17204                 cls : 'input-group-addon input-group-prepend input-group-text',
17205                 html : this.before
17206             });
17207         }
17208         
17209         if(this.removable && !this.multiple){
17210             inputblock.cls += ' roo-removable';
17211             
17212             inputblock.cn.push({
17213                 tag: 'button',
17214                 html : 'x',
17215                 cls : 'roo-combo-removable-btn close'
17216             });
17217         }
17218
17219         if(this.hasFeedback && !this.allowBlank){
17220             
17221             inputblock.cls += ' has-feedback';
17222             
17223             inputblock.cn.push({
17224                 tag: 'span',
17225                 cls: 'glyphicon form-control-feedback'
17226             });
17227             
17228         }
17229         
17230         if (this.after) {
17231             
17232             inputblock.cls += (this.before) ? '' : ' input-group';
17233             
17234             inputblock.cn.push({
17235                 tag :'span',
17236                 cls : 'input-group-addon input-group-append input-group-text',
17237                 html : this.after
17238             });
17239         }
17240
17241         
17242         var ibwrap = inputblock;
17243         
17244         if(this.multiple){
17245             ibwrap = {
17246                 tag: 'ul',
17247                 cls: 'roo-select2-choices',
17248                 cn:[
17249                     {
17250                         tag: 'li',
17251                         cls: 'roo-select2-search-field',
17252                         cn: [
17253
17254                             inputblock
17255                         ]
17256                     }
17257                 ]
17258             };
17259         
17260             
17261         }
17262         
17263         var combobox = {
17264             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17265             cn: [
17266                 {
17267                     tag: 'input',
17268                     type : 'hidden',
17269                     cls: 'form-hidden-field'
17270                 },
17271                 ibwrap
17272             ]
17273         };
17274         
17275         if(!this.multiple && this.showToggleBtn){
17276             
17277             var caret = {
17278                 cls: 'caret'
17279             };
17280             
17281             if (this.caret != false) {
17282                 caret = {
17283                      tag: 'i',
17284                      cls: 'fa fa-' + this.caret
17285                 };
17286                 
17287             }
17288             
17289             combobox.cn.push({
17290                 tag :'span',
17291                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17292                 cn : [
17293                     Roo.bootstrap.version == 3 ? caret : '',
17294                     {
17295                         tag: 'span',
17296                         cls: 'combobox-clear',
17297                         cn  : [
17298                             {
17299                                 tag : 'i',
17300                                 cls: 'icon-remove'
17301                             }
17302                         ]
17303                     }
17304                 ]
17305
17306             })
17307         }
17308         
17309         if(this.multiple){
17310             combobox.cls += ' roo-select2-container-multi';
17311         }
17312         
17313         var align = this.labelAlign || this.parentLabelAlign();
17314         
17315         if (align ==='left' && this.fieldLabel.length) {
17316
17317             cfg.cn = [
17318                 {
17319                    tag : 'i',
17320                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17321                    tooltip : 'This field is required'
17322                 },
17323                 {
17324                     tag: 'label',
17325                     cls : 'control-label col-form-label',
17326                     html : this.fieldLabel
17327
17328                 },
17329                 {
17330                     cls : 'roo-combobox-wrap ', 
17331                     cn: [
17332                         combobox
17333                     ]
17334                 }
17335             ];
17336             
17337             var labelCfg = cfg.cn[1];
17338             var contentCfg = cfg.cn[2];
17339             
17340
17341             if(this.indicatorpos == 'right'){
17342                 cfg.cn = [
17343                     {
17344                         tag: 'label',
17345                         'for' :  id,
17346                         cls : 'control-label col-form-label',
17347                         cn : [
17348                             {
17349                                 tag : 'span',
17350                                 html : this.fieldLabel
17351                             },
17352                             {
17353                                 tag : 'i',
17354                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17355                                 tooltip : 'This field is required'
17356                             }
17357                         ]
17358                     },
17359                     {
17360                         cls : "roo-combobox-wrap ",
17361                         cn: [
17362                             combobox
17363                         ]
17364                     }
17365
17366                 ];
17367                 
17368                 labelCfg = cfg.cn[0];
17369                 contentCfg = cfg.cn[1];
17370             }
17371             
17372            
17373             
17374             if(this.labelWidth > 12){
17375                 labelCfg.style = "width: " + this.labelWidth + 'px';
17376             }
17377            
17378             if(this.labelWidth < 13 && this.labelmd == 0){
17379                 this.labelmd = this.labelWidth;
17380             }
17381             
17382             if(this.labellg > 0){
17383                 labelCfg.cls += ' col-lg-' + this.labellg;
17384                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17385             }
17386             
17387             if(this.labelmd > 0){
17388                 labelCfg.cls += ' col-md-' + this.labelmd;
17389                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17390             }
17391             
17392             if(this.labelsm > 0){
17393                 labelCfg.cls += ' col-sm-' + this.labelsm;
17394                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17395             }
17396             
17397             if(this.labelxs > 0){
17398                 labelCfg.cls += ' col-xs-' + this.labelxs;
17399                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17400             }
17401                 
17402                 
17403         } else if ( this.fieldLabel.length) {
17404             cfg.cn = [
17405                 {
17406                    tag : 'i',
17407                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17408                    tooltip : 'This field is required'
17409                 },
17410                 {
17411                     tag: 'label',
17412                     cls : 'control-label',
17413                     html : this.fieldLabel
17414
17415                 },
17416                 {
17417                     cls : '', 
17418                     cn: [
17419                         combobox
17420                     ]
17421                 }
17422             ];
17423             
17424             if(this.indicatorpos == 'right'){
17425                 cfg.cn = [
17426                     {
17427                         tag: 'label',
17428                         cls : 'control-label',
17429                         html : this.fieldLabel,
17430                         cn : [
17431                             {
17432                                tag : 'i',
17433                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17434                                tooltip : 'This field is required'
17435                             }
17436                         ]
17437                     },
17438                     {
17439                         cls : '', 
17440                         cn: [
17441                             combobox
17442                         ]
17443                     }
17444                 ];
17445             }
17446         } else {
17447             cfg.cn = combobox;    
17448         }
17449         
17450         
17451         var settings = this;
17452         
17453         ['xs','sm','md','lg'].map(function(size){
17454             if (settings[size]) {
17455                 cfg.cls += ' col-' + size + '-' + settings[size];
17456             }
17457         });
17458         
17459         return cfg;
17460     },
17461     
17462     initTouchView : function()
17463     {
17464         this.renderTouchView();
17465         
17466         this.touchViewEl.on('scroll', function(){
17467             this.el.dom.scrollTop = 0;
17468         }, this);
17469         
17470         this.originalValue = this.getValue();
17471         
17472         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17473         
17474         this.inputEl().on("click", this.showTouchView, this);
17475         if (this.triggerEl) {
17476             this.triggerEl.on("click", this.showTouchView, this);
17477         }
17478         
17479         
17480         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17481         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17482         
17483         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17484         
17485         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17486         this.store.on('load', this.onTouchViewLoad, this);
17487         this.store.on('loadexception', this.onTouchViewLoadException, this);
17488         
17489         if(this.hiddenName){
17490             
17491             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17492             
17493             this.hiddenField.dom.value =
17494                 this.hiddenValue !== undefined ? this.hiddenValue :
17495                 this.value !== undefined ? this.value : '';
17496         
17497             this.el.dom.removeAttribute('name');
17498             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17499         }
17500         
17501         if(this.multiple){
17502             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17503             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17504         }
17505         
17506         if(this.removable && !this.multiple){
17507             var close = this.closeTriggerEl();
17508             if(close){
17509                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17510                 close.on('click', this.removeBtnClick, this, close);
17511             }
17512         }
17513         /*
17514          * fix the bug in Safari iOS8
17515          */
17516         this.inputEl().on("focus", function(e){
17517             document.activeElement.blur();
17518         }, this);
17519         
17520         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17521         
17522         return;
17523         
17524         
17525     },
17526     
17527     renderTouchView : function()
17528     {
17529         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17530         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17531         
17532         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17533         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17534         
17535         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17536         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17537         this.touchViewBodyEl.setStyle('overflow', 'auto');
17538         
17539         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17540         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17541         
17542         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17543         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17544         
17545     },
17546     
17547     showTouchView : function()
17548     {
17549         if(this.disabled){
17550             return;
17551         }
17552         
17553         this.touchViewHeaderEl.hide();
17554
17555         if(this.modalTitle.length){
17556             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17557             this.touchViewHeaderEl.show();
17558         }
17559
17560         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17561         this.touchViewEl.show();
17562
17563         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17564         
17565         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17566         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17567
17568         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17569
17570         if(this.modalTitle.length){
17571             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17572         }
17573         
17574         this.touchViewBodyEl.setHeight(bodyHeight);
17575
17576         if(this.animate){
17577             var _this = this;
17578             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17579         }else{
17580             this.touchViewEl.addClass(['in','show']);
17581         }
17582         
17583         if(this._touchViewMask){
17584             Roo.get(document.body).addClass("x-body-masked");
17585             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17586             this._touchViewMask.setStyle('z-index', 10000);
17587             this._touchViewMask.addClass('show');
17588         }
17589         
17590         this.doTouchViewQuery();
17591         
17592     },
17593     
17594     hideTouchView : function()
17595     {
17596         this.touchViewEl.removeClass(['in','show']);
17597
17598         if(this.animate){
17599             var _this = this;
17600             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17601         }else{
17602             this.touchViewEl.setStyle('display', 'none');
17603         }
17604         
17605         if(this._touchViewMask){
17606             this._touchViewMask.removeClass('show');
17607             Roo.get(document.body).removeClass("x-body-masked");
17608         }
17609     },
17610     
17611     setTouchViewValue : function()
17612     {
17613         if(this.multiple){
17614             this.clearItem();
17615         
17616             var _this = this;
17617
17618             Roo.each(this.tickItems, function(o){
17619                 this.addItem(o);
17620             }, this);
17621         }
17622         
17623         this.hideTouchView();
17624     },
17625     
17626     doTouchViewQuery : function()
17627     {
17628         var qe = {
17629             query: '',
17630             forceAll: true,
17631             combo: this,
17632             cancel:false
17633         };
17634         
17635         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17636             return false;
17637         }
17638         
17639         if(!this.alwaysQuery || this.mode == 'local'){
17640             this.onTouchViewLoad();
17641             return;
17642         }
17643         
17644         this.store.load();
17645     },
17646     
17647     onTouchViewBeforeLoad : function(combo,opts)
17648     {
17649         return;
17650     },
17651
17652     // private
17653     onTouchViewLoad : function()
17654     {
17655         if(this.store.getCount() < 1){
17656             this.onTouchViewEmptyResults();
17657             return;
17658         }
17659         
17660         this.clearTouchView();
17661         
17662         var rawValue = this.getRawValue();
17663         
17664         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17665         
17666         this.tickItems = [];
17667         
17668         this.store.data.each(function(d, rowIndex){
17669             var row = this.touchViewListGroup.createChild(template);
17670             
17671             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17672                 row.addClass(d.data.cls);
17673             }
17674             
17675             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17676                 var cfg = {
17677                     data : d.data,
17678                     html : d.data[this.displayField]
17679                 };
17680                 
17681                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17682                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17683                 }
17684             }
17685             row.removeClass('selected');
17686             if(!this.multiple && this.valueField &&
17687                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17688             {
17689                 // radio buttons..
17690                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17691                 row.addClass('selected');
17692             }
17693             
17694             if(this.multiple && this.valueField &&
17695                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17696             {
17697                 
17698                 // checkboxes...
17699                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17700                 this.tickItems.push(d.data);
17701             }
17702             
17703             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17704             
17705         }, this);
17706         
17707         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17708         
17709         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17710
17711         if(this.modalTitle.length){
17712             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17713         }
17714
17715         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17716         
17717         if(this.mobile_restrict_height && listHeight < bodyHeight){
17718             this.touchViewBodyEl.setHeight(listHeight);
17719         }
17720         
17721         var _this = this;
17722         
17723         if(firstChecked && listHeight > bodyHeight){
17724             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17725         }
17726         
17727     },
17728     
17729     onTouchViewLoadException : function()
17730     {
17731         this.hideTouchView();
17732     },
17733     
17734     onTouchViewEmptyResults : function()
17735     {
17736         this.clearTouchView();
17737         
17738         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17739         
17740         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17741         
17742     },
17743     
17744     clearTouchView : function()
17745     {
17746         this.touchViewListGroup.dom.innerHTML = '';
17747     },
17748     
17749     onTouchViewClick : function(e, el, o)
17750     {
17751         e.preventDefault();
17752         
17753         var row = o.row;
17754         var rowIndex = o.rowIndex;
17755         
17756         var r = this.store.getAt(rowIndex);
17757         
17758         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17759             
17760             if(!this.multiple){
17761                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17762                     c.dom.removeAttribute('checked');
17763                 }, this);
17764
17765                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17766
17767                 this.setFromData(r.data);
17768
17769                 var close = this.closeTriggerEl();
17770
17771                 if(close){
17772                     close.show();
17773                 }
17774
17775                 this.hideTouchView();
17776
17777                 this.fireEvent('select', this, r, rowIndex);
17778
17779                 return;
17780             }
17781
17782             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17783                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17784                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17785                 return;
17786             }
17787
17788             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17789             this.addItem(r.data);
17790             this.tickItems.push(r.data);
17791         }
17792     },
17793     
17794     getAutoCreateNativeIOS : function()
17795     {
17796         var cfg = {
17797             cls: 'form-group' //input-group,
17798         };
17799         
17800         var combobox =  {
17801             tag: 'select',
17802             cls : 'roo-ios-select'
17803         };
17804         
17805         if (this.name) {
17806             combobox.name = this.name;
17807         }
17808         
17809         if (this.disabled) {
17810             combobox.disabled = true;
17811         }
17812         
17813         var settings = this;
17814         
17815         ['xs','sm','md','lg'].map(function(size){
17816             if (settings[size]) {
17817                 cfg.cls += ' col-' + size + '-' + settings[size];
17818             }
17819         });
17820         
17821         cfg.cn = combobox;
17822         
17823         return cfg;
17824         
17825     },
17826     
17827     initIOSView : function()
17828     {
17829         this.store.on('load', this.onIOSViewLoad, this);
17830         
17831         return;
17832     },
17833     
17834     onIOSViewLoad : function()
17835     {
17836         if(this.store.getCount() < 1){
17837             return;
17838         }
17839         
17840         this.clearIOSView();
17841         
17842         if(this.allowBlank) {
17843             
17844             var default_text = '-- SELECT --';
17845             
17846             if(this.placeholder.length){
17847                 default_text = this.placeholder;
17848             }
17849             
17850             if(this.emptyTitle.length){
17851                 default_text += ' - ' + this.emptyTitle + ' -';
17852             }
17853             
17854             var opt = this.inputEl().createChild({
17855                 tag: 'option',
17856                 value : 0,
17857                 html : default_text
17858             });
17859             
17860             var o = {};
17861             o[this.valueField] = 0;
17862             o[this.displayField] = default_text;
17863             
17864             this.ios_options.push({
17865                 data : o,
17866                 el : opt
17867             });
17868             
17869         }
17870         
17871         this.store.data.each(function(d, rowIndex){
17872             
17873             var html = '';
17874             
17875             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17876                 html = d.data[this.displayField];
17877             }
17878             
17879             var value = '';
17880             
17881             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17882                 value = d.data[this.valueField];
17883             }
17884             
17885             var option = {
17886                 tag: 'option',
17887                 value : value,
17888                 html : html
17889             };
17890             
17891             if(this.value == d.data[this.valueField]){
17892                 option['selected'] = true;
17893             }
17894             
17895             var opt = this.inputEl().createChild(option);
17896             
17897             this.ios_options.push({
17898                 data : d.data,
17899                 el : opt
17900             });
17901             
17902         }, this);
17903         
17904         this.inputEl().on('change', function(){
17905            this.fireEvent('select', this);
17906         }, this);
17907         
17908     },
17909     
17910     clearIOSView: function()
17911     {
17912         this.inputEl().dom.innerHTML = '';
17913         
17914         this.ios_options = [];
17915     },
17916     
17917     setIOSValue: function(v)
17918     {
17919         this.value = v;
17920         
17921         if(!this.ios_options){
17922             return;
17923         }
17924         
17925         Roo.each(this.ios_options, function(opts){
17926            
17927            opts.el.dom.removeAttribute('selected');
17928            
17929            if(opts.data[this.valueField] != v){
17930                return;
17931            }
17932            
17933            opts.el.dom.setAttribute('selected', true);
17934            
17935         }, this);
17936     }
17937
17938     /** 
17939     * @cfg {Boolean} grow 
17940     * @hide 
17941     */
17942     /** 
17943     * @cfg {Number} growMin 
17944     * @hide 
17945     */
17946     /** 
17947     * @cfg {Number} growMax 
17948     * @hide 
17949     */
17950     /**
17951      * @hide
17952      * @method autoSize
17953      */
17954 });
17955
17956 Roo.apply(Roo.bootstrap.ComboBox,  {
17957     
17958     header : {
17959         tag: 'div',
17960         cls: 'modal-header',
17961         cn: [
17962             {
17963                 tag: 'h4',
17964                 cls: 'modal-title'
17965             }
17966         ]
17967     },
17968     
17969     body : {
17970         tag: 'div',
17971         cls: 'modal-body',
17972         cn: [
17973             {
17974                 tag: 'ul',
17975                 cls: 'list-group'
17976             }
17977         ]
17978     },
17979     
17980     listItemRadio : {
17981         tag: 'li',
17982         cls: 'list-group-item',
17983         cn: [
17984             {
17985                 tag: 'span',
17986                 cls: 'roo-combobox-list-group-item-value'
17987             },
17988             {
17989                 tag: 'div',
17990                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17991                 cn: [
17992                     {
17993                         tag: 'input',
17994                         type: 'radio'
17995                     },
17996                     {
17997                         tag: 'label'
17998                     }
17999                 ]
18000             }
18001         ]
18002     },
18003     
18004     listItemCheckbox : {
18005         tag: 'li',
18006         cls: 'list-group-item',
18007         cn: [
18008             {
18009                 tag: 'span',
18010                 cls: 'roo-combobox-list-group-item-value'
18011             },
18012             {
18013                 tag: 'div',
18014                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18015                 cn: [
18016                     {
18017                         tag: 'input',
18018                         type: 'checkbox'
18019                     },
18020                     {
18021                         tag: 'label'
18022                     }
18023                 ]
18024             }
18025         ]
18026     },
18027     
18028     emptyResult : {
18029         tag: 'div',
18030         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18031     },
18032     
18033     footer : {
18034         tag: 'div',
18035         cls: 'modal-footer',
18036         cn: [
18037             {
18038                 tag: 'div',
18039                 cls: 'row',
18040                 cn: [
18041                     {
18042                         tag: 'div',
18043                         cls: 'col-xs-6 text-left',
18044                         cn: {
18045                             tag: 'button',
18046                             cls: 'btn btn-danger roo-touch-view-cancel',
18047                             html: 'Cancel'
18048                         }
18049                     },
18050                     {
18051                         tag: 'div',
18052                         cls: 'col-xs-6 text-right',
18053                         cn: {
18054                             tag: 'button',
18055                             cls: 'btn btn-success roo-touch-view-ok',
18056                             html: 'OK'
18057                         }
18058                     }
18059                 ]
18060             }
18061         ]
18062         
18063     }
18064 });
18065
18066 Roo.apply(Roo.bootstrap.ComboBox,  {
18067     
18068     touchViewTemplate : {
18069         tag: 'div',
18070         cls: 'modal fade roo-combobox-touch-view',
18071         cn: [
18072             {
18073                 tag: 'div',
18074                 cls: 'modal-dialog',
18075                 style : 'position:fixed', // we have to fix position....
18076                 cn: [
18077                     {
18078                         tag: 'div',
18079                         cls: 'modal-content',
18080                         cn: [
18081                             Roo.bootstrap.ComboBox.header,
18082                             Roo.bootstrap.ComboBox.body,
18083                             Roo.bootstrap.ComboBox.footer
18084                         ]
18085                     }
18086                 ]
18087             }
18088         ]
18089     }
18090 });/*
18091  * Based on:
18092  * Ext JS Library 1.1.1
18093  * Copyright(c) 2006-2007, Ext JS, LLC.
18094  *
18095  * Originally Released Under LGPL - original licence link has changed is not relivant.
18096  *
18097  * Fork - LGPL
18098  * <script type="text/javascript">
18099  */
18100
18101 /**
18102  * @class Roo.View
18103  * @extends Roo.util.Observable
18104  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18105  * This class also supports single and multi selection modes. <br>
18106  * Create a data model bound view:
18107  <pre><code>
18108  var store = new Roo.data.Store(...);
18109
18110  var view = new Roo.View({
18111     el : "my-element",
18112     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18113  
18114     singleSelect: true,
18115     selectedClass: "ydataview-selected",
18116     store: store
18117  });
18118
18119  // listen for node click?
18120  view.on("click", function(vw, index, node, e){
18121  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18122  });
18123
18124  // load XML data
18125  dataModel.load("foobar.xml");
18126  </code></pre>
18127  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18128  * <br><br>
18129  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18130  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18131  * 
18132  * Note: old style constructor is still suported (container, template, config)
18133  * 
18134  * @constructor
18135  * Create a new View
18136  * @param {Object} config The config object
18137  * 
18138  */
18139 Roo.View = function(config, depreciated_tpl, depreciated_config){
18140     
18141     this.parent = false;
18142     
18143     if (typeof(depreciated_tpl) == 'undefined') {
18144         // new way.. - universal constructor.
18145         Roo.apply(this, config);
18146         this.el  = Roo.get(this.el);
18147     } else {
18148         // old format..
18149         this.el  = Roo.get(config);
18150         this.tpl = depreciated_tpl;
18151         Roo.apply(this, depreciated_config);
18152     }
18153     this.wrapEl  = this.el.wrap().wrap();
18154     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18155     
18156     
18157     if(typeof(this.tpl) == "string"){
18158         this.tpl = new Roo.Template(this.tpl);
18159     } else {
18160         // support xtype ctors..
18161         this.tpl = new Roo.factory(this.tpl, Roo);
18162     }
18163     
18164     
18165     this.tpl.compile();
18166     
18167     /** @private */
18168     this.addEvents({
18169         /**
18170          * @event beforeclick
18171          * Fires before a click is processed. Returns false to cancel the default action.
18172          * @param {Roo.View} this
18173          * @param {Number} index The index of the target node
18174          * @param {HTMLElement} node The target node
18175          * @param {Roo.EventObject} e The raw event object
18176          */
18177             "beforeclick" : true,
18178         /**
18179          * @event click
18180          * Fires when a template node is clicked.
18181          * @param {Roo.View} this
18182          * @param {Number} index The index of the target node
18183          * @param {HTMLElement} node The target node
18184          * @param {Roo.EventObject} e The raw event object
18185          */
18186             "click" : true,
18187         /**
18188          * @event dblclick
18189          * Fires when a template node is double clicked.
18190          * @param {Roo.View} this
18191          * @param {Number} index The index of the target node
18192          * @param {HTMLElement} node The target node
18193          * @param {Roo.EventObject} e The raw event object
18194          */
18195             "dblclick" : true,
18196         /**
18197          * @event contextmenu
18198          * Fires when a template node is right clicked.
18199          * @param {Roo.View} this
18200          * @param {Number} index The index of the target node
18201          * @param {HTMLElement} node The target node
18202          * @param {Roo.EventObject} e The raw event object
18203          */
18204             "contextmenu" : true,
18205         /**
18206          * @event selectionchange
18207          * Fires when the selected nodes change.
18208          * @param {Roo.View} this
18209          * @param {Array} selections Array of the selected nodes
18210          */
18211             "selectionchange" : true,
18212     
18213         /**
18214          * @event beforeselect
18215          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18216          * @param {Roo.View} this
18217          * @param {HTMLElement} node The node to be selected
18218          * @param {Array} selections Array of currently selected nodes
18219          */
18220             "beforeselect" : true,
18221         /**
18222          * @event preparedata
18223          * Fires on every row to render, to allow you to change the data.
18224          * @param {Roo.View} this
18225          * @param {Object} data to be rendered (change this)
18226          */
18227           "preparedata" : true
18228           
18229           
18230         });
18231
18232
18233
18234     this.el.on({
18235         "click": this.onClick,
18236         "dblclick": this.onDblClick,
18237         "contextmenu": this.onContextMenu,
18238         scope:this
18239     });
18240
18241     this.selections = [];
18242     this.nodes = [];
18243     this.cmp = new Roo.CompositeElementLite([]);
18244     if(this.store){
18245         this.store = Roo.factory(this.store, Roo.data);
18246         this.setStore(this.store, true);
18247     }
18248     
18249     if ( this.footer && this.footer.xtype) {
18250            
18251          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18252         
18253         this.footer.dataSource = this.store;
18254         this.footer.container = fctr;
18255         this.footer = Roo.factory(this.footer, Roo);
18256         fctr.insertFirst(this.el);
18257         
18258         // this is a bit insane - as the paging toolbar seems to detach the el..
18259 //        dom.parentNode.parentNode.parentNode
18260          // they get detached?
18261     }
18262     
18263     
18264     Roo.View.superclass.constructor.call(this);
18265     
18266     
18267 };
18268
18269 Roo.extend(Roo.View, Roo.util.Observable, {
18270     
18271      /**
18272      * @cfg {Roo.data.Store} store Data store to load data from.
18273      */
18274     store : false,
18275     
18276     /**
18277      * @cfg {String|Roo.Element} el The container element.
18278      */
18279     el : '',
18280     
18281     /**
18282      * @cfg {String|Roo.Template} tpl The template used by this View 
18283      */
18284     tpl : false,
18285     /**
18286      * @cfg {String} dataName the named area of the template to use as the data area
18287      *                          Works with domtemplates roo-name="name"
18288      */
18289     dataName: false,
18290     /**
18291      * @cfg {String} selectedClass The css class to add to selected nodes
18292      */
18293     selectedClass : "x-view-selected",
18294      /**
18295      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18296      */
18297     emptyText : "",
18298     
18299     /**
18300      * @cfg {String} text to display on mask (default Loading)
18301      */
18302     mask : false,
18303     /**
18304      * @cfg {Boolean} multiSelect Allow multiple selection
18305      */
18306     multiSelect : false,
18307     /**
18308      * @cfg {Boolean} singleSelect Allow single selection
18309      */
18310     singleSelect:  false,
18311     
18312     /**
18313      * @cfg {Boolean} toggleSelect - selecting 
18314      */
18315     toggleSelect : false,
18316     
18317     /**
18318      * @cfg {Boolean} tickable - selecting 
18319      */
18320     tickable : false,
18321     
18322     /**
18323      * Returns the element this view is bound to.
18324      * @return {Roo.Element}
18325      */
18326     getEl : function(){
18327         return this.wrapEl;
18328     },
18329     
18330     
18331
18332     /**
18333      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18334      */
18335     refresh : function(){
18336         //Roo.log('refresh');
18337         var t = this.tpl;
18338         
18339         // if we are using something like 'domtemplate', then
18340         // the what gets used is:
18341         // t.applySubtemplate(NAME, data, wrapping data..)
18342         // the outer template then get' applied with
18343         //     the store 'extra data'
18344         // and the body get's added to the
18345         //      roo-name="data" node?
18346         //      <span class='roo-tpl-{name}'></span> ?????
18347         
18348         
18349         
18350         this.clearSelections();
18351         this.el.update("");
18352         var html = [];
18353         var records = this.store.getRange();
18354         if(records.length < 1) {
18355             
18356             // is this valid??  = should it render a template??
18357             
18358             this.el.update(this.emptyText);
18359             return;
18360         }
18361         var el = this.el;
18362         if (this.dataName) {
18363             this.el.update(t.apply(this.store.meta)); //????
18364             el = this.el.child('.roo-tpl-' + this.dataName);
18365         }
18366         
18367         for(var i = 0, len = records.length; i < len; i++){
18368             var data = this.prepareData(records[i].data, i, records[i]);
18369             this.fireEvent("preparedata", this, data, i, records[i]);
18370             
18371             var d = Roo.apply({}, data);
18372             
18373             if(this.tickable){
18374                 Roo.apply(d, {'roo-id' : Roo.id()});
18375                 
18376                 var _this = this;
18377             
18378                 Roo.each(this.parent.item, function(item){
18379                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18380                         return;
18381                     }
18382                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18383                 });
18384             }
18385             
18386             html[html.length] = Roo.util.Format.trim(
18387                 this.dataName ?
18388                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18389                     t.apply(d)
18390             );
18391         }
18392         
18393         
18394         
18395         el.update(html.join(""));
18396         this.nodes = el.dom.childNodes;
18397         this.updateIndexes(0);
18398     },
18399     
18400
18401     /**
18402      * Function to override to reformat the data that is sent to
18403      * the template for each node.
18404      * DEPRICATED - use the preparedata event handler.
18405      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18406      * a JSON object for an UpdateManager bound view).
18407      */
18408     prepareData : function(data, index, record)
18409     {
18410         this.fireEvent("preparedata", this, data, index, record);
18411         return data;
18412     },
18413
18414     onUpdate : function(ds, record){
18415         // Roo.log('on update');   
18416         this.clearSelections();
18417         var index = this.store.indexOf(record);
18418         var n = this.nodes[index];
18419         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18420         n.parentNode.removeChild(n);
18421         this.updateIndexes(index, index);
18422     },
18423
18424     
18425     
18426 // --------- FIXME     
18427     onAdd : function(ds, records, index)
18428     {
18429         //Roo.log(['on Add', ds, records, index] );        
18430         this.clearSelections();
18431         if(this.nodes.length == 0){
18432             this.refresh();
18433             return;
18434         }
18435         var n = this.nodes[index];
18436         for(var i = 0, len = records.length; i < len; i++){
18437             var d = this.prepareData(records[i].data, i, records[i]);
18438             if(n){
18439                 this.tpl.insertBefore(n, d);
18440             }else{
18441                 
18442                 this.tpl.append(this.el, d);
18443             }
18444         }
18445         this.updateIndexes(index);
18446     },
18447
18448     onRemove : function(ds, record, index){
18449        // Roo.log('onRemove');
18450         this.clearSelections();
18451         var el = this.dataName  ?
18452             this.el.child('.roo-tpl-' + this.dataName) :
18453             this.el; 
18454         
18455         el.dom.removeChild(this.nodes[index]);
18456         this.updateIndexes(index);
18457     },
18458
18459     /**
18460      * Refresh an individual node.
18461      * @param {Number} index
18462      */
18463     refreshNode : function(index){
18464         this.onUpdate(this.store, this.store.getAt(index));
18465     },
18466
18467     updateIndexes : function(startIndex, endIndex){
18468         var ns = this.nodes;
18469         startIndex = startIndex || 0;
18470         endIndex = endIndex || ns.length - 1;
18471         for(var i = startIndex; i <= endIndex; i++){
18472             ns[i].nodeIndex = i;
18473         }
18474     },
18475
18476     /**
18477      * Changes the data store this view uses and refresh the view.
18478      * @param {Store} store
18479      */
18480     setStore : function(store, initial){
18481         if(!initial && this.store){
18482             this.store.un("datachanged", this.refresh);
18483             this.store.un("add", this.onAdd);
18484             this.store.un("remove", this.onRemove);
18485             this.store.un("update", this.onUpdate);
18486             this.store.un("clear", this.refresh);
18487             this.store.un("beforeload", this.onBeforeLoad);
18488             this.store.un("load", this.onLoad);
18489             this.store.un("loadexception", this.onLoad);
18490         }
18491         if(store){
18492           
18493             store.on("datachanged", this.refresh, this);
18494             store.on("add", this.onAdd, this);
18495             store.on("remove", this.onRemove, this);
18496             store.on("update", this.onUpdate, this);
18497             store.on("clear", this.refresh, this);
18498             store.on("beforeload", this.onBeforeLoad, this);
18499             store.on("load", this.onLoad, this);
18500             store.on("loadexception", this.onLoad, this);
18501         }
18502         
18503         if(store){
18504             this.refresh();
18505         }
18506     },
18507     /**
18508      * onbeforeLoad - masks the loading area.
18509      *
18510      */
18511     onBeforeLoad : function(store,opts)
18512     {
18513          //Roo.log('onBeforeLoad');   
18514         if (!opts.add) {
18515             this.el.update("");
18516         }
18517         this.el.mask(this.mask ? this.mask : "Loading" ); 
18518     },
18519     onLoad : function ()
18520     {
18521         this.el.unmask();
18522     },
18523     
18524
18525     /**
18526      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18527      * @param {HTMLElement} node
18528      * @return {HTMLElement} The template node
18529      */
18530     findItemFromChild : function(node){
18531         var el = this.dataName  ?
18532             this.el.child('.roo-tpl-' + this.dataName,true) :
18533             this.el.dom; 
18534         
18535         if(!node || node.parentNode == el){
18536                     return node;
18537             }
18538             var p = node.parentNode;
18539             while(p && p != el){
18540             if(p.parentNode == el){
18541                 return p;
18542             }
18543             p = p.parentNode;
18544         }
18545             return null;
18546     },
18547
18548     /** @ignore */
18549     onClick : function(e){
18550         var item = this.findItemFromChild(e.getTarget());
18551         if(item){
18552             var index = this.indexOf(item);
18553             if(this.onItemClick(item, index, e) !== false){
18554                 this.fireEvent("click", this, index, item, e);
18555             }
18556         }else{
18557             this.clearSelections();
18558         }
18559     },
18560
18561     /** @ignore */
18562     onContextMenu : function(e){
18563         var item = this.findItemFromChild(e.getTarget());
18564         if(item){
18565             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18566         }
18567     },
18568
18569     /** @ignore */
18570     onDblClick : function(e){
18571         var item = this.findItemFromChild(e.getTarget());
18572         if(item){
18573             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18574         }
18575     },
18576
18577     onItemClick : function(item, index, e)
18578     {
18579         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18580             return false;
18581         }
18582         if (this.toggleSelect) {
18583             var m = this.isSelected(item) ? 'unselect' : 'select';
18584             //Roo.log(m);
18585             var _t = this;
18586             _t[m](item, true, false);
18587             return true;
18588         }
18589         if(this.multiSelect || this.singleSelect){
18590             if(this.multiSelect && e.shiftKey && this.lastSelection){
18591                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18592             }else{
18593                 this.select(item, this.multiSelect && e.ctrlKey);
18594                 this.lastSelection = item;
18595             }
18596             
18597             if(!this.tickable){
18598                 e.preventDefault();
18599             }
18600             
18601         }
18602         return true;
18603     },
18604
18605     /**
18606      * Get the number of selected nodes.
18607      * @return {Number}
18608      */
18609     getSelectionCount : function(){
18610         return this.selections.length;
18611     },
18612
18613     /**
18614      * Get the currently selected nodes.
18615      * @return {Array} An array of HTMLElements
18616      */
18617     getSelectedNodes : function(){
18618         return this.selections;
18619     },
18620
18621     /**
18622      * Get the indexes of the selected nodes.
18623      * @return {Array}
18624      */
18625     getSelectedIndexes : function(){
18626         var indexes = [], s = this.selections;
18627         for(var i = 0, len = s.length; i < len; i++){
18628             indexes.push(s[i].nodeIndex);
18629         }
18630         return indexes;
18631     },
18632
18633     /**
18634      * Clear all selections
18635      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18636      */
18637     clearSelections : function(suppressEvent){
18638         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18639             this.cmp.elements = this.selections;
18640             this.cmp.removeClass(this.selectedClass);
18641             this.selections = [];
18642             if(!suppressEvent){
18643                 this.fireEvent("selectionchange", this, this.selections);
18644             }
18645         }
18646     },
18647
18648     /**
18649      * Returns true if the passed node is selected
18650      * @param {HTMLElement/Number} node The node or node index
18651      * @return {Boolean}
18652      */
18653     isSelected : function(node){
18654         var s = this.selections;
18655         if(s.length < 1){
18656             return false;
18657         }
18658         node = this.getNode(node);
18659         return s.indexOf(node) !== -1;
18660     },
18661
18662     /**
18663      * Selects nodes.
18664      * @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
18665      * @param {Boolean} keepExisting (optional) true to keep existing selections
18666      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18667      */
18668     select : function(nodeInfo, keepExisting, suppressEvent){
18669         if(nodeInfo instanceof Array){
18670             if(!keepExisting){
18671                 this.clearSelections(true);
18672             }
18673             for(var i = 0, len = nodeInfo.length; i < len; i++){
18674                 this.select(nodeInfo[i], true, true);
18675             }
18676             return;
18677         } 
18678         var node = this.getNode(nodeInfo);
18679         if(!node || this.isSelected(node)){
18680             return; // already selected.
18681         }
18682         if(!keepExisting){
18683             this.clearSelections(true);
18684         }
18685         
18686         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18687             Roo.fly(node).addClass(this.selectedClass);
18688             this.selections.push(node);
18689             if(!suppressEvent){
18690                 this.fireEvent("selectionchange", this, this.selections);
18691             }
18692         }
18693         
18694         
18695     },
18696       /**
18697      * Unselects nodes.
18698      * @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
18699      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18700      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18701      */
18702     unselect : function(nodeInfo, keepExisting, suppressEvent)
18703     {
18704         if(nodeInfo instanceof Array){
18705             Roo.each(this.selections, function(s) {
18706                 this.unselect(s, nodeInfo);
18707             }, this);
18708             return;
18709         }
18710         var node = this.getNode(nodeInfo);
18711         if(!node || !this.isSelected(node)){
18712             //Roo.log("not selected");
18713             return; // not selected.
18714         }
18715         // fireevent???
18716         var ns = [];
18717         Roo.each(this.selections, function(s) {
18718             if (s == node ) {
18719                 Roo.fly(node).removeClass(this.selectedClass);
18720
18721                 return;
18722             }
18723             ns.push(s);
18724         },this);
18725         
18726         this.selections= ns;
18727         this.fireEvent("selectionchange", this, this.selections);
18728     },
18729
18730     /**
18731      * Gets a template node.
18732      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18733      * @return {HTMLElement} The node or null if it wasn't found
18734      */
18735     getNode : function(nodeInfo){
18736         if(typeof nodeInfo == "string"){
18737             return document.getElementById(nodeInfo);
18738         }else if(typeof nodeInfo == "number"){
18739             return this.nodes[nodeInfo];
18740         }
18741         return nodeInfo;
18742     },
18743
18744     /**
18745      * Gets a range template nodes.
18746      * @param {Number} startIndex
18747      * @param {Number} endIndex
18748      * @return {Array} An array of nodes
18749      */
18750     getNodes : function(start, end){
18751         var ns = this.nodes;
18752         start = start || 0;
18753         end = typeof end == "undefined" ? ns.length - 1 : end;
18754         var nodes = [];
18755         if(start <= end){
18756             for(var i = start; i <= end; i++){
18757                 nodes.push(ns[i]);
18758             }
18759         } else{
18760             for(var i = start; i >= end; i--){
18761                 nodes.push(ns[i]);
18762             }
18763         }
18764         return nodes;
18765     },
18766
18767     /**
18768      * Finds the index of the passed node
18769      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18770      * @return {Number} The index of the node or -1
18771      */
18772     indexOf : function(node){
18773         node = this.getNode(node);
18774         if(typeof node.nodeIndex == "number"){
18775             return node.nodeIndex;
18776         }
18777         var ns = this.nodes;
18778         for(var i = 0, len = ns.length; i < len; i++){
18779             if(ns[i] == node){
18780                 return i;
18781             }
18782         }
18783         return -1;
18784     }
18785 });
18786 /*
18787  * - LGPL
18788  *
18789  * based on jquery fullcalendar
18790  * 
18791  */
18792
18793 Roo.bootstrap = Roo.bootstrap || {};
18794 /**
18795  * @class Roo.bootstrap.Calendar
18796  * @extends Roo.bootstrap.Component
18797  * Bootstrap Calendar class
18798  * @cfg {Boolean} loadMask (true|false) default false
18799  * @cfg {Object} header generate the user specific header of the calendar, default false
18800
18801  * @constructor
18802  * Create a new Container
18803  * @param {Object} config The config object
18804  */
18805
18806
18807
18808 Roo.bootstrap.Calendar = function(config){
18809     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18810      this.addEvents({
18811         /**
18812              * @event select
18813              * Fires when a date is selected
18814              * @param {DatePicker} this
18815              * @param {Date} date The selected date
18816              */
18817         'select': true,
18818         /**
18819              * @event monthchange
18820              * Fires when the displayed month changes 
18821              * @param {DatePicker} this
18822              * @param {Date} date The selected month
18823              */
18824         'monthchange': true,
18825         /**
18826              * @event evententer
18827              * Fires when mouse over an event
18828              * @param {Calendar} this
18829              * @param {event} Event
18830              */
18831         'evententer': true,
18832         /**
18833              * @event eventleave
18834              * Fires when the mouse leaves an
18835              * @param {Calendar} this
18836              * @param {event}
18837              */
18838         'eventleave': true,
18839         /**
18840              * @event eventclick
18841              * Fires when the mouse click an
18842              * @param {Calendar} this
18843              * @param {event}
18844              */
18845         'eventclick': true
18846         
18847     });
18848
18849 };
18850
18851 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18852     
18853      /**
18854      * @cfg {Number} startDay
18855      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18856      */
18857     startDay : 0,
18858     
18859     loadMask : false,
18860     
18861     header : false,
18862       
18863     getAutoCreate : function(){
18864         
18865         
18866         var fc_button = function(name, corner, style, content ) {
18867             return Roo.apply({},{
18868                 tag : 'span',
18869                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18870                          (corner.length ?
18871                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18872                             ''
18873                         ),
18874                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18875                 unselectable: 'on'
18876             });
18877         };
18878         
18879         var header = {};
18880         
18881         if(!this.header){
18882             header = {
18883                 tag : 'table',
18884                 cls : 'fc-header',
18885                 style : 'width:100%',
18886                 cn : [
18887                     {
18888                         tag: 'tr',
18889                         cn : [
18890                             {
18891                                 tag : 'td',
18892                                 cls : 'fc-header-left',
18893                                 cn : [
18894                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18895                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18896                                     { tag: 'span', cls: 'fc-header-space' },
18897                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18898
18899
18900                                 ]
18901                             },
18902
18903                             {
18904                                 tag : 'td',
18905                                 cls : 'fc-header-center',
18906                                 cn : [
18907                                     {
18908                                         tag: 'span',
18909                                         cls: 'fc-header-title',
18910                                         cn : {
18911                                             tag: 'H2',
18912                                             html : 'month / year'
18913                                         }
18914                                     }
18915
18916                                 ]
18917                             },
18918                             {
18919                                 tag : 'td',
18920                                 cls : 'fc-header-right',
18921                                 cn : [
18922                               /*      fc_button('month', 'left', '', 'month' ),
18923                                     fc_button('week', '', '', 'week' ),
18924                                     fc_button('day', 'right', '', 'day' )
18925                                 */    
18926
18927                                 ]
18928                             }
18929
18930                         ]
18931                     }
18932                 ]
18933             };
18934         }
18935         
18936         header = this.header;
18937         
18938        
18939         var cal_heads = function() {
18940             var ret = [];
18941             // fixme - handle this.
18942             
18943             for (var i =0; i < Date.dayNames.length; i++) {
18944                 var d = Date.dayNames[i];
18945                 ret.push({
18946                     tag: 'th',
18947                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18948                     html : d.substring(0,3)
18949                 });
18950                 
18951             }
18952             ret[0].cls += ' fc-first';
18953             ret[6].cls += ' fc-last';
18954             return ret;
18955         };
18956         var cal_cell = function(n) {
18957             return  {
18958                 tag: 'td',
18959                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18960                 cn : [
18961                     {
18962                         cn : [
18963                             {
18964                                 cls: 'fc-day-number',
18965                                 html: 'D'
18966                             },
18967                             {
18968                                 cls: 'fc-day-content',
18969                              
18970                                 cn : [
18971                                      {
18972                                         style: 'position: relative;' // height: 17px;
18973                                     }
18974                                 ]
18975                             }
18976                             
18977                             
18978                         ]
18979                     }
18980                 ]
18981                 
18982             }
18983         };
18984         var cal_rows = function() {
18985             
18986             var ret = [];
18987             for (var r = 0; r < 6; r++) {
18988                 var row= {
18989                     tag : 'tr',
18990                     cls : 'fc-week',
18991                     cn : []
18992                 };
18993                 
18994                 for (var i =0; i < Date.dayNames.length; i++) {
18995                     var d = Date.dayNames[i];
18996                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18997
18998                 }
18999                 row.cn[0].cls+=' fc-first';
19000                 row.cn[0].cn[0].style = 'min-height:90px';
19001                 row.cn[6].cls+=' fc-last';
19002                 ret.push(row);
19003                 
19004             }
19005             ret[0].cls += ' fc-first';
19006             ret[4].cls += ' fc-prev-last';
19007             ret[5].cls += ' fc-last';
19008             return ret;
19009             
19010         };
19011         
19012         var cal_table = {
19013             tag: 'table',
19014             cls: 'fc-border-separate',
19015             style : 'width:100%',
19016             cellspacing  : 0,
19017             cn : [
19018                 { 
19019                     tag: 'thead',
19020                     cn : [
19021                         { 
19022                             tag: 'tr',
19023                             cls : 'fc-first fc-last',
19024                             cn : cal_heads()
19025                         }
19026                     ]
19027                 },
19028                 { 
19029                     tag: 'tbody',
19030                     cn : cal_rows()
19031                 }
19032                   
19033             ]
19034         };
19035          
19036          var cfg = {
19037             cls : 'fc fc-ltr',
19038             cn : [
19039                 header,
19040                 {
19041                     cls : 'fc-content',
19042                     style : "position: relative;",
19043                     cn : [
19044                         {
19045                             cls : 'fc-view fc-view-month fc-grid',
19046                             style : 'position: relative',
19047                             unselectable : 'on',
19048                             cn : [
19049                                 {
19050                                     cls : 'fc-event-container',
19051                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19052                                 },
19053                                 cal_table
19054                             ]
19055                         }
19056                     ]
19057     
19058                 }
19059            ] 
19060             
19061         };
19062         
19063          
19064         
19065         return cfg;
19066     },
19067     
19068     
19069     initEvents : function()
19070     {
19071         if(!this.store){
19072             throw "can not find store for calendar";
19073         }
19074         
19075         var mark = {
19076             tag: "div",
19077             cls:"x-dlg-mask",
19078             style: "text-align:center",
19079             cn: [
19080                 {
19081                     tag: "div",
19082                     style: "background-color:white;width:50%;margin:250 auto",
19083                     cn: [
19084                         {
19085                             tag: "img",
19086                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19087                         },
19088                         {
19089                             tag: "span",
19090                             html: "Loading"
19091                         }
19092                         
19093                     ]
19094                 }
19095             ]
19096         };
19097         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19098         
19099         var size = this.el.select('.fc-content', true).first().getSize();
19100         this.maskEl.setSize(size.width, size.height);
19101         this.maskEl.enableDisplayMode("block");
19102         if(!this.loadMask){
19103             this.maskEl.hide();
19104         }
19105         
19106         this.store = Roo.factory(this.store, Roo.data);
19107         this.store.on('load', this.onLoad, this);
19108         this.store.on('beforeload', this.onBeforeLoad, this);
19109         
19110         this.resize();
19111         
19112         this.cells = this.el.select('.fc-day',true);
19113         //Roo.log(this.cells);
19114         this.textNodes = this.el.query('.fc-day-number');
19115         this.cells.addClassOnOver('fc-state-hover');
19116         
19117         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19118         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19119         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19120         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19121         
19122         this.on('monthchange', this.onMonthChange, this);
19123         
19124         this.update(new Date().clearTime());
19125     },
19126     
19127     resize : function() {
19128         var sz  = this.el.getSize();
19129         
19130         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19131         this.el.select('.fc-day-content div',true).setHeight(34);
19132     },
19133     
19134     
19135     // private
19136     showPrevMonth : function(e){
19137         this.update(this.activeDate.add("mo", -1));
19138     },
19139     showToday : function(e){
19140         this.update(new Date().clearTime());
19141     },
19142     // private
19143     showNextMonth : function(e){
19144         this.update(this.activeDate.add("mo", 1));
19145     },
19146
19147     // private
19148     showPrevYear : function(){
19149         this.update(this.activeDate.add("y", -1));
19150     },
19151
19152     // private
19153     showNextYear : function(){
19154         this.update(this.activeDate.add("y", 1));
19155     },
19156
19157     
19158    // private
19159     update : function(date)
19160     {
19161         var vd = this.activeDate;
19162         this.activeDate = date;
19163 //        if(vd && this.el){
19164 //            var t = date.getTime();
19165 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19166 //                Roo.log('using add remove');
19167 //                
19168 //                this.fireEvent('monthchange', this, date);
19169 //                
19170 //                this.cells.removeClass("fc-state-highlight");
19171 //                this.cells.each(function(c){
19172 //                   if(c.dateValue == t){
19173 //                       c.addClass("fc-state-highlight");
19174 //                       setTimeout(function(){
19175 //                            try{c.dom.firstChild.focus();}catch(e){}
19176 //                       }, 50);
19177 //                       return false;
19178 //                   }
19179 //                   return true;
19180 //                });
19181 //                return;
19182 //            }
19183 //        }
19184         
19185         var days = date.getDaysInMonth();
19186         
19187         var firstOfMonth = date.getFirstDateOfMonth();
19188         var startingPos = firstOfMonth.getDay()-this.startDay;
19189         
19190         if(startingPos < this.startDay){
19191             startingPos += 7;
19192         }
19193         
19194         var pm = date.add(Date.MONTH, -1);
19195         var prevStart = pm.getDaysInMonth()-startingPos;
19196 //        
19197         this.cells = this.el.select('.fc-day',true);
19198         this.textNodes = this.el.query('.fc-day-number');
19199         this.cells.addClassOnOver('fc-state-hover');
19200         
19201         var cells = this.cells.elements;
19202         var textEls = this.textNodes;
19203         
19204         Roo.each(cells, function(cell){
19205             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19206         });
19207         
19208         days += startingPos;
19209
19210         // convert everything to numbers so it's fast
19211         var day = 86400000;
19212         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19213         //Roo.log(d);
19214         //Roo.log(pm);
19215         //Roo.log(prevStart);
19216         
19217         var today = new Date().clearTime().getTime();
19218         var sel = date.clearTime().getTime();
19219         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19220         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19221         var ddMatch = this.disabledDatesRE;
19222         var ddText = this.disabledDatesText;
19223         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19224         var ddaysText = this.disabledDaysText;
19225         var format = this.format;
19226         
19227         var setCellClass = function(cal, cell){
19228             cell.row = 0;
19229             cell.events = [];
19230             cell.more = [];
19231             //Roo.log('set Cell Class');
19232             cell.title = "";
19233             var t = d.getTime();
19234             
19235             //Roo.log(d);
19236             
19237             cell.dateValue = t;
19238             if(t == today){
19239                 cell.className += " fc-today";
19240                 cell.className += " fc-state-highlight";
19241                 cell.title = cal.todayText;
19242             }
19243             if(t == sel){
19244                 // disable highlight in other month..
19245                 //cell.className += " fc-state-highlight";
19246                 
19247             }
19248             // disabling
19249             if(t < min) {
19250                 cell.className = " fc-state-disabled";
19251                 cell.title = cal.minText;
19252                 return;
19253             }
19254             if(t > max) {
19255                 cell.className = " fc-state-disabled";
19256                 cell.title = cal.maxText;
19257                 return;
19258             }
19259             if(ddays){
19260                 if(ddays.indexOf(d.getDay()) != -1){
19261                     cell.title = ddaysText;
19262                     cell.className = " fc-state-disabled";
19263                 }
19264             }
19265             if(ddMatch && format){
19266                 var fvalue = d.dateFormat(format);
19267                 if(ddMatch.test(fvalue)){
19268                     cell.title = ddText.replace("%0", fvalue);
19269                     cell.className = " fc-state-disabled";
19270                 }
19271             }
19272             
19273             if (!cell.initialClassName) {
19274                 cell.initialClassName = cell.dom.className;
19275             }
19276             
19277             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19278         };
19279
19280         var i = 0;
19281         
19282         for(; i < startingPos; i++) {
19283             textEls[i].innerHTML = (++prevStart);
19284             d.setDate(d.getDate()+1);
19285             
19286             cells[i].className = "fc-past fc-other-month";
19287             setCellClass(this, cells[i]);
19288         }
19289         
19290         var intDay = 0;
19291         
19292         for(; i < days; i++){
19293             intDay = i - startingPos + 1;
19294             textEls[i].innerHTML = (intDay);
19295             d.setDate(d.getDate()+1);
19296             
19297             cells[i].className = ''; // "x-date-active";
19298             setCellClass(this, cells[i]);
19299         }
19300         var extraDays = 0;
19301         
19302         for(; i < 42; i++) {
19303             textEls[i].innerHTML = (++extraDays);
19304             d.setDate(d.getDate()+1);
19305             
19306             cells[i].className = "fc-future fc-other-month";
19307             setCellClass(this, cells[i]);
19308         }
19309         
19310         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19311         
19312         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19313         
19314         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19315         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19316         
19317         if(totalRows != 6){
19318             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19319             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19320         }
19321         
19322         this.fireEvent('monthchange', this, date);
19323         
19324         
19325         /*
19326         if(!this.internalRender){
19327             var main = this.el.dom.firstChild;
19328             var w = main.offsetWidth;
19329             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19330             Roo.fly(main).setWidth(w);
19331             this.internalRender = true;
19332             // opera does not respect the auto grow header center column
19333             // then, after it gets a width opera refuses to recalculate
19334             // without a second pass
19335             if(Roo.isOpera && !this.secondPass){
19336                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19337                 this.secondPass = true;
19338                 this.update.defer(10, this, [date]);
19339             }
19340         }
19341         */
19342         
19343     },
19344     
19345     findCell : function(dt) {
19346         dt = dt.clearTime().getTime();
19347         var ret = false;
19348         this.cells.each(function(c){
19349             //Roo.log("check " +c.dateValue + '?=' + dt);
19350             if(c.dateValue == dt){
19351                 ret = c;
19352                 return false;
19353             }
19354             return true;
19355         });
19356         
19357         return ret;
19358     },
19359     
19360     findCells : function(ev) {
19361         var s = ev.start.clone().clearTime().getTime();
19362        // Roo.log(s);
19363         var e= ev.end.clone().clearTime().getTime();
19364        // Roo.log(e);
19365         var ret = [];
19366         this.cells.each(function(c){
19367              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19368             
19369             if(c.dateValue > e){
19370                 return ;
19371             }
19372             if(c.dateValue < s){
19373                 return ;
19374             }
19375             ret.push(c);
19376         });
19377         
19378         return ret;    
19379     },
19380     
19381 //    findBestRow: function(cells)
19382 //    {
19383 //        var ret = 0;
19384 //        
19385 //        for (var i =0 ; i < cells.length;i++) {
19386 //            ret  = Math.max(cells[i].rows || 0,ret);
19387 //        }
19388 //        return ret;
19389 //        
19390 //    },
19391     
19392     
19393     addItem : function(ev)
19394     {
19395         // look for vertical location slot in
19396         var cells = this.findCells(ev);
19397         
19398 //        ev.row = this.findBestRow(cells);
19399         
19400         // work out the location.
19401         
19402         var crow = false;
19403         var rows = [];
19404         for(var i =0; i < cells.length; i++) {
19405             
19406             cells[i].row = cells[0].row;
19407             
19408             if(i == 0){
19409                 cells[i].row = cells[i].row + 1;
19410             }
19411             
19412             if (!crow) {
19413                 crow = {
19414                     start : cells[i],
19415                     end :  cells[i]
19416                 };
19417                 continue;
19418             }
19419             if (crow.start.getY() == cells[i].getY()) {
19420                 // on same row.
19421                 crow.end = cells[i];
19422                 continue;
19423             }
19424             // different row.
19425             rows.push(crow);
19426             crow = {
19427                 start: cells[i],
19428                 end : cells[i]
19429             };
19430             
19431         }
19432         
19433         rows.push(crow);
19434         ev.els = [];
19435         ev.rows = rows;
19436         ev.cells = cells;
19437         
19438         cells[0].events.push(ev);
19439         
19440         this.calevents.push(ev);
19441     },
19442     
19443     clearEvents: function() {
19444         
19445         if(!this.calevents){
19446             return;
19447         }
19448         
19449         Roo.each(this.cells.elements, function(c){
19450             c.row = 0;
19451             c.events = [];
19452             c.more = [];
19453         });
19454         
19455         Roo.each(this.calevents, function(e) {
19456             Roo.each(e.els, function(el) {
19457                 el.un('mouseenter' ,this.onEventEnter, this);
19458                 el.un('mouseleave' ,this.onEventLeave, this);
19459                 el.remove();
19460             },this);
19461         },this);
19462         
19463         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19464             e.remove();
19465         });
19466         
19467     },
19468     
19469     renderEvents: function()
19470     {   
19471         var _this = this;
19472         
19473         this.cells.each(function(c) {
19474             
19475             if(c.row < 5){
19476                 return;
19477             }
19478             
19479             var ev = c.events;
19480             
19481             var r = 4;
19482             if(c.row != c.events.length){
19483                 r = 4 - (4 - (c.row - c.events.length));
19484             }
19485             
19486             c.events = ev.slice(0, r);
19487             c.more = ev.slice(r);
19488             
19489             if(c.more.length && c.more.length == 1){
19490                 c.events.push(c.more.pop());
19491             }
19492             
19493             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19494             
19495         });
19496             
19497         this.cells.each(function(c) {
19498             
19499             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19500             
19501             
19502             for (var e = 0; e < c.events.length; e++){
19503                 var ev = c.events[e];
19504                 var rows = ev.rows;
19505                 
19506                 for(var i = 0; i < rows.length; i++) {
19507                 
19508                     // how many rows should it span..
19509
19510                     var  cfg = {
19511                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19512                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19513
19514                         unselectable : "on",
19515                         cn : [
19516                             {
19517                                 cls: 'fc-event-inner',
19518                                 cn : [
19519     //                                {
19520     //                                  tag:'span',
19521     //                                  cls: 'fc-event-time',
19522     //                                  html : cells.length > 1 ? '' : ev.time
19523     //                                },
19524                                     {
19525                                       tag:'span',
19526                                       cls: 'fc-event-title',
19527                                       html : String.format('{0}', ev.title)
19528                                     }
19529
19530
19531                                 ]
19532                             },
19533                             {
19534                                 cls: 'ui-resizable-handle ui-resizable-e',
19535                                 html : '&nbsp;&nbsp;&nbsp'
19536                             }
19537
19538                         ]
19539                     };
19540
19541                     if (i == 0) {
19542                         cfg.cls += ' fc-event-start';
19543                     }
19544                     if ((i+1) == rows.length) {
19545                         cfg.cls += ' fc-event-end';
19546                     }
19547
19548                     var ctr = _this.el.select('.fc-event-container',true).first();
19549                     var cg = ctr.createChild(cfg);
19550
19551                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19552                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19553
19554                     var r = (c.more.length) ? 1 : 0;
19555                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19556                     cg.setWidth(ebox.right - sbox.x -2);
19557
19558                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19559                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19560                     cg.on('click', _this.onEventClick, _this, ev);
19561
19562                     ev.els.push(cg);
19563                     
19564                 }
19565                 
19566             }
19567             
19568             
19569             if(c.more.length){
19570                 var  cfg = {
19571                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19572                     style : 'position: absolute',
19573                     unselectable : "on",
19574                     cn : [
19575                         {
19576                             cls: 'fc-event-inner',
19577                             cn : [
19578                                 {
19579                                   tag:'span',
19580                                   cls: 'fc-event-title',
19581                                   html : 'More'
19582                                 }
19583
19584
19585                             ]
19586                         },
19587                         {
19588                             cls: 'ui-resizable-handle ui-resizable-e',
19589                             html : '&nbsp;&nbsp;&nbsp'
19590                         }
19591
19592                     ]
19593                 };
19594
19595                 var ctr = _this.el.select('.fc-event-container',true).first();
19596                 var cg = ctr.createChild(cfg);
19597
19598                 var sbox = c.select('.fc-day-content',true).first().getBox();
19599                 var ebox = c.select('.fc-day-content',true).first().getBox();
19600                 //Roo.log(cg);
19601                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19602                 cg.setWidth(ebox.right - sbox.x -2);
19603
19604                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19605                 
19606             }
19607             
19608         });
19609         
19610         
19611         
19612     },
19613     
19614     onEventEnter: function (e, el,event,d) {
19615         this.fireEvent('evententer', this, el, event);
19616     },
19617     
19618     onEventLeave: function (e, el,event,d) {
19619         this.fireEvent('eventleave', this, el, event);
19620     },
19621     
19622     onEventClick: function (e, el,event,d) {
19623         this.fireEvent('eventclick', this, el, event);
19624     },
19625     
19626     onMonthChange: function () {
19627         this.store.load();
19628     },
19629     
19630     onMoreEventClick: function(e, el, more)
19631     {
19632         var _this = this;
19633         
19634         this.calpopover.placement = 'right';
19635         this.calpopover.setTitle('More');
19636         
19637         this.calpopover.setContent('');
19638         
19639         var ctr = this.calpopover.el.select('.popover-content', true).first();
19640         
19641         Roo.each(more, function(m){
19642             var cfg = {
19643                 cls : 'fc-event-hori fc-event-draggable',
19644                 html : m.title
19645             };
19646             var cg = ctr.createChild(cfg);
19647             
19648             cg.on('click', _this.onEventClick, _this, m);
19649         });
19650         
19651         this.calpopover.show(el);
19652         
19653         
19654     },
19655     
19656     onLoad: function () 
19657     {   
19658         this.calevents = [];
19659         var cal = this;
19660         
19661         if(this.store.getCount() > 0){
19662             this.store.data.each(function(d){
19663                cal.addItem({
19664                     id : d.data.id,
19665                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19666                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19667                     time : d.data.start_time,
19668                     title : d.data.title,
19669                     description : d.data.description,
19670                     venue : d.data.venue
19671                 });
19672             });
19673         }
19674         
19675         this.renderEvents();
19676         
19677         if(this.calevents.length && this.loadMask){
19678             this.maskEl.hide();
19679         }
19680     },
19681     
19682     onBeforeLoad: function()
19683     {
19684         this.clearEvents();
19685         if(this.loadMask){
19686             this.maskEl.show();
19687         }
19688     }
19689 });
19690
19691  
19692  /*
19693  * - LGPL
19694  *
19695  * element
19696  * 
19697  */
19698
19699 /**
19700  * @class Roo.bootstrap.Popover
19701  * @extends Roo.bootstrap.Component
19702  * Bootstrap Popover class
19703  * @cfg {String} html contents of the popover   (or false to use children..)
19704  * @cfg {String} title of popover (or false to hide)
19705  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19706  * @cfg {String} trigger click || hover (or false to trigger manually)
19707  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19708  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19709  *      - if false and it has a 'parent' then it will be automatically added to that element
19710  *      - if string - Roo.get  will be called 
19711  * @cfg {Number} delay - delay before showing
19712  
19713  * @constructor
19714  * Create a new Popover
19715  * @param {Object} config The config object
19716  */
19717
19718 Roo.bootstrap.Popover = function(config){
19719     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19720     
19721     this.addEvents({
19722         // raw events
19723          /**
19724          * @event show
19725          * After the popover show
19726          * 
19727          * @param {Roo.bootstrap.Popover} this
19728          */
19729         "show" : true,
19730         /**
19731          * @event hide
19732          * After the popover hide
19733          * 
19734          * @param {Roo.bootstrap.Popover} this
19735          */
19736         "hide" : true
19737     });
19738 };
19739
19740 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19741     
19742     title: false,
19743     html: false,
19744     
19745     placement : 'right',
19746     trigger : 'hover', // hover
19747     modal : false,
19748     delay : 0,
19749     
19750     over: false,
19751     
19752     can_build_overlaid : false,
19753     
19754     maskEl : false, // the mask element
19755     headerEl : false,
19756     contentEl : false,
19757     alignEl : false, // when show is called with an element - this get's stored.
19758     
19759     getChildContainer : function()
19760     {
19761         return this.contentEl;
19762         
19763     },
19764     getPopoverHeader : function()
19765     {
19766         this.title = true; // flag not to hide it..
19767         this.headerEl.addClass('p-0');
19768         return this.headerEl
19769     },
19770     
19771     
19772     getAutoCreate : function(){
19773          
19774         var cfg = {
19775            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19776            style: 'display:block',
19777            cn : [
19778                 {
19779                     cls : 'arrow'
19780                 },
19781                 {
19782                     cls : 'popover-inner ',
19783                     cn : [
19784                         {
19785                             tag: 'h3',
19786                             cls: 'popover-title popover-header',
19787                             html : this.title === false ? '' : this.title
19788                         },
19789                         {
19790                             cls : 'popover-content popover-body '  + (this.cls || ''),
19791                             html : this.html || ''
19792                         }
19793                     ]
19794                     
19795                 }
19796            ]
19797         };
19798         
19799         return cfg;
19800     },
19801     /**
19802      * @param {string} the title
19803      */
19804     setTitle: function(str)
19805     {
19806         this.title = str;
19807         if (this.el) {
19808             this.headerEl.dom.innerHTML = str;
19809         }
19810         
19811     },
19812     /**
19813      * @param {string} the body content
19814      */
19815     setContent: function(str)
19816     {
19817         this.html = str;
19818         if (this.contentEl) {
19819             this.contentEl.dom.innerHTML = str;
19820         }
19821         
19822     },
19823     // as it get's added to the bottom of the page.
19824     onRender : function(ct, position)
19825     {
19826         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19827         
19828         
19829         
19830         if(!this.el){
19831             var cfg = Roo.apply({},  this.getAutoCreate());
19832             cfg.id = Roo.id();
19833             
19834             if (this.cls) {
19835                 cfg.cls += ' ' + this.cls;
19836             }
19837             if (this.style) {
19838                 cfg.style = this.style;
19839             }
19840             //Roo.log("adding to ");
19841             this.el = Roo.get(document.body).createChild(cfg, position);
19842 //            Roo.log(this.el);
19843         }
19844         
19845         this.contentEl = this.el.select('.popover-content',true).first();
19846         this.headerEl =  this.el.select('.popover-title',true).first();
19847         
19848         var nitems = [];
19849         if(typeof(this.items) != 'undefined'){
19850             var items = this.items;
19851             delete this.items;
19852
19853             for(var i =0;i < items.length;i++) {
19854                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19855             }
19856         }
19857
19858         this.items = nitems;
19859         
19860         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19861         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19862         
19863         
19864         
19865         this.initEvents();
19866     },
19867     
19868     resizeMask : function()
19869     {
19870         this.maskEl.setSize(
19871             Roo.lib.Dom.getViewWidth(true),
19872             Roo.lib.Dom.getViewHeight(true)
19873         );
19874     },
19875     
19876     initEvents : function()
19877     {
19878         
19879         if (!this.modal) { 
19880             Roo.bootstrap.Popover.register(this);
19881         }
19882          
19883         this.arrowEl = this.el.select('.arrow',true).first();
19884         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19885         this.el.enableDisplayMode('block');
19886         this.el.hide();
19887  
19888         
19889         if (this.over === false && !this.parent()) {
19890             return; 
19891         }
19892         if (this.triggers === false) {
19893             return;
19894         }
19895          
19896         // support parent
19897         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19898         var triggers = this.trigger ? this.trigger.split(' ') : [];
19899         Roo.each(triggers, function(trigger) {
19900         
19901             if (trigger == 'click') {
19902                 on_el.on('click', this.toggle, this);
19903             } else if (trigger != 'manual') {
19904                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19905                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19906       
19907                 on_el.on(eventIn  ,this.enter, this);
19908                 on_el.on(eventOut, this.leave, this);
19909             }
19910         }, this);
19911     },
19912     
19913     
19914     // private
19915     timeout : null,
19916     hoverState : null,
19917     
19918     toggle : function () {
19919         this.hoverState == 'in' ? this.leave() : this.enter();
19920     },
19921     
19922     enter : function () {
19923         
19924         clearTimeout(this.timeout);
19925     
19926         this.hoverState = 'in';
19927     
19928         if (!this.delay || !this.delay.show) {
19929             this.show();
19930             return;
19931         }
19932         var _t = this;
19933         this.timeout = setTimeout(function () {
19934             if (_t.hoverState == 'in') {
19935                 _t.show();
19936             }
19937         }, this.delay.show)
19938     },
19939     
19940     leave : function() {
19941         clearTimeout(this.timeout);
19942     
19943         this.hoverState = 'out';
19944     
19945         if (!this.delay || !this.delay.hide) {
19946             this.hide();
19947             return;
19948         }
19949         var _t = this;
19950         this.timeout = setTimeout(function () {
19951             if (_t.hoverState == 'out') {
19952                 _t.hide();
19953             }
19954         }, this.delay.hide)
19955     },
19956     /**
19957      * Show the popover
19958      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19959      * @param {string} (left|right|top|bottom) position
19960      */
19961     show : function (on_el, placement)
19962     {
19963         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19964         on_el = on_el || false; // default to false
19965          
19966         if (!on_el) {
19967             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19968                 on_el = this.parent().el;
19969             } else if (this.over) {
19970                 Roo.get(this.over);
19971             }
19972             
19973         }
19974         
19975         this.alignEl = Roo.get( on_el );
19976
19977         if (!this.el) {
19978             this.render(document.body);
19979         }
19980         
19981         
19982          
19983         
19984         if (this.title === false) {
19985             this.headerEl.hide();
19986         }
19987         
19988        
19989         this.el.show();
19990         this.el.dom.style.display = 'block';
19991          
19992  
19993         if (this.alignEl) {
19994             this.updatePosition(this.placement, true);
19995              
19996         } else {
19997             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19998             var es = this.el.getSize();
19999             var x = Roo.lib.Dom.getViewWidth()/2;
20000             var y = Roo.lib.Dom.getViewHeight()/2;
20001             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20002             
20003         }
20004
20005         
20006         //var arrow = this.el.select('.arrow',true).first();
20007         //arrow.set(align[2], 
20008         
20009         this.el.addClass('in');
20010         
20011          
20012         
20013         this.hoverState = 'in';
20014         
20015         if (this.modal) {
20016             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20017             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20018             this.maskEl.dom.style.display = 'block';
20019             this.maskEl.addClass('show');
20020         }
20021         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20022  
20023         this.fireEvent('show', this);
20024         
20025     },
20026     /**
20027      * fire this manually after loading a grid in the table for example
20028      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20029      * @param {Boolean} try and move it if we cant get right position.
20030      */
20031     updatePosition : function(placement, try_move)
20032     {
20033         // allow for calling with no parameters
20034         placement = placement   ? placement :  this.placement;
20035         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20036         
20037         this.el.removeClass([
20038             'fade','top','bottom', 'left', 'right','in',
20039             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20040         ]);
20041         this.el.addClass(placement + ' bs-popover-' + placement);
20042         
20043         if (!this.alignEl ) {
20044             return false;
20045         }
20046         
20047         switch (placement) {
20048             case 'right':
20049                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20050                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20051                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20052                     //normal display... or moved up/down.
20053                     this.el.setXY(offset);
20054                     var xy = this.alignEl.getAnchorXY('tr', false);
20055                     xy[0]+=2;xy[1]+=5;
20056                     this.arrowEl.setXY(xy);
20057                     return true;
20058                 }
20059                 // continue through...
20060                 return this.updatePosition('left', false);
20061                 
20062             
20063             case 'left':
20064                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20065                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20066                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20067                     //normal display... or moved up/down.
20068                     this.el.setXY(offset);
20069                     var xy = this.alignEl.getAnchorXY('tl', false);
20070                     xy[0]-=10;xy[1]+=5; // << fix me
20071                     this.arrowEl.setXY(xy);
20072                     return true;
20073                 }
20074                 // call self...
20075                 return this.updatePosition('right', false);
20076             
20077             case 'top':
20078                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20079                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20080                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20081                     //normal display... or moved up/down.
20082                     this.el.setXY(offset);
20083                     var xy = this.alignEl.getAnchorXY('t', false);
20084                     xy[1]-=10; // << fix me
20085                     this.arrowEl.setXY(xy);
20086                     return true;
20087                 }
20088                 // fall through
20089                return this.updatePosition('bottom', false);
20090             
20091             case 'bottom':
20092                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20093                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20094                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20095                     //normal display... or moved up/down.
20096                     this.el.setXY(offset);
20097                     var xy = this.alignEl.getAnchorXY('b', false);
20098                      xy[1]+=2; // << fix me
20099                     this.arrowEl.setXY(xy);
20100                     return true;
20101                 }
20102                 // fall through
20103                 return this.updatePosition('top', false);
20104                 
20105             
20106         }
20107         
20108         
20109         return false;
20110     },
20111     
20112     hide : function()
20113     {
20114         this.el.setXY([0,0]);
20115         this.el.removeClass('in');
20116         this.el.hide();
20117         this.hoverState = null;
20118         this.maskEl.hide(); // always..
20119         this.fireEvent('hide', this);
20120     }
20121     
20122 });
20123
20124
20125 Roo.apply(Roo.bootstrap.Popover, {
20126
20127     alignment : {
20128         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20129         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20130         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20131         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20132     },
20133     
20134     zIndex : 20001,
20135
20136     clickHander : false,
20137     
20138
20139     onMouseDown : function(e)
20140     {
20141         if (!e.getTarget(".roo-popover")) {
20142             this.hideAll();
20143         }
20144          
20145     },
20146     
20147     popups : [],
20148     
20149     register : function(popup)
20150     {
20151         if (!Roo.bootstrap.Popover.clickHandler) {
20152             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20153         }
20154         // hide other popups.
20155         this.hideAll();
20156         this.popups.push(popup);
20157     },
20158     hideAll : function()
20159     {
20160         this.popups.forEach(function(p) {
20161             p.hide();
20162         });
20163     }
20164
20165 });/*
20166  * - LGPL
20167  *
20168  * Card header - holder for the card header elements.
20169  * 
20170  */
20171
20172 /**
20173  * @class Roo.bootstrap.PopoverNav
20174  * @extends Roo.bootstrap.NavGroup
20175  * Bootstrap Popover header navigation class
20176  * @constructor
20177  * Create a new Popover Header Navigation 
20178  * @param {Object} config The config object
20179  */
20180
20181 Roo.bootstrap.PopoverNav = function(config){
20182     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20183 };
20184
20185 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20186     
20187     
20188     container_method : 'getPopoverHeader' 
20189     
20190      
20191     
20192     
20193    
20194 });
20195
20196  
20197
20198  /*
20199  * - LGPL
20200  *
20201  * Progress
20202  * 
20203  */
20204
20205 /**
20206  * @class Roo.bootstrap.Progress
20207  * @extends Roo.bootstrap.Component
20208  * Bootstrap Progress class
20209  * @cfg {Boolean} striped striped of the progress bar
20210  * @cfg {Boolean} active animated of the progress bar
20211  * 
20212  * 
20213  * @constructor
20214  * Create a new Progress
20215  * @param {Object} config The config object
20216  */
20217
20218 Roo.bootstrap.Progress = function(config){
20219     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20220 };
20221
20222 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20223     
20224     striped : false,
20225     active: false,
20226     
20227     getAutoCreate : function(){
20228         var cfg = {
20229             tag: 'div',
20230             cls: 'progress'
20231         };
20232         
20233         
20234         if(this.striped){
20235             cfg.cls += ' progress-striped';
20236         }
20237       
20238         if(this.active){
20239             cfg.cls += ' active';
20240         }
20241         
20242         
20243         return cfg;
20244     }
20245    
20246 });
20247
20248  
20249
20250  /*
20251  * - LGPL
20252  *
20253  * ProgressBar
20254  * 
20255  */
20256
20257 /**
20258  * @class Roo.bootstrap.ProgressBar
20259  * @extends Roo.bootstrap.Component
20260  * Bootstrap ProgressBar class
20261  * @cfg {Number} aria_valuenow aria-value now
20262  * @cfg {Number} aria_valuemin aria-value min
20263  * @cfg {Number} aria_valuemax aria-value max
20264  * @cfg {String} label label for the progress bar
20265  * @cfg {String} panel (success | info | warning | danger )
20266  * @cfg {String} role role of the progress bar
20267  * @cfg {String} sr_only text
20268  * 
20269  * 
20270  * @constructor
20271  * Create a new ProgressBar
20272  * @param {Object} config The config object
20273  */
20274
20275 Roo.bootstrap.ProgressBar = function(config){
20276     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20277 };
20278
20279 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20280     
20281     aria_valuenow : 0,
20282     aria_valuemin : 0,
20283     aria_valuemax : 100,
20284     label : false,
20285     panel : false,
20286     role : false,
20287     sr_only: false,
20288     
20289     getAutoCreate : function()
20290     {
20291         
20292         var cfg = {
20293             tag: 'div',
20294             cls: 'progress-bar',
20295             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20296         };
20297         
20298         if(this.sr_only){
20299             cfg.cn = {
20300                 tag: 'span',
20301                 cls: 'sr-only',
20302                 html: this.sr_only
20303             }
20304         }
20305         
20306         if(this.role){
20307             cfg.role = this.role;
20308         }
20309         
20310         if(this.aria_valuenow){
20311             cfg['aria-valuenow'] = this.aria_valuenow;
20312         }
20313         
20314         if(this.aria_valuemin){
20315             cfg['aria-valuemin'] = this.aria_valuemin;
20316         }
20317         
20318         if(this.aria_valuemax){
20319             cfg['aria-valuemax'] = this.aria_valuemax;
20320         }
20321         
20322         if(this.label && !this.sr_only){
20323             cfg.html = this.label;
20324         }
20325         
20326         if(this.panel){
20327             cfg.cls += ' progress-bar-' + this.panel;
20328         }
20329         
20330         return cfg;
20331     },
20332     
20333     update : function(aria_valuenow)
20334     {
20335         this.aria_valuenow = aria_valuenow;
20336         
20337         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20338     }
20339    
20340 });
20341
20342  
20343
20344  /*
20345  * - LGPL
20346  *
20347  * column
20348  * 
20349  */
20350
20351 /**
20352  * @class Roo.bootstrap.TabGroup
20353  * @extends Roo.bootstrap.Column
20354  * Bootstrap Column class
20355  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20356  * @cfg {Boolean} carousel true to make the group behave like a carousel
20357  * @cfg {Boolean} bullets show bullets for the panels
20358  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20359  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20360  * @cfg {Boolean} showarrow (true|false) show arrow default true
20361  * 
20362  * @constructor
20363  * Create a new TabGroup
20364  * @param {Object} config The config object
20365  */
20366
20367 Roo.bootstrap.TabGroup = function(config){
20368     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20369     if (!this.navId) {
20370         this.navId = Roo.id();
20371     }
20372     this.tabs = [];
20373     Roo.bootstrap.TabGroup.register(this);
20374     
20375 };
20376
20377 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20378     
20379     carousel : false,
20380     transition : false,
20381     bullets : 0,
20382     timer : 0,
20383     autoslide : false,
20384     slideFn : false,
20385     slideOnTouch : false,
20386     showarrow : true,
20387     
20388     getAutoCreate : function()
20389     {
20390         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20391         
20392         cfg.cls += ' tab-content';
20393         
20394         if (this.carousel) {
20395             cfg.cls += ' carousel slide';
20396             
20397             cfg.cn = [{
20398                cls : 'carousel-inner',
20399                cn : []
20400             }];
20401         
20402             if(this.bullets  && !Roo.isTouch){
20403                 
20404                 var bullets = {
20405                     cls : 'carousel-bullets',
20406                     cn : []
20407                 };
20408                
20409                 if(this.bullets_cls){
20410                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20411                 }
20412                 
20413                 bullets.cn.push({
20414                     cls : 'clear'
20415                 });
20416                 
20417                 cfg.cn[0].cn.push(bullets);
20418             }
20419             
20420             if(this.showarrow){
20421                 cfg.cn[0].cn.push({
20422                     tag : 'div',
20423                     class : 'carousel-arrow',
20424                     cn : [
20425                         {
20426                             tag : 'div',
20427                             class : 'carousel-prev',
20428                             cn : [
20429                                 {
20430                                     tag : 'i',
20431                                     class : 'fa fa-chevron-left'
20432                                 }
20433                             ]
20434                         },
20435                         {
20436                             tag : 'div',
20437                             class : 'carousel-next',
20438                             cn : [
20439                                 {
20440                                     tag : 'i',
20441                                     class : 'fa fa-chevron-right'
20442                                 }
20443                             ]
20444                         }
20445                     ]
20446                 });
20447             }
20448             
20449         }
20450         
20451         return cfg;
20452     },
20453     
20454     initEvents:  function()
20455     {
20456 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20457 //            this.el.on("touchstart", this.onTouchStart, this);
20458 //        }
20459         
20460         if(this.autoslide){
20461             var _this = this;
20462             
20463             this.slideFn = window.setInterval(function() {
20464                 _this.showPanelNext();
20465             }, this.timer);
20466         }
20467         
20468         if(this.showarrow){
20469             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20470             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20471         }
20472         
20473         
20474     },
20475     
20476 //    onTouchStart : function(e, el, o)
20477 //    {
20478 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20479 //            return;
20480 //        }
20481 //        
20482 //        this.showPanelNext();
20483 //    },
20484     
20485     
20486     getChildContainer : function()
20487     {
20488         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20489     },
20490     
20491     /**
20492     * register a Navigation item
20493     * @param {Roo.bootstrap.NavItem} the navitem to add
20494     */
20495     register : function(item)
20496     {
20497         this.tabs.push( item);
20498         item.navId = this.navId; // not really needed..
20499         this.addBullet();
20500     
20501     },
20502     
20503     getActivePanel : function()
20504     {
20505         var r = false;
20506         Roo.each(this.tabs, function(t) {
20507             if (t.active) {
20508                 r = t;
20509                 return false;
20510             }
20511             return null;
20512         });
20513         return r;
20514         
20515     },
20516     getPanelByName : function(n)
20517     {
20518         var r = false;
20519         Roo.each(this.tabs, function(t) {
20520             if (t.tabId == n) {
20521                 r = t;
20522                 return false;
20523             }
20524             return null;
20525         });
20526         return r;
20527     },
20528     indexOfPanel : function(p)
20529     {
20530         var r = false;
20531         Roo.each(this.tabs, function(t,i) {
20532             if (t.tabId == p.tabId) {
20533                 r = i;
20534                 return false;
20535             }
20536             return null;
20537         });
20538         return r;
20539     },
20540     /**
20541      * show a specific panel
20542      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20543      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20544      */
20545     showPanel : function (pan)
20546     {
20547         if(this.transition || typeof(pan) == 'undefined'){
20548             Roo.log("waiting for the transitionend");
20549             return false;
20550         }
20551         
20552         if (typeof(pan) == 'number') {
20553             pan = this.tabs[pan];
20554         }
20555         
20556         if (typeof(pan) == 'string') {
20557             pan = this.getPanelByName(pan);
20558         }
20559         
20560         var cur = this.getActivePanel();
20561         
20562         if(!pan || !cur){
20563             Roo.log('pan or acitve pan is undefined');
20564             return false;
20565         }
20566         
20567         if (pan.tabId == this.getActivePanel().tabId) {
20568             return true;
20569         }
20570         
20571         if (false === cur.fireEvent('beforedeactivate')) {
20572             return false;
20573         }
20574         
20575         if(this.bullets > 0 && !Roo.isTouch){
20576             this.setActiveBullet(this.indexOfPanel(pan));
20577         }
20578         
20579         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20580             
20581             //class="carousel-item carousel-item-next carousel-item-left"
20582             
20583             this.transition = true;
20584             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20585             var lr = dir == 'next' ? 'left' : 'right';
20586             pan.el.addClass(dir); // or prev
20587             pan.el.addClass('carousel-item-' + dir); // or prev
20588             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20589             cur.el.addClass(lr); // or right
20590             pan.el.addClass(lr);
20591             cur.el.addClass('carousel-item-' +lr); // or right
20592             pan.el.addClass('carousel-item-' +lr);
20593             
20594             
20595             var _this = this;
20596             cur.el.on('transitionend', function() {
20597                 Roo.log("trans end?");
20598                 
20599                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20600                 pan.setActive(true);
20601                 
20602                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20603                 cur.setActive(false);
20604                 
20605                 _this.transition = false;
20606                 
20607             }, this, { single:  true } );
20608             
20609             return true;
20610         }
20611         
20612         cur.setActive(false);
20613         pan.setActive(true);
20614         
20615         return true;
20616         
20617     },
20618     showPanelNext : function()
20619     {
20620         var i = this.indexOfPanel(this.getActivePanel());
20621         
20622         if (i >= this.tabs.length - 1 && !this.autoslide) {
20623             return;
20624         }
20625         
20626         if (i >= this.tabs.length - 1 && this.autoslide) {
20627             i = -1;
20628         }
20629         
20630         this.showPanel(this.tabs[i+1]);
20631     },
20632     
20633     showPanelPrev : function()
20634     {
20635         var i = this.indexOfPanel(this.getActivePanel());
20636         
20637         if (i  < 1 && !this.autoslide) {
20638             return;
20639         }
20640         
20641         if (i < 1 && this.autoslide) {
20642             i = this.tabs.length;
20643         }
20644         
20645         this.showPanel(this.tabs[i-1]);
20646     },
20647     
20648     
20649     addBullet: function()
20650     {
20651         if(!this.bullets || Roo.isTouch){
20652             return;
20653         }
20654         var ctr = this.el.select('.carousel-bullets',true).first();
20655         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20656         var bullet = ctr.createChild({
20657             cls : 'bullet bullet-' + i
20658         },ctr.dom.lastChild);
20659         
20660         
20661         var _this = this;
20662         
20663         bullet.on('click', (function(e, el, o, ii, t){
20664
20665             e.preventDefault();
20666
20667             this.showPanel(ii);
20668
20669             if(this.autoslide && this.slideFn){
20670                 clearInterval(this.slideFn);
20671                 this.slideFn = window.setInterval(function() {
20672                     _this.showPanelNext();
20673                 }, this.timer);
20674             }
20675
20676         }).createDelegate(this, [i, bullet], true));
20677                 
20678         
20679     },
20680      
20681     setActiveBullet : function(i)
20682     {
20683         if(Roo.isTouch){
20684             return;
20685         }
20686         
20687         Roo.each(this.el.select('.bullet', true).elements, function(el){
20688             el.removeClass('selected');
20689         });
20690
20691         var bullet = this.el.select('.bullet-' + i, true).first();
20692         
20693         if(!bullet){
20694             return;
20695         }
20696         
20697         bullet.addClass('selected');
20698     }
20699     
20700     
20701   
20702 });
20703
20704  
20705
20706  
20707  
20708 Roo.apply(Roo.bootstrap.TabGroup, {
20709     
20710     groups: {},
20711      /**
20712     * register a Navigation Group
20713     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20714     */
20715     register : function(navgrp)
20716     {
20717         this.groups[navgrp.navId] = navgrp;
20718         
20719     },
20720     /**
20721     * fetch a Navigation Group based on the navigation ID
20722     * if one does not exist , it will get created.
20723     * @param {string} the navgroup to add
20724     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20725     */
20726     get: function(navId) {
20727         if (typeof(this.groups[navId]) == 'undefined') {
20728             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20729         }
20730         return this.groups[navId] ;
20731     }
20732     
20733     
20734     
20735 });
20736
20737  /*
20738  * - LGPL
20739  *
20740  * TabPanel
20741  * 
20742  */
20743
20744 /**
20745  * @class Roo.bootstrap.TabPanel
20746  * @extends Roo.bootstrap.Component
20747  * Bootstrap TabPanel class
20748  * @cfg {Boolean} active panel active
20749  * @cfg {String} html panel content
20750  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20751  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20752  * @cfg {String} href click to link..
20753  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20754  * 
20755  * 
20756  * @constructor
20757  * Create a new TabPanel
20758  * @param {Object} config The config object
20759  */
20760
20761 Roo.bootstrap.TabPanel = function(config){
20762     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20763     this.addEvents({
20764         /**
20765              * @event changed
20766              * Fires when the active status changes
20767              * @param {Roo.bootstrap.TabPanel} this
20768              * @param {Boolean} state the new state
20769             
20770          */
20771         'changed': true,
20772         /**
20773              * @event beforedeactivate
20774              * Fires before a tab is de-activated - can be used to do validation on a form.
20775              * @param {Roo.bootstrap.TabPanel} this
20776              * @return {Boolean} false if there is an error
20777             
20778          */
20779         'beforedeactivate': true
20780      });
20781     
20782     this.tabId = this.tabId || Roo.id();
20783   
20784 };
20785
20786 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20787     
20788     active: false,
20789     html: false,
20790     tabId: false,
20791     navId : false,
20792     href : '',
20793     touchSlide : false,
20794     getAutoCreate : function(){
20795         
20796         
20797         var cfg = {
20798             tag: 'div',
20799             // item is needed for carousel - not sure if it has any effect otherwise
20800             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20801             html: this.html || ''
20802         };
20803         
20804         if(this.active){
20805             cfg.cls += ' active';
20806         }
20807         
20808         if(this.tabId){
20809             cfg.tabId = this.tabId;
20810         }
20811         
20812         
20813         
20814         return cfg;
20815     },
20816     
20817     initEvents:  function()
20818     {
20819         var p = this.parent();
20820         
20821         this.navId = this.navId || p.navId;
20822         
20823         if (typeof(this.navId) != 'undefined') {
20824             // not really needed.. but just in case.. parent should be a NavGroup.
20825             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20826             
20827             tg.register(this);
20828             
20829             var i = tg.tabs.length - 1;
20830             
20831             if(this.active && tg.bullets > 0 && i < tg.bullets){
20832                 tg.setActiveBullet(i);
20833             }
20834         }
20835         
20836         this.el.on('click', this.onClick, this);
20837         
20838         if(Roo.isTouch && this.touchSlide){
20839             this.el.on("touchstart", this.onTouchStart, this);
20840             this.el.on("touchmove", this.onTouchMove, this);
20841             this.el.on("touchend", this.onTouchEnd, this);
20842         }
20843         
20844     },
20845     
20846     onRender : function(ct, position)
20847     {
20848         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20849     },
20850     
20851     setActive : function(state)
20852     {
20853         Roo.log("panel - set active " + this.tabId + "=" + state);
20854         
20855         this.active = state;
20856         if (!state) {
20857             this.el.removeClass('active');
20858             
20859         } else  if (!this.el.hasClass('active')) {
20860             this.el.addClass('active');
20861         }
20862         
20863         this.fireEvent('changed', this, state);
20864     },
20865     
20866     onClick : function(e)
20867     {
20868         e.preventDefault();
20869         
20870         if(!this.href.length){
20871             return;
20872         }
20873         
20874         window.location.href = this.href;
20875     },
20876     
20877     startX : 0,
20878     startY : 0,
20879     endX : 0,
20880     endY : 0,
20881     swiping : false,
20882     
20883     onTouchStart : function(e)
20884     {
20885         this.swiping = false;
20886         
20887         this.startX = e.browserEvent.touches[0].clientX;
20888         this.startY = e.browserEvent.touches[0].clientY;
20889     },
20890     
20891     onTouchMove : function(e)
20892     {
20893         this.swiping = true;
20894         
20895         this.endX = e.browserEvent.touches[0].clientX;
20896         this.endY = e.browserEvent.touches[0].clientY;
20897     },
20898     
20899     onTouchEnd : function(e)
20900     {
20901         if(!this.swiping){
20902             this.onClick(e);
20903             return;
20904         }
20905         
20906         var tabGroup = this.parent();
20907         
20908         if(this.endX > this.startX){ // swiping right
20909             tabGroup.showPanelPrev();
20910             return;
20911         }
20912         
20913         if(this.startX > this.endX){ // swiping left
20914             tabGroup.showPanelNext();
20915             return;
20916         }
20917     }
20918     
20919     
20920 });
20921  
20922
20923  
20924
20925  /*
20926  * - LGPL
20927  *
20928  * DateField
20929  * 
20930  */
20931
20932 /**
20933  * @class Roo.bootstrap.DateField
20934  * @extends Roo.bootstrap.Input
20935  * Bootstrap DateField class
20936  * @cfg {Number} weekStart default 0
20937  * @cfg {String} viewMode default empty, (months|years)
20938  * @cfg {String} minViewMode default empty, (months|years)
20939  * @cfg {Number} startDate default -Infinity
20940  * @cfg {Number} endDate default Infinity
20941  * @cfg {Boolean} todayHighlight default false
20942  * @cfg {Boolean} todayBtn default false
20943  * @cfg {Boolean} calendarWeeks default false
20944  * @cfg {Object} daysOfWeekDisabled default empty
20945  * @cfg {Boolean} singleMode default false (true | false)
20946  * 
20947  * @cfg {Boolean} keyboardNavigation default true
20948  * @cfg {String} language default en
20949  * 
20950  * @constructor
20951  * Create a new DateField
20952  * @param {Object} config The config object
20953  */
20954
20955 Roo.bootstrap.DateField = function(config){
20956     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20957      this.addEvents({
20958             /**
20959              * @event show
20960              * Fires when this field show.
20961              * @param {Roo.bootstrap.DateField} this
20962              * @param {Mixed} date The date value
20963              */
20964             show : true,
20965             /**
20966              * @event show
20967              * Fires when this field hide.
20968              * @param {Roo.bootstrap.DateField} this
20969              * @param {Mixed} date The date value
20970              */
20971             hide : true,
20972             /**
20973              * @event select
20974              * Fires when select a date.
20975              * @param {Roo.bootstrap.DateField} this
20976              * @param {Mixed} date The date value
20977              */
20978             select : true,
20979             /**
20980              * @event beforeselect
20981              * Fires when before select a date.
20982              * @param {Roo.bootstrap.DateField} this
20983              * @param {Mixed} date The date value
20984              */
20985             beforeselect : true
20986         });
20987 };
20988
20989 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20990     
20991     /**
20992      * @cfg {String} format
20993      * The default date format string which can be overriden for localization support.  The format must be
20994      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20995      */
20996     format : "m/d/y",
20997     /**
20998      * @cfg {String} altFormats
20999      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21000      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21001      */
21002     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21003     
21004     weekStart : 0,
21005     
21006     viewMode : '',
21007     
21008     minViewMode : '',
21009     
21010     todayHighlight : false,
21011     
21012     todayBtn: false,
21013     
21014     language: 'en',
21015     
21016     keyboardNavigation: true,
21017     
21018     calendarWeeks: false,
21019     
21020     startDate: -Infinity,
21021     
21022     endDate: Infinity,
21023     
21024     daysOfWeekDisabled: [],
21025     
21026     _events: [],
21027     
21028     singleMode : false,
21029     
21030     UTCDate: function()
21031     {
21032         return new Date(Date.UTC.apply(Date, arguments));
21033     },
21034     
21035     UTCToday: function()
21036     {
21037         var today = new Date();
21038         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21039     },
21040     
21041     getDate: function() {
21042             var d = this.getUTCDate();
21043             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21044     },
21045     
21046     getUTCDate: function() {
21047             return this.date;
21048     },
21049     
21050     setDate: function(d) {
21051             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21052     },
21053     
21054     setUTCDate: function(d) {
21055             this.date = d;
21056             this.setValue(this.formatDate(this.date));
21057     },
21058         
21059     onRender: function(ct, position)
21060     {
21061         
21062         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21063         
21064         this.language = this.language || 'en';
21065         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21066         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21067         
21068         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21069         this.format = this.format || 'm/d/y';
21070         this.isInline = false;
21071         this.isInput = true;
21072         this.component = this.el.select('.add-on', true).first() || false;
21073         this.component = (this.component && this.component.length === 0) ? false : this.component;
21074         this.hasInput = this.component && this.inputEl().length;
21075         
21076         if (typeof(this.minViewMode === 'string')) {
21077             switch (this.minViewMode) {
21078                 case 'months':
21079                     this.minViewMode = 1;
21080                     break;
21081                 case 'years':
21082                     this.minViewMode = 2;
21083                     break;
21084                 default:
21085                     this.minViewMode = 0;
21086                     break;
21087             }
21088         }
21089         
21090         if (typeof(this.viewMode === 'string')) {
21091             switch (this.viewMode) {
21092                 case 'months':
21093                     this.viewMode = 1;
21094                     break;
21095                 case 'years':
21096                     this.viewMode = 2;
21097                     break;
21098                 default:
21099                     this.viewMode = 0;
21100                     break;
21101             }
21102         }
21103                 
21104         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21105         
21106 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21107         
21108         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21109         
21110         this.picker().on('mousedown', this.onMousedown, this);
21111         this.picker().on('click', this.onClick, this);
21112         
21113         this.picker().addClass('datepicker-dropdown');
21114         
21115         this.startViewMode = this.viewMode;
21116         
21117         if(this.singleMode){
21118             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21119                 v.setVisibilityMode(Roo.Element.DISPLAY);
21120                 v.hide();
21121             });
21122             
21123             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21124                 v.setStyle('width', '189px');
21125             });
21126         }
21127         
21128         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21129             if(!this.calendarWeeks){
21130                 v.remove();
21131                 return;
21132             }
21133             
21134             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21135             v.attr('colspan', function(i, val){
21136                 return parseInt(val) + 1;
21137             });
21138         });
21139                         
21140         
21141         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21142         
21143         this.setStartDate(this.startDate);
21144         this.setEndDate(this.endDate);
21145         
21146         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21147         
21148         this.fillDow();
21149         this.fillMonths();
21150         this.update();
21151         this.showMode();
21152         
21153         if(this.isInline) {
21154             this.showPopup();
21155         }
21156     },
21157     
21158     picker : function()
21159     {
21160         return this.pickerEl;
21161 //        return this.el.select('.datepicker', true).first();
21162     },
21163     
21164     fillDow: function()
21165     {
21166         var dowCnt = this.weekStart;
21167         
21168         var dow = {
21169             tag: 'tr',
21170             cn: [
21171                 
21172             ]
21173         };
21174         
21175         if(this.calendarWeeks){
21176             dow.cn.push({
21177                 tag: 'th',
21178                 cls: 'cw',
21179                 html: '&nbsp;'
21180             })
21181         }
21182         
21183         while (dowCnt < this.weekStart + 7) {
21184             dow.cn.push({
21185                 tag: 'th',
21186                 cls: 'dow',
21187                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21188             });
21189         }
21190         
21191         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21192     },
21193     
21194     fillMonths: function()
21195     {    
21196         var i = 0;
21197         var months = this.picker().select('>.datepicker-months td', true).first();
21198         
21199         months.dom.innerHTML = '';
21200         
21201         while (i < 12) {
21202             var month = {
21203                 tag: 'span',
21204                 cls: 'month',
21205                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21206             };
21207             
21208             months.createChild(month);
21209         }
21210         
21211     },
21212     
21213     update: function()
21214     {
21215         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;
21216         
21217         if (this.date < this.startDate) {
21218             this.viewDate = new Date(this.startDate);
21219         } else if (this.date > this.endDate) {
21220             this.viewDate = new Date(this.endDate);
21221         } else {
21222             this.viewDate = new Date(this.date);
21223         }
21224         
21225         this.fill();
21226     },
21227     
21228     fill: function() 
21229     {
21230         var d = new Date(this.viewDate),
21231                 year = d.getUTCFullYear(),
21232                 month = d.getUTCMonth(),
21233                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21234                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21235                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21236                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21237                 currentDate = this.date && this.date.valueOf(),
21238                 today = this.UTCToday();
21239         
21240         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21241         
21242 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21243         
21244 //        this.picker.select('>tfoot th.today').
21245 //                                              .text(dates[this.language].today)
21246 //                                              .toggle(this.todayBtn !== false);
21247     
21248         this.updateNavArrows();
21249         this.fillMonths();
21250                                                 
21251         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21252         
21253         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21254          
21255         prevMonth.setUTCDate(day);
21256         
21257         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21258         
21259         var nextMonth = new Date(prevMonth);
21260         
21261         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21262         
21263         nextMonth = nextMonth.valueOf();
21264         
21265         var fillMonths = false;
21266         
21267         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21268         
21269         while(prevMonth.valueOf() <= nextMonth) {
21270             var clsName = '';
21271             
21272             if (prevMonth.getUTCDay() === this.weekStart) {
21273                 if(fillMonths){
21274                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21275                 }
21276                     
21277                 fillMonths = {
21278                     tag: 'tr',
21279                     cn: []
21280                 };
21281                 
21282                 if(this.calendarWeeks){
21283                     // ISO 8601: First week contains first thursday.
21284                     // ISO also states week starts on Monday, but we can be more abstract here.
21285                     var
21286                     // Start of current week: based on weekstart/current date
21287                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21288                     // Thursday of this week
21289                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21290                     // First Thursday of year, year from thursday
21291                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21292                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21293                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21294                     
21295                     fillMonths.cn.push({
21296                         tag: 'td',
21297                         cls: 'cw',
21298                         html: calWeek
21299                     });
21300                 }
21301             }
21302             
21303             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21304                 clsName += ' old';
21305             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21306                 clsName += ' new';
21307             }
21308             if (this.todayHighlight &&
21309                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21310                 prevMonth.getUTCMonth() == today.getMonth() &&
21311                 prevMonth.getUTCDate() == today.getDate()) {
21312                 clsName += ' today';
21313             }
21314             
21315             if (currentDate && prevMonth.valueOf() === currentDate) {
21316                 clsName += ' active';
21317             }
21318             
21319             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21320                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21321                     clsName += ' disabled';
21322             }
21323             
21324             fillMonths.cn.push({
21325                 tag: 'td',
21326                 cls: 'day ' + clsName,
21327                 html: prevMonth.getDate()
21328             });
21329             
21330             prevMonth.setDate(prevMonth.getDate()+1);
21331         }
21332           
21333         var currentYear = this.date && this.date.getUTCFullYear();
21334         var currentMonth = this.date && this.date.getUTCMonth();
21335         
21336         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21337         
21338         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21339             v.removeClass('active');
21340             
21341             if(currentYear === year && k === currentMonth){
21342                 v.addClass('active');
21343             }
21344             
21345             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21346                 v.addClass('disabled');
21347             }
21348             
21349         });
21350         
21351         
21352         year = parseInt(year/10, 10) * 10;
21353         
21354         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21355         
21356         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21357         
21358         year -= 1;
21359         for (var i = -1; i < 11; i++) {
21360             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21361                 tag: 'span',
21362                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21363                 html: year
21364             });
21365             
21366             year += 1;
21367         }
21368     },
21369     
21370     showMode: function(dir) 
21371     {
21372         if (dir) {
21373             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21374         }
21375         
21376         Roo.each(this.picker().select('>div',true).elements, function(v){
21377             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21378             v.hide();
21379         });
21380         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21381     },
21382     
21383     place: function()
21384     {
21385         if(this.isInline) {
21386             return;
21387         }
21388         
21389         this.picker().removeClass(['bottom', 'top']);
21390         
21391         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21392             /*
21393              * place to the top of element!
21394              *
21395              */
21396             
21397             this.picker().addClass('top');
21398             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21399             
21400             return;
21401         }
21402         
21403         this.picker().addClass('bottom');
21404         
21405         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21406     },
21407     
21408     parseDate : function(value)
21409     {
21410         if(!value || value instanceof Date){
21411             return value;
21412         }
21413         var v = Date.parseDate(value, this.format);
21414         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21415             v = Date.parseDate(value, 'Y-m-d');
21416         }
21417         if(!v && this.altFormats){
21418             if(!this.altFormatsArray){
21419                 this.altFormatsArray = this.altFormats.split("|");
21420             }
21421             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21422                 v = Date.parseDate(value, this.altFormatsArray[i]);
21423             }
21424         }
21425         return v;
21426     },
21427     
21428     formatDate : function(date, fmt)
21429     {   
21430         return (!date || !(date instanceof Date)) ?
21431         date : date.dateFormat(fmt || this.format);
21432     },
21433     
21434     onFocus : function()
21435     {
21436         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21437         this.showPopup();
21438     },
21439     
21440     onBlur : function()
21441     {
21442         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21443         
21444         var d = this.inputEl().getValue();
21445         
21446         this.setValue(d);
21447                 
21448         this.hidePopup();
21449     },
21450     
21451     showPopup : function()
21452     {
21453         this.picker().show();
21454         this.update();
21455         this.place();
21456         
21457         this.fireEvent('showpopup', this, this.date);
21458     },
21459     
21460     hidePopup : function()
21461     {
21462         if(this.isInline) {
21463             return;
21464         }
21465         this.picker().hide();
21466         this.viewMode = this.startViewMode;
21467         this.showMode();
21468         
21469         this.fireEvent('hidepopup', this, this.date);
21470         
21471     },
21472     
21473     onMousedown: function(e)
21474     {
21475         e.stopPropagation();
21476         e.preventDefault();
21477     },
21478     
21479     keyup: function(e)
21480     {
21481         Roo.bootstrap.DateField.superclass.keyup.call(this);
21482         this.update();
21483     },
21484
21485     setValue: function(v)
21486     {
21487         if(this.fireEvent('beforeselect', this, v) !== false){
21488             var d = new Date(this.parseDate(v) ).clearTime();
21489         
21490             if(isNaN(d.getTime())){
21491                 this.date = this.viewDate = '';
21492                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21493                 return;
21494             }
21495
21496             v = this.formatDate(d);
21497
21498             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21499
21500             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21501
21502             this.update();
21503
21504             this.fireEvent('select', this, this.date);
21505         }
21506     },
21507     
21508     getValue: function()
21509     {
21510         return this.formatDate(this.date);
21511     },
21512     
21513     fireKey: function(e)
21514     {
21515         if (!this.picker().isVisible()){
21516             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21517                 this.showPopup();
21518             }
21519             return;
21520         }
21521         
21522         var dateChanged = false,
21523         dir, day, month,
21524         newDate, newViewDate;
21525         
21526         switch(e.keyCode){
21527             case 27: // escape
21528                 this.hidePopup();
21529                 e.preventDefault();
21530                 break;
21531             case 37: // left
21532             case 39: // right
21533                 if (!this.keyboardNavigation) {
21534                     break;
21535                 }
21536                 dir = e.keyCode == 37 ? -1 : 1;
21537                 
21538                 if (e.ctrlKey){
21539                     newDate = this.moveYear(this.date, dir);
21540                     newViewDate = this.moveYear(this.viewDate, dir);
21541                 } else if (e.shiftKey){
21542                     newDate = this.moveMonth(this.date, dir);
21543                     newViewDate = this.moveMonth(this.viewDate, dir);
21544                 } else {
21545                     newDate = new Date(this.date);
21546                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21547                     newViewDate = new Date(this.viewDate);
21548                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21549                 }
21550                 if (this.dateWithinRange(newDate)){
21551                     this.date = newDate;
21552                     this.viewDate = newViewDate;
21553                     this.setValue(this.formatDate(this.date));
21554 //                    this.update();
21555                     e.preventDefault();
21556                     dateChanged = true;
21557                 }
21558                 break;
21559             case 38: // up
21560             case 40: // down
21561                 if (!this.keyboardNavigation) {
21562                     break;
21563                 }
21564                 dir = e.keyCode == 38 ? -1 : 1;
21565                 if (e.ctrlKey){
21566                     newDate = this.moveYear(this.date, dir);
21567                     newViewDate = this.moveYear(this.viewDate, dir);
21568                 } else if (e.shiftKey){
21569                     newDate = this.moveMonth(this.date, dir);
21570                     newViewDate = this.moveMonth(this.viewDate, dir);
21571                 } else {
21572                     newDate = new Date(this.date);
21573                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21574                     newViewDate = new Date(this.viewDate);
21575                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21576                 }
21577                 if (this.dateWithinRange(newDate)){
21578                     this.date = newDate;
21579                     this.viewDate = newViewDate;
21580                     this.setValue(this.formatDate(this.date));
21581 //                    this.update();
21582                     e.preventDefault();
21583                     dateChanged = true;
21584                 }
21585                 break;
21586             case 13: // enter
21587                 this.setValue(this.formatDate(this.date));
21588                 this.hidePopup();
21589                 e.preventDefault();
21590                 break;
21591             case 9: // tab
21592                 this.setValue(this.formatDate(this.date));
21593                 this.hidePopup();
21594                 break;
21595             case 16: // shift
21596             case 17: // ctrl
21597             case 18: // alt
21598                 break;
21599             default :
21600                 this.hidePopup();
21601                 
21602         }
21603     },
21604     
21605     
21606     onClick: function(e) 
21607     {
21608         e.stopPropagation();
21609         e.preventDefault();
21610         
21611         var target = e.getTarget();
21612         
21613         if(target.nodeName.toLowerCase() === 'i'){
21614             target = Roo.get(target).dom.parentNode;
21615         }
21616         
21617         var nodeName = target.nodeName;
21618         var className = target.className;
21619         var html = target.innerHTML;
21620         //Roo.log(nodeName);
21621         
21622         switch(nodeName.toLowerCase()) {
21623             case 'th':
21624                 switch(className) {
21625                     case 'switch':
21626                         this.showMode(1);
21627                         break;
21628                     case 'prev':
21629                     case 'next':
21630                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21631                         switch(this.viewMode){
21632                                 case 0:
21633                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21634                                         break;
21635                                 case 1:
21636                                 case 2:
21637                                         this.viewDate = this.moveYear(this.viewDate, dir);
21638                                         break;
21639                         }
21640                         this.fill();
21641                         break;
21642                     case 'today':
21643                         var date = new Date();
21644                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21645 //                        this.fill()
21646                         this.setValue(this.formatDate(this.date));
21647                         
21648                         this.hidePopup();
21649                         break;
21650                 }
21651                 break;
21652             case 'span':
21653                 if (className.indexOf('disabled') < 0) {
21654                     this.viewDate.setUTCDate(1);
21655                     if (className.indexOf('month') > -1) {
21656                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21657                     } else {
21658                         var year = parseInt(html, 10) || 0;
21659                         this.viewDate.setUTCFullYear(year);
21660                         
21661                     }
21662                     
21663                     if(this.singleMode){
21664                         this.setValue(this.formatDate(this.viewDate));
21665                         this.hidePopup();
21666                         return;
21667                     }
21668                     
21669                     this.showMode(-1);
21670                     this.fill();
21671                 }
21672                 break;
21673                 
21674             case 'td':
21675                 //Roo.log(className);
21676                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21677                     var day = parseInt(html, 10) || 1;
21678                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21679                         month = (this.viewDate || new Date()).getUTCMonth();
21680
21681                     if (className.indexOf('old') > -1) {
21682                         if(month === 0 ){
21683                             month = 11;
21684                             year -= 1;
21685                         }else{
21686                             month -= 1;
21687                         }
21688                     } else if (className.indexOf('new') > -1) {
21689                         if (month == 11) {
21690                             month = 0;
21691                             year += 1;
21692                         } else {
21693                             month += 1;
21694                         }
21695                     }
21696                     //Roo.log([year,month,day]);
21697                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21698                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21699 //                    this.fill();
21700                     //Roo.log(this.formatDate(this.date));
21701                     this.setValue(this.formatDate(this.date));
21702                     this.hidePopup();
21703                 }
21704                 break;
21705         }
21706     },
21707     
21708     setStartDate: function(startDate)
21709     {
21710         this.startDate = startDate || -Infinity;
21711         if (this.startDate !== -Infinity) {
21712             this.startDate = this.parseDate(this.startDate);
21713         }
21714         this.update();
21715         this.updateNavArrows();
21716     },
21717
21718     setEndDate: function(endDate)
21719     {
21720         this.endDate = endDate || Infinity;
21721         if (this.endDate !== Infinity) {
21722             this.endDate = this.parseDate(this.endDate);
21723         }
21724         this.update();
21725         this.updateNavArrows();
21726     },
21727     
21728     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21729     {
21730         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21731         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21732             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21733         }
21734         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21735             return parseInt(d, 10);
21736         });
21737         this.update();
21738         this.updateNavArrows();
21739     },
21740     
21741     updateNavArrows: function() 
21742     {
21743         if(this.singleMode){
21744             return;
21745         }
21746         
21747         var d = new Date(this.viewDate),
21748         year = d.getUTCFullYear(),
21749         month = d.getUTCMonth();
21750         
21751         Roo.each(this.picker().select('.prev', true).elements, function(v){
21752             v.show();
21753             switch (this.viewMode) {
21754                 case 0:
21755
21756                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21757                         v.hide();
21758                     }
21759                     break;
21760                 case 1:
21761                 case 2:
21762                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21763                         v.hide();
21764                     }
21765                     break;
21766             }
21767         });
21768         
21769         Roo.each(this.picker().select('.next', true).elements, function(v){
21770             v.show();
21771             switch (this.viewMode) {
21772                 case 0:
21773
21774                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21775                         v.hide();
21776                     }
21777                     break;
21778                 case 1:
21779                 case 2:
21780                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21781                         v.hide();
21782                     }
21783                     break;
21784             }
21785         })
21786     },
21787     
21788     moveMonth: function(date, dir)
21789     {
21790         if (!dir) {
21791             return date;
21792         }
21793         var new_date = new Date(date.valueOf()),
21794         day = new_date.getUTCDate(),
21795         month = new_date.getUTCMonth(),
21796         mag = Math.abs(dir),
21797         new_month, test;
21798         dir = dir > 0 ? 1 : -1;
21799         if (mag == 1){
21800             test = dir == -1
21801             // If going back one month, make sure month is not current month
21802             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21803             ? function(){
21804                 return new_date.getUTCMonth() == month;
21805             }
21806             // If going forward one month, make sure month is as expected
21807             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21808             : function(){
21809                 return new_date.getUTCMonth() != new_month;
21810             };
21811             new_month = month + dir;
21812             new_date.setUTCMonth(new_month);
21813             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21814             if (new_month < 0 || new_month > 11) {
21815                 new_month = (new_month + 12) % 12;
21816             }
21817         } else {
21818             // For magnitudes >1, move one month at a time...
21819             for (var i=0; i<mag; i++) {
21820                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21821                 new_date = this.moveMonth(new_date, dir);
21822             }
21823             // ...then reset the day, keeping it in the new month
21824             new_month = new_date.getUTCMonth();
21825             new_date.setUTCDate(day);
21826             test = function(){
21827                 return new_month != new_date.getUTCMonth();
21828             };
21829         }
21830         // Common date-resetting loop -- if date is beyond end of month, make it
21831         // end of month
21832         while (test()){
21833             new_date.setUTCDate(--day);
21834             new_date.setUTCMonth(new_month);
21835         }
21836         return new_date;
21837     },
21838
21839     moveYear: function(date, dir)
21840     {
21841         return this.moveMonth(date, dir*12);
21842     },
21843
21844     dateWithinRange: function(date)
21845     {
21846         return date >= this.startDate && date <= this.endDate;
21847     },
21848
21849     
21850     remove: function() 
21851     {
21852         this.picker().remove();
21853     },
21854     
21855     validateValue : function(value)
21856     {
21857         if(this.getVisibilityEl().hasClass('hidden')){
21858             return true;
21859         }
21860         
21861         if(value.length < 1)  {
21862             if(this.allowBlank){
21863                 return true;
21864             }
21865             return false;
21866         }
21867         
21868         if(value.length < this.minLength){
21869             return false;
21870         }
21871         if(value.length > this.maxLength){
21872             return false;
21873         }
21874         if(this.vtype){
21875             var vt = Roo.form.VTypes;
21876             if(!vt[this.vtype](value, this)){
21877                 return false;
21878             }
21879         }
21880         if(typeof this.validator == "function"){
21881             var msg = this.validator(value);
21882             if(msg !== true){
21883                 return false;
21884             }
21885         }
21886         
21887         if(this.regex && !this.regex.test(value)){
21888             return false;
21889         }
21890         
21891         if(typeof(this.parseDate(value)) == 'undefined'){
21892             return false;
21893         }
21894         
21895         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21896             return false;
21897         }      
21898         
21899         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21900             return false;
21901         } 
21902         
21903         
21904         return true;
21905     },
21906     
21907     reset : function()
21908     {
21909         this.date = this.viewDate = '';
21910         
21911         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21912     }
21913    
21914 });
21915
21916 Roo.apply(Roo.bootstrap.DateField,  {
21917     
21918     head : {
21919         tag: 'thead',
21920         cn: [
21921         {
21922             tag: 'tr',
21923             cn: [
21924             {
21925                 tag: 'th',
21926                 cls: 'prev',
21927                 html: '<i class="fa fa-arrow-left"/>'
21928             },
21929             {
21930                 tag: 'th',
21931                 cls: 'switch',
21932                 colspan: '5'
21933             },
21934             {
21935                 tag: 'th',
21936                 cls: 'next',
21937                 html: '<i class="fa fa-arrow-right"/>'
21938             }
21939
21940             ]
21941         }
21942         ]
21943     },
21944     
21945     content : {
21946         tag: 'tbody',
21947         cn: [
21948         {
21949             tag: 'tr',
21950             cn: [
21951             {
21952                 tag: 'td',
21953                 colspan: '7'
21954             }
21955             ]
21956         }
21957         ]
21958     },
21959     
21960     footer : {
21961         tag: 'tfoot',
21962         cn: [
21963         {
21964             tag: 'tr',
21965             cn: [
21966             {
21967                 tag: 'th',
21968                 colspan: '7',
21969                 cls: 'today'
21970             }
21971                     
21972             ]
21973         }
21974         ]
21975     },
21976     
21977     dates:{
21978         en: {
21979             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21980             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21981             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21982             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21983             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21984             today: "Today"
21985         }
21986     },
21987     
21988     modes: [
21989     {
21990         clsName: 'days',
21991         navFnc: 'Month',
21992         navStep: 1
21993     },
21994     {
21995         clsName: 'months',
21996         navFnc: 'FullYear',
21997         navStep: 1
21998     },
21999     {
22000         clsName: 'years',
22001         navFnc: 'FullYear',
22002         navStep: 10
22003     }]
22004 });
22005
22006 Roo.apply(Roo.bootstrap.DateField,  {
22007   
22008     template : {
22009         tag: 'div',
22010         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22011         cn: [
22012         {
22013             tag: 'div',
22014             cls: 'datepicker-days',
22015             cn: [
22016             {
22017                 tag: 'table',
22018                 cls: 'table-condensed',
22019                 cn:[
22020                 Roo.bootstrap.DateField.head,
22021                 {
22022                     tag: 'tbody'
22023                 },
22024                 Roo.bootstrap.DateField.footer
22025                 ]
22026             }
22027             ]
22028         },
22029         {
22030             tag: 'div',
22031             cls: 'datepicker-months',
22032             cn: [
22033             {
22034                 tag: 'table',
22035                 cls: 'table-condensed',
22036                 cn:[
22037                 Roo.bootstrap.DateField.head,
22038                 Roo.bootstrap.DateField.content,
22039                 Roo.bootstrap.DateField.footer
22040                 ]
22041             }
22042             ]
22043         },
22044         {
22045             tag: 'div',
22046             cls: 'datepicker-years',
22047             cn: [
22048             {
22049                 tag: 'table',
22050                 cls: 'table-condensed',
22051                 cn:[
22052                 Roo.bootstrap.DateField.head,
22053                 Roo.bootstrap.DateField.content,
22054                 Roo.bootstrap.DateField.footer
22055                 ]
22056             }
22057             ]
22058         }
22059         ]
22060     }
22061 });
22062
22063  
22064
22065  /*
22066  * - LGPL
22067  *
22068  * TimeField
22069  * 
22070  */
22071
22072 /**
22073  * @class Roo.bootstrap.TimeField
22074  * @extends Roo.bootstrap.Input
22075  * Bootstrap DateField class
22076  * 
22077  * 
22078  * @constructor
22079  * Create a new TimeField
22080  * @param {Object} config The config object
22081  */
22082
22083 Roo.bootstrap.TimeField = function(config){
22084     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22085     this.addEvents({
22086             /**
22087              * @event show
22088              * Fires when this field show.
22089              * @param {Roo.bootstrap.DateField} thisthis
22090              * @param {Mixed} date The date value
22091              */
22092             show : true,
22093             /**
22094              * @event show
22095              * Fires when this field hide.
22096              * @param {Roo.bootstrap.DateField} this
22097              * @param {Mixed} date The date value
22098              */
22099             hide : true,
22100             /**
22101              * @event select
22102              * Fires when select a date.
22103              * @param {Roo.bootstrap.DateField} this
22104              * @param {Mixed} date The date value
22105              */
22106             select : true
22107         });
22108 };
22109
22110 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22111     
22112     /**
22113      * @cfg {String} format
22114      * The default time format string which can be overriden for localization support.  The format must be
22115      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22116      */
22117     format : "H:i",
22118
22119     getAutoCreate : function()
22120     {
22121         this.after = '<i class="fa far fa-clock"></i>';
22122         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22123         
22124          
22125     },
22126     onRender: function(ct, position)
22127     {
22128         
22129         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22130                 
22131         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22132         
22133         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22134         
22135         this.pop = this.picker().select('>.datepicker-time',true).first();
22136         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22137         
22138         this.picker().on('mousedown', this.onMousedown, this);
22139         this.picker().on('click', this.onClick, this);
22140         
22141         this.picker().addClass('datepicker-dropdown');
22142     
22143         this.fillTime();
22144         this.update();
22145             
22146         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22147         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22148         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22149         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22150         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22151         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22152
22153     },
22154     
22155     fireKey: function(e){
22156         if (!this.picker().isVisible()){
22157             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22158                 this.show();
22159             }
22160             return;
22161         }
22162
22163         e.preventDefault();
22164         
22165         switch(e.keyCode){
22166             case 27: // escape
22167                 this.hide();
22168                 break;
22169             case 37: // left
22170             case 39: // right
22171                 this.onTogglePeriod();
22172                 break;
22173             case 38: // up
22174                 this.onIncrementMinutes();
22175                 break;
22176             case 40: // down
22177                 this.onDecrementMinutes();
22178                 break;
22179             case 13: // enter
22180             case 9: // tab
22181                 this.setTime();
22182                 break;
22183         }
22184     },
22185     
22186     onClick: function(e) {
22187         e.stopPropagation();
22188         e.preventDefault();
22189     },
22190     
22191     picker : function()
22192     {
22193         return this.pickerEl;
22194     },
22195     
22196     fillTime: function()
22197     {    
22198         var time = this.pop.select('tbody', true).first();
22199         
22200         time.dom.innerHTML = '';
22201         
22202         time.createChild({
22203             tag: 'tr',
22204             cn: [
22205                 {
22206                     tag: 'td',
22207                     cn: [
22208                         {
22209                             tag: 'a',
22210                             href: '#',
22211                             cls: 'btn',
22212                             cn: [
22213                                 {
22214                                     tag: 'i',
22215                                     cls: 'hours-up fa fas fa-chevron-up'
22216                                 }
22217                             ]
22218                         } 
22219                     ]
22220                 },
22221                 {
22222                     tag: 'td',
22223                     cls: 'separator'
22224                 },
22225                 {
22226                     tag: 'td',
22227                     cn: [
22228                         {
22229                             tag: 'a',
22230                             href: '#',
22231                             cls: 'btn',
22232                             cn: [
22233                                 {
22234                                     tag: 'i',
22235                                     cls: 'minutes-up fa fas fa-chevron-up'
22236                                 }
22237                             ]
22238                         }
22239                     ]
22240                 },
22241                 {
22242                     tag: 'td',
22243                     cls: 'separator'
22244                 }
22245             ]
22246         });
22247         
22248         time.createChild({
22249             tag: 'tr',
22250             cn: [
22251                 {
22252                     tag: 'td',
22253                     cn: [
22254                         {
22255                             tag: 'span',
22256                             cls: 'timepicker-hour',
22257                             html: '00'
22258                         }  
22259                     ]
22260                 },
22261                 {
22262                     tag: 'td',
22263                     cls: 'separator',
22264                     html: ':'
22265                 },
22266                 {
22267                     tag: 'td',
22268                     cn: [
22269                         {
22270                             tag: 'span',
22271                             cls: 'timepicker-minute',
22272                             html: '00'
22273                         }  
22274                     ]
22275                 },
22276                 {
22277                     tag: 'td',
22278                     cls: 'separator'
22279                 },
22280                 {
22281                     tag: 'td',
22282                     cn: [
22283                         {
22284                             tag: 'button',
22285                             type: 'button',
22286                             cls: 'btn btn-primary period',
22287                             html: 'AM'
22288                             
22289                         }
22290                     ]
22291                 }
22292             ]
22293         });
22294         
22295         time.createChild({
22296             tag: 'tr',
22297             cn: [
22298                 {
22299                     tag: 'td',
22300                     cn: [
22301                         {
22302                             tag: 'a',
22303                             href: '#',
22304                             cls: 'btn',
22305                             cn: [
22306                                 {
22307                                     tag: 'span',
22308                                     cls: 'hours-down fa fas fa-chevron-down'
22309                                 }
22310                             ]
22311                         }
22312                     ]
22313                 },
22314                 {
22315                     tag: 'td',
22316                     cls: 'separator'
22317                 },
22318                 {
22319                     tag: 'td',
22320                     cn: [
22321                         {
22322                             tag: 'a',
22323                             href: '#',
22324                             cls: 'btn',
22325                             cn: [
22326                                 {
22327                                     tag: 'span',
22328                                     cls: 'minutes-down fa fas fa-chevron-down'
22329                                 }
22330                             ]
22331                         }
22332                     ]
22333                 },
22334                 {
22335                     tag: 'td',
22336                     cls: 'separator'
22337                 }
22338             ]
22339         });
22340         
22341     },
22342     
22343     update: function()
22344     {
22345         
22346         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22347         
22348         this.fill();
22349     },
22350     
22351     fill: function() 
22352     {
22353         var hours = this.time.getHours();
22354         var minutes = this.time.getMinutes();
22355         var period = 'AM';
22356         
22357         if(hours > 11){
22358             period = 'PM';
22359         }
22360         
22361         if(hours == 0){
22362             hours = 12;
22363         }
22364         
22365         
22366         if(hours > 12){
22367             hours = hours - 12;
22368         }
22369         
22370         if(hours < 10){
22371             hours = '0' + hours;
22372         }
22373         
22374         if(minutes < 10){
22375             minutes = '0' + minutes;
22376         }
22377         
22378         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22379         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22380         this.pop.select('button', true).first().dom.innerHTML = period;
22381         
22382     },
22383     
22384     place: function()
22385     {   
22386         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22387         
22388         var cls = ['bottom'];
22389         
22390         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22391             cls.pop();
22392             cls.push('top');
22393         }
22394         
22395         cls.push('right');
22396         
22397         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22398             cls.pop();
22399             cls.push('left');
22400         }
22401         //this.picker().setXY(20000,20000);
22402         this.picker().addClass(cls.join('-'));
22403         
22404         var _this = this;
22405         
22406         Roo.each(cls, function(c){
22407             if(c == 'bottom'){
22408                 (function() {
22409                  //  
22410                 }).defer(200);
22411                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22412                 //_this.picker().setTop(_this.inputEl().getHeight());
22413                 return;
22414             }
22415             if(c == 'top'){
22416                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22417                 
22418                 //_this.picker().setTop(0 - _this.picker().getHeight());
22419                 return;
22420             }
22421             /*
22422             if(c == 'left'){
22423                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22424                 return;
22425             }
22426             if(c == 'right'){
22427                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22428                 return;
22429             }
22430             */
22431         });
22432         
22433     },
22434   
22435     onFocus : function()
22436     {
22437         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22438         this.show();
22439     },
22440     
22441     onBlur : function()
22442     {
22443         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22444         this.hide();
22445     },
22446     
22447     show : function()
22448     {
22449         this.picker().show();
22450         this.pop.show();
22451         this.update();
22452         this.place();
22453         
22454         this.fireEvent('show', this, this.date);
22455     },
22456     
22457     hide : function()
22458     {
22459         this.picker().hide();
22460         this.pop.hide();
22461         
22462         this.fireEvent('hide', this, this.date);
22463     },
22464     
22465     setTime : function()
22466     {
22467         this.hide();
22468         this.setValue(this.time.format(this.format));
22469         
22470         this.fireEvent('select', this, this.date);
22471         
22472         
22473     },
22474     
22475     onMousedown: function(e){
22476         e.stopPropagation();
22477         e.preventDefault();
22478     },
22479     
22480     onIncrementHours: function()
22481     {
22482         Roo.log('onIncrementHours');
22483         this.time = this.time.add(Date.HOUR, 1);
22484         this.update();
22485         
22486     },
22487     
22488     onDecrementHours: function()
22489     {
22490         Roo.log('onDecrementHours');
22491         this.time = this.time.add(Date.HOUR, -1);
22492         this.update();
22493     },
22494     
22495     onIncrementMinutes: function()
22496     {
22497         Roo.log('onIncrementMinutes');
22498         this.time = this.time.add(Date.MINUTE, 1);
22499         this.update();
22500     },
22501     
22502     onDecrementMinutes: function()
22503     {
22504         Roo.log('onDecrementMinutes');
22505         this.time = this.time.add(Date.MINUTE, -1);
22506         this.update();
22507     },
22508     
22509     onTogglePeriod: function()
22510     {
22511         Roo.log('onTogglePeriod');
22512         this.time = this.time.add(Date.HOUR, 12);
22513         this.update();
22514     }
22515     
22516    
22517 });
22518  
22519
22520 Roo.apply(Roo.bootstrap.TimeField,  {
22521   
22522     template : {
22523         tag: 'div',
22524         cls: 'datepicker dropdown-menu',
22525         cn: [
22526             {
22527                 tag: 'div',
22528                 cls: 'datepicker-time',
22529                 cn: [
22530                 {
22531                     tag: 'table',
22532                     cls: 'table-condensed',
22533                     cn:[
22534                         {
22535                             tag: 'tbody',
22536                             cn: [
22537                                 {
22538                                     tag: 'tr',
22539                                     cn: [
22540                                     {
22541                                         tag: 'td',
22542                                         colspan: '7'
22543                                     }
22544                                     ]
22545                                 }
22546                             ]
22547                         },
22548                         {
22549                             tag: 'tfoot',
22550                             cn: [
22551                                 {
22552                                     tag: 'tr',
22553                                     cn: [
22554                                     {
22555                                         tag: 'th',
22556                                         colspan: '7',
22557                                         cls: '',
22558                                         cn: [
22559                                             {
22560                                                 tag: 'button',
22561                                                 cls: 'btn btn-info ok',
22562                                                 html: 'OK'
22563                                             }
22564                                         ]
22565                                     }
22566                     
22567                                     ]
22568                                 }
22569                             ]
22570                         }
22571                     ]
22572                 }
22573                 ]
22574             }
22575         ]
22576     }
22577 });
22578
22579  
22580
22581  /*
22582  * - LGPL
22583  *
22584  * MonthField
22585  * 
22586  */
22587
22588 /**
22589  * @class Roo.bootstrap.MonthField
22590  * @extends Roo.bootstrap.Input
22591  * Bootstrap MonthField class
22592  * 
22593  * @cfg {String} language default en
22594  * 
22595  * @constructor
22596  * Create a new MonthField
22597  * @param {Object} config The config object
22598  */
22599
22600 Roo.bootstrap.MonthField = function(config){
22601     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22602     
22603     this.addEvents({
22604         /**
22605          * @event show
22606          * Fires when this field show.
22607          * @param {Roo.bootstrap.MonthField} this
22608          * @param {Mixed} date The date value
22609          */
22610         show : true,
22611         /**
22612          * @event show
22613          * Fires when this field hide.
22614          * @param {Roo.bootstrap.MonthField} this
22615          * @param {Mixed} date The date value
22616          */
22617         hide : true,
22618         /**
22619          * @event select
22620          * Fires when select a date.
22621          * @param {Roo.bootstrap.MonthField} this
22622          * @param {String} oldvalue The old value
22623          * @param {String} newvalue The new value
22624          */
22625         select : true
22626     });
22627 };
22628
22629 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22630     
22631     onRender: function(ct, position)
22632     {
22633         
22634         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22635         
22636         this.language = this.language || 'en';
22637         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22638         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22639         
22640         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22641         this.isInline = false;
22642         this.isInput = true;
22643         this.component = this.el.select('.add-on', true).first() || false;
22644         this.component = (this.component && this.component.length === 0) ? false : this.component;
22645         this.hasInput = this.component && this.inputEL().length;
22646         
22647         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22648         
22649         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22650         
22651         this.picker().on('mousedown', this.onMousedown, this);
22652         this.picker().on('click', this.onClick, this);
22653         
22654         this.picker().addClass('datepicker-dropdown');
22655         
22656         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22657             v.setStyle('width', '189px');
22658         });
22659         
22660         this.fillMonths();
22661         
22662         this.update();
22663         
22664         if(this.isInline) {
22665             this.show();
22666         }
22667         
22668     },
22669     
22670     setValue: function(v, suppressEvent)
22671     {   
22672         var o = this.getValue();
22673         
22674         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22675         
22676         this.update();
22677
22678         if(suppressEvent !== true){
22679             this.fireEvent('select', this, o, v);
22680         }
22681         
22682     },
22683     
22684     getValue: function()
22685     {
22686         return this.value;
22687     },
22688     
22689     onClick: function(e) 
22690     {
22691         e.stopPropagation();
22692         e.preventDefault();
22693         
22694         var target = e.getTarget();
22695         
22696         if(target.nodeName.toLowerCase() === 'i'){
22697             target = Roo.get(target).dom.parentNode;
22698         }
22699         
22700         var nodeName = target.nodeName;
22701         var className = target.className;
22702         var html = target.innerHTML;
22703         
22704         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22705             return;
22706         }
22707         
22708         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22709         
22710         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22711         
22712         this.hide();
22713                         
22714     },
22715     
22716     picker : function()
22717     {
22718         return this.pickerEl;
22719     },
22720     
22721     fillMonths: function()
22722     {    
22723         var i = 0;
22724         var months = this.picker().select('>.datepicker-months td', true).first();
22725         
22726         months.dom.innerHTML = '';
22727         
22728         while (i < 12) {
22729             var month = {
22730                 tag: 'span',
22731                 cls: 'month',
22732                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22733             };
22734             
22735             months.createChild(month);
22736         }
22737         
22738     },
22739     
22740     update: function()
22741     {
22742         var _this = this;
22743         
22744         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22745             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22746         }
22747         
22748         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22749             e.removeClass('active');
22750             
22751             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22752                 e.addClass('active');
22753             }
22754         })
22755     },
22756     
22757     place: function()
22758     {
22759         if(this.isInline) {
22760             return;
22761         }
22762         
22763         this.picker().removeClass(['bottom', 'top']);
22764         
22765         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22766             /*
22767              * place to the top of element!
22768              *
22769              */
22770             
22771             this.picker().addClass('top');
22772             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22773             
22774             return;
22775         }
22776         
22777         this.picker().addClass('bottom');
22778         
22779         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22780     },
22781     
22782     onFocus : function()
22783     {
22784         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22785         this.show();
22786     },
22787     
22788     onBlur : function()
22789     {
22790         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22791         
22792         var d = this.inputEl().getValue();
22793         
22794         this.setValue(d);
22795                 
22796         this.hide();
22797     },
22798     
22799     show : function()
22800     {
22801         this.picker().show();
22802         this.picker().select('>.datepicker-months', true).first().show();
22803         this.update();
22804         this.place();
22805         
22806         this.fireEvent('show', this, this.date);
22807     },
22808     
22809     hide : function()
22810     {
22811         if(this.isInline) {
22812             return;
22813         }
22814         this.picker().hide();
22815         this.fireEvent('hide', this, this.date);
22816         
22817     },
22818     
22819     onMousedown: function(e)
22820     {
22821         e.stopPropagation();
22822         e.preventDefault();
22823     },
22824     
22825     keyup: function(e)
22826     {
22827         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22828         this.update();
22829     },
22830
22831     fireKey: function(e)
22832     {
22833         if (!this.picker().isVisible()){
22834             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22835                 this.show();
22836             }
22837             return;
22838         }
22839         
22840         var dir;
22841         
22842         switch(e.keyCode){
22843             case 27: // escape
22844                 this.hide();
22845                 e.preventDefault();
22846                 break;
22847             case 37: // left
22848             case 39: // right
22849                 dir = e.keyCode == 37 ? -1 : 1;
22850                 
22851                 this.vIndex = this.vIndex + dir;
22852                 
22853                 if(this.vIndex < 0){
22854                     this.vIndex = 0;
22855                 }
22856                 
22857                 if(this.vIndex > 11){
22858                     this.vIndex = 11;
22859                 }
22860                 
22861                 if(isNaN(this.vIndex)){
22862                     this.vIndex = 0;
22863                 }
22864                 
22865                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22866                 
22867                 break;
22868             case 38: // up
22869             case 40: // down
22870                 
22871                 dir = e.keyCode == 38 ? -1 : 1;
22872                 
22873                 this.vIndex = this.vIndex + dir * 4;
22874                 
22875                 if(this.vIndex < 0){
22876                     this.vIndex = 0;
22877                 }
22878                 
22879                 if(this.vIndex > 11){
22880                     this.vIndex = 11;
22881                 }
22882                 
22883                 if(isNaN(this.vIndex)){
22884                     this.vIndex = 0;
22885                 }
22886                 
22887                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22888                 break;
22889                 
22890             case 13: // enter
22891                 
22892                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22893                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22894                 }
22895                 
22896                 this.hide();
22897                 e.preventDefault();
22898                 break;
22899             case 9: // tab
22900                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22901                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22902                 }
22903                 this.hide();
22904                 break;
22905             case 16: // shift
22906             case 17: // ctrl
22907             case 18: // alt
22908                 break;
22909             default :
22910                 this.hide();
22911                 
22912         }
22913     },
22914     
22915     remove: function() 
22916     {
22917         this.picker().remove();
22918     }
22919    
22920 });
22921
22922 Roo.apply(Roo.bootstrap.MonthField,  {
22923     
22924     content : {
22925         tag: 'tbody',
22926         cn: [
22927         {
22928             tag: 'tr',
22929             cn: [
22930             {
22931                 tag: 'td',
22932                 colspan: '7'
22933             }
22934             ]
22935         }
22936         ]
22937     },
22938     
22939     dates:{
22940         en: {
22941             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22942             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22943         }
22944     }
22945 });
22946
22947 Roo.apply(Roo.bootstrap.MonthField,  {
22948   
22949     template : {
22950         tag: 'div',
22951         cls: 'datepicker dropdown-menu roo-dynamic',
22952         cn: [
22953             {
22954                 tag: 'div',
22955                 cls: 'datepicker-months',
22956                 cn: [
22957                 {
22958                     tag: 'table',
22959                     cls: 'table-condensed',
22960                     cn:[
22961                         Roo.bootstrap.DateField.content
22962                     ]
22963                 }
22964                 ]
22965             }
22966         ]
22967     }
22968 });
22969
22970  
22971
22972  
22973  /*
22974  * - LGPL
22975  *
22976  * CheckBox
22977  * 
22978  */
22979
22980 /**
22981  * @class Roo.bootstrap.CheckBox
22982  * @extends Roo.bootstrap.Input
22983  * Bootstrap CheckBox class
22984  * 
22985  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22986  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22987  * @cfg {String} boxLabel The text that appears beside the checkbox
22988  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22989  * @cfg {Boolean} checked initnal the element
22990  * @cfg {Boolean} inline inline the element (default false)
22991  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22992  * @cfg {String} tooltip label tooltip
22993  * 
22994  * @constructor
22995  * Create a new CheckBox
22996  * @param {Object} config The config object
22997  */
22998
22999 Roo.bootstrap.CheckBox = function(config){
23000     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23001    
23002     this.addEvents({
23003         /**
23004         * @event check
23005         * Fires when the element is checked or unchecked.
23006         * @param {Roo.bootstrap.CheckBox} this This input
23007         * @param {Boolean} checked The new checked value
23008         */
23009        check : true,
23010        /**
23011         * @event click
23012         * Fires when the element is click.
23013         * @param {Roo.bootstrap.CheckBox} this This input
23014         */
23015        click : true
23016     });
23017     
23018 };
23019
23020 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23021   
23022     inputType: 'checkbox',
23023     inputValue: 1,
23024     valueOff: 0,
23025     boxLabel: false,
23026     checked: false,
23027     weight : false,
23028     inline: false,
23029     tooltip : '',
23030     
23031     // checkbox success does not make any sense really.. 
23032     invalidClass : "",
23033     validClass : "",
23034     
23035     
23036     getAutoCreate : function()
23037     {
23038         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23039         
23040         var id = Roo.id();
23041         
23042         var cfg = {};
23043         
23044         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23045         
23046         if(this.inline){
23047             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23048         }
23049         
23050         var input =  {
23051             tag: 'input',
23052             id : id,
23053             type : this.inputType,
23054             value : this.inputValue,
23055             cls : 'roo-' + this.inputType, //'form-box',
23056             placeholder : this.placeholder || ''
23057             
23058         };
23059         
23060         if(this.inputType != 'radio'){
23061             var hidden =  {
23062                 tag: 'input',
23063                 type : 'hidden',
23064                 cls : 'roo-hidden-value',
23065                 value : this.checked ? this.inputValue : this.valueOff
23066             };
23067         }
23068         
23069             
23070         if (this.weight) { // Validity check?
23071             cfg.cls += " " + this.inputType + "-" + this.weight;
23072         }
23073         
23074         if (this.disabled) {
23075             input.disabled=true;
23076         }
23077         
23078         if(this.checked){
23079             input.checked = this.checked;
23080         }
23081         
23082         if (this.name) {
23083             
23084             input.name = this.name;
23085             
23086             if(this.inputType != 'radio'){
23087                 hidden.name = this.name;
23088                 input.name = '_hidden_' + this.name;
23089             }
23090         }
23091         
23092         if (this.size) {
23093             input.cls += ' input-' + this.size;
23094         }
23095         
23096         var settings=this;
23097         
23098         ['xs','sm','md','lg'].map(function(size){
23099             if (settings[size]) {
23100                 cfg.cls += ' col-' + size + '-' + settings[size];
23101             }
23102         });
23103         
23104         var inputblock = input;
23105          
23106         if (this.before || this.after) {
23107             
23108             inputblock = {
23109                 cls : 'input-group',
23110                 cn :  [] 
23111             };
23112             
23113             if (this.before) {
23114                 inputblock.cn.push({
23115                     tag :'span',
23116                     cls : 'input-group-addon',
23117                     html : this.before
23118                 });
23119             }
23120             
23121             inputblock.cn.push(input);
23122             
23123             if(this.inputType != 'radio'){
23124                 inputblock.cn.push(hidden);
23125             }
23126             
23127             if (this.after) {
23128                 inputblock.cn.push({
23129                     tag :'span',
23130                     cls : 'input-group-addon',
23131                     html : this.after
23132                 });
23133             }
23134             
23135         }
23136         var boxLabelCfg = false;
23137         
23138         if(this.boxLabel){
23139            
23140             boxLabelCfg = {
23141                 tag: 'label',
23142                 //'for': id, // box label is handled by onclick - so no for...
23143                 cls: 'box-label',
23144                 html: this.boxLabel
23145             };
23146             if(this.tooltip){
23147                 boxLabelCfg.tooltip = this.tooltip;
23148             }
23149              
23150         }
23151         
23152         
23153         if (align ==='left' && this.fieldLabel.length) {
23154 //                Roo.log("left and has label");
23155             cfg.cn = [
23156                 {
23157                     tag: 'label',
23158                     'for' :  id,
23159                     cls : 'control-label',
23160                     html : this.fieldLabel
23161                 },
23162                 {
23163                     cls : "", 
23164                     cn: [
23165                         inputblock
23166                     ]
23167                 }
23168             ];
23169             
23170             if (boxLabelCfg) {
23171                 cfg.cn[1].cn.push(boxLabelCfg);
23172             }
23173             
23174             if(this.labelWidth > 12){
23175                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23176             }
23177             
23178             if(this.labelWidth < 13 && this.labelmd == 0){
23179                 this.labelmd = this.labelWidth;
23180             }
23181             
23182             if(this.labellg > 0){
23183                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23184                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23185             }
23186             
23187             if(this.labelmd > 0){
23188                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23189                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23190             }
23191             
23192             if(this.labelsm > 0){
23193                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23194                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23195             }
23196             
23197             if(this.labelxs > 0){
23198                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23199                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23200             }
23201             
23202         } else if ( this.fieldLabel.length) {
23203 //                Roo.log(" label");
23204                 cfg.cn = [
23205                    
23206                     {
23207                         tag: this.boxLabel ? 'span' : 'label',
23208                         'for': id,
23209                         cls: 'control-label box-input-label',
23210                         //cls : 'input-group-addon',
23211                         html : this.fieldLabel
23212                     },
23213                     
23214                     inputblock
23215                     
23216                 ];
23217                 if (boxLabelCfg) {
23218                     cfg.cn.push(boxLabelCfg);
23219                 }
23220
23221         } else {
23222             
23223 //                Roo.log(" no label && no align");
23224                 cfg.cn = [  inputblock ] ;
23225                 if (boxLabelCfg) {
23226                     cfg.cn.push(boxLabelCfg);
23227                 }
23228
23229                 
23230         }
23231         
23232        
23233         
23234         if(this.inputType != 'radio'){
23235             cfg.cn.push(hidden);
23236         }
23237         
23238         return cfg;
23239         
23240     },
23241     
23242     /**
23243      * return the real input element.
23244      */
23245     inputEl: function ()
23246     {
23247         return this.el.select('input.roo-' + this.inputType,true).first();
23248     },
23249     hiddenEl: function ()
23250     {
23251         return this.el.select('input.roo-hidden-value',true).first();
23252     },
23253     
23254     labelEl: function()
23255     {
23256         return this.el.select('label.control-label',true).first();
23257     },
23258     /* depricated... */
23259     
23260     label: function()
23261     {
23262         return this.labelEl();
23263     },
23264     
23265     boxLabelEl: function()
23266     {
23267         return this.el.select('label.box-label',true).first();
23268     },
23269     
23270     initEvents : function()
23271     {
23272 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23273         
23274         this.inputEl().on('click', this.onClick,  this);
23275         
23276         if (this.boxLabel) { 
23277             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23278         }
23279         
23280         this.startValue = this.getValue();
23281         
23282         if(this.groupId){
23283             Roo.bootstrap.CheckBox.register(this);
23284         }
23285     },
23286     
23287     onClick : function(e)
23288     {   
23289         if(this.fireEvent('click', this, e) !== false){
23290             this.setChecked(!this.checked);
23291         }
23292         
23293     },
23294     
23295     setChecked : function(state,suppressEvent)
23296     {
23297         this.startValue = this.getValue();
23298
23299         if(this.inputType == 'radio'){
23300             
23301             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23302                 e.dom.checked = false;
23303             });
23304             
23305             this.inputEl().dom.checked = true;
23306             
23307             this.inputEl().dom.value = this.inputValue;
23308             
23309             if(suppressEvent !== true){
23310                 this.fireEvent('check', this, true);
23311             }
23312             
23313             this.validate();
23314             
23315             return;
23316         }
23317         
23318         this.checked = state;
23319         
23320         this.inputEl().dom.checked = state;
23321         
23322         
23323         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23324         
23325         if(suppressEvent !== true){
23326             this.fireEvent('check', this, state);
23327         }
23328         
23329         this.validate();
23330     },
23331     
23332     getValue : function()
23333     {
23334         if(this.inputType == 'radio'){
23335             return this.getGroupValue();
23336         }
23337         
23338         return this.hiddenEl().dom.value;
23339         
23340     },
23341     
23342     getGroupValue : function()
23343     {
23344         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23345             return '';
23346         }
23347         
23348         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23349     },
23350     
23351     setValue : function(v,suppressEvent)
23352     {
23353         if(this.inputType == 'radio'){
23354             this.setGroupValue(v, suppressEvent);
23355             return;
23356         }
23357         
23358         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23359         
23360         this.validate();
23361     },
23362     
23363     setGroupValue : function(v, suppressEvent)
23364     {
23365         this.startValue = this.getValue();
23366         
23367         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23368             e.dom.checked = false;
23369             
23370             if(e.dom.value == v){
23371                 e.dom.checked = true;
23372             }
23373         });
23374         
23375         if(suppressEvent !== true){
23376             this.fireEvent('check', this, true);
23377         }
23378
23379         this.validate();
23380         
23381         return;
23382     },
23383     
23384     validate : function()
23385     {
23386         if(this.getVisibilityEl().hasClass('hidden')){
23387             return true;
23388         }
23389         
23390         if(
23391                 this.disabled || 
23392                 (this.inputType == 'radio' && this.validateRadio()) ||
23393                 (this.inputType == 'checkbox' && this.validateCheckbox())
23394         ){
23395             this.markValid();
23396             return true;
23397         }
23398         
23399         this.markInvalid();
23400         return false;
23401     },
23402     
23403     validateRadio : function()
23404     {
23405         if(this.getVisibilityEl().hasClass('hidden')){
23406             return true;
23407         }
23408         
23409         if(this.allowBlank){
23410             return true;
23411         }
23412         
23413         var valid = false;
23414         
23415         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23416             if(!e.dom.checked){
23417                 return;
23418             }
23419             
23420             valid = true;
23421             
23422             return false;
23423         });
23424         
23425         return valid;
23426     },
23427     
23428     validateCheckbox : function()
23429     {
23430         if(!this.groupId){
23431             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23432             //return (this.getValue() == this.inputValue) ? true : false;
23433         }
23434         
23435         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23436         
23437         if(!group){
23438             return false;
23439         }
23440         
23441         var r = false;
23442         
23443         for(var i in group){
23444             if(group[i].el.isVisible(true)){
23445                 r = false;
23446                 break;
23447             }
23448             
23449             r = true;
23450         }
23451         
23452         for(var i in group){
23453             if(r){
23454                 break;
23455             }
23456             
23457             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23458         }
23459         
23460         return r;
23461     },
23462     
23463     /**
23464      * Mark this field as valid
23465      */
23466     markValid : function()
23467     {
23468         var _this = this;
23469         
23470         this.fireEvent('valid', this);
23471         
23472         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23473         
23474         if(this.groupId){
23475             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23476         }
23477         
23478         if(label){
23479             label.markValid();
23480         }
23481
23482         if(this.inputType == 'radio'){
23483             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23484                 var fg = e.findParent('.form-group', false, true);
23485                 if (Roo.bootstrap.version == 3) {
23486                     fg.removeClass([_this.invalidClass, _this.validClass]);
23487                     fg.addClass(_this.validClass);
23488                 } else {
23489                     fg.removeClass(['is-valid', 'is-invalid']);
23490                     fg.addClass('is-valid');
23491                 }
23492             });
23493             
23494             return;
23495         }
23496
23497         if(!this.groupId){
23498             var fg = this.el.findParent('.form-group', false, true);
23499             if (Roo.bootstrap.version == 3) {
23500                 fg.removeClass([this.invalidClass, this.validClass]);
23501                 fg.addClass(this.validClass);
23502             } else {
23503                 fg.removeClass(['is-valid', 'is-invalid']);
23504                 fg.addClass('is-valid');
23505             }
23506             return;
23507         }
23508         
23509         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23510         
23511         if(!group){
23512             return;
23513         }
23514         
23515         for(var i in group){
23516             var fg = group[i].el.findParent('.form-group', false, true);
23517             if (Roo.bootstrap.version == 3) {
23518                 fg.removeClass([this.invalidClass, this.validClass]);
23519                 fg.addClass(this.validClass);
23520             } else {
23521                 fg.removeClass(['is-valid', 'is-invalid']);
23522                 fg.addClass('is-valid');
23523             }
23524         }
23525     },
23526     
23527      /**
23528      * Mark this field as invalid
23529      * @param {String} msg The validation message
23530      */
23531     markInvalid : function(msg)
23532     {
23533         if(this.allowBlank){
23534             return;
23535         }
23536         
23537         var _this = this;
23538         
23539         this.fireEvent('invalid', this, msg);
23540         
23541         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23542         
23543         if(this.groupId){
23544             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23545         }
23546         
23547         if(label){
23548             label.markInvalid();
23549         }
23550             
23551         if(this.inputType == 'radio'){
23552             
23553             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23554                 var fg = e.findParent('.form-group', false, true);
23555                 if (Roo.bootstrap.version == 3) {
23556                     fg.removeClass([_this.invalidClass, _this.validClass]);
23557                     fg.addClass(_this.invalidClass);
23558                 } else {
23559                     fg.removeClass(['is-invalid', 'is-valid']);
23560                     fg.addClass('is-invalid');
23561                 }
23562             });
23563             
23564             return;
23565         }
23566         
23567         if(!this.groupId){
23568             var fg = this.el.findParent('.form-group', false, true);
23569             if (Roo.bootstrap.version == 3) {
23570                 fg.removeClass([_this.invalidClass, _this.validClass]);
23571                 fg.addClass(_this.invalidClass);
23572             } else {
23573                 fg.removeClass(['is-invalid', 'is-valid']);
23574                 fg.addClass('is-invalid');
23575             }
23576             return;
23577         }
23578         
23579         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23580         
23581         if(!group){
23582             return;
23583         }
23584         
23585         for(var i in group){
23586             var fg = group[i].el.findParent('.form-group', false, true);
23587             if (Roo.bootstrap.version == 3) {
23588                 fg.removeClass([_this.invalidClass, _this.validClass]);
23589                 fg.addClass(_this.invalidClass);
23590             } else {
23591                 fg.removeClass(['is-invalid', 'is-valid']);
23592                 fg.addClass('is-invalid');
23593             }
23594         }
23595         
23596     },
23597     
23598     clearInvalid : function()
23599     {
23600         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23601         
23602         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23603         
23604         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23605         
23606         if (label && label.iconEl) {
23607             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23608             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23609         }
23610     },
23611     
23612     disable : function()
23613     {
23614         if(this.inputType != 'radio'){
23615             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23616             return;
23617         }
23618         
23619         var _this = this;
23620         
23621         if(this.rendered){
23622             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23623                 _this.getActionEl().addClass(this.disabledClass);
23624                 e.dom.disabled = true;
23625             });
23626         }
23627         
23628         this.disabled = true;
23629         this.fireEvent("disable", this);
23630         return this;
23631     },
23632
23633     enable : function()
23634     {
23635         if(this.inputType != 'radio'){
23636             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23637             return;
23638         }
23639         
23640         var _this = this;
23641         
23642         if(this.rendered){
23643             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23644                 _this.getActionEl().removeClass(this.disabledClass);
23645                 e.dom.disabled = false;
23646             });
23647         }
23648         
23649         this.disabled = false;
23650         this.fireEvent("enable", this);
23651         return this;
23652     },
23653     
23654     setBoxLabel : function(v)
23655     {
23656         this.boxLabel = v;
23657         
23658         if(this.rendered){
23659             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23660         }
23661     }
23662
23663 });
23664
23665 Roo.apply(Roo.bootstrap.CheckBox, {
23666     
23667     groups: {},
23668     
23669      /**
23670     * register a CheckBox Group
23671     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23672     */
23673     register : function(checkbox)
23674     {
23675         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23676             this.groups[checkbox.groupId] = {};
23677         }
23678         
23679         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23680             return;
23681         }
23682         
23683         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23684         
23685     },
23686     /**
23687     * fetch a CheckBox Group based on the group ID
23688     * @param {string} the group ID
23689     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23690     */
23691     get: function(groupId) {
23692         if (typeof(this.groups[groupId]) == 'undefined') {
23693             return false;
23694         }
23695         
23696         return this.groups[groupId] ;
23697     }
23698     
23699     
23700 });
23701 /*
23702  * - LGPL
23703  *
23704  * RadioItem
23705  * 
23706  */
23707
23708 /**
23709  * @class Roo.bootstrap.Radio
23710  * @extends Roo.bootstrap.Component
23711  * Bootstrap Radio class
23712  * @cfg {String} boxLabel - the label associated
23713  * @cfg {String} value - the value of radio
23714  * 
23715  * @constructor
23716  * Create a new Radio
23717  * @param {Object} config The config object
23718  */
23719 Roo.bootstrap.Radio = function(config){
23720     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23721     
23722 };
23723
23724 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23725     
23726     boxLabel : '',
23727     
23728     value : '',
23729     
23730     getAutoCreate : function()
23731     {
23732         var cfg = {
23733             tag : 'div',
23734             cls : 'form-group radio',
23735             cn : [
23736                 {
23737                     tag : 'label',
23738                     cls : 'box-label',
23739                     html : this.boxLabel
23740                 }
23741             ]
23742         };
23743         
23744         return cfg;
23745     },
23746     
23747     initEvents : function() 
23748     {
23749         this.parent().register(this);
23750         
23751         this.el.on('click', this.onClick, this);
23752         
23753     },
23754     
23755     onClick : function(e)
23756     {
23757         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23758             this.setChecked(true);
23759         }
23760     },
23761     
23762     setChecked : function(state, suppressEvent)
23763     {
23764         this.parent().setValue(this.value, suppressEvent);
23765         
23766     },
23767     
23768     setBoxLabel : function(v)
23769     {
23770         this.boxLabel = v;
23771         
23772         if(this.rendered){
23773             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23774         }
23775     }
23776     
23777 });
23778  
23779
23780  /*
23781  * - LGPL
23782  *
23783  * Input
23784  * 
23785  */
23786
23787 /**
23788  * @class Roo.bootstrap.SecurePass
23789  * @extends Roo.bootstrap.Input
23790  * Bootstrap SecurePass class
23791  *
23792  * 
23793  * @constructor
23794  * Create a new SecurePass
23795  * @param {Object} config The config object
23796  */
23797  
23798 Roo.bootstrap.SecurePass = function (config) {
23799     // these go here, so the translation tool can replace them..
23800     this.errors = {
23801         PwdEmpty: "Please type a password, and then retype it to confirm.",
23802         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23803         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23804         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23805         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23806         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23807         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23808         TooWeak: "Your password is Too Weak."
23809     },
23810     this.meterLabel = "Password strength:";
23811     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23812     this.meterClass = [
23813         "roo-password-meter-tooweak", 
23814         "roo-password-meter-weak", 
23815         "roo-password-meter-medium", 
23816         "roo-password-meter-strong", 
23817         "roo-password-meter-grey"
23818     ];
23819     
23820     this.errors = {};
23821     
23822     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23823 }
23824
23825 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23826     /**
23827      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23828      * {
23829      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23830      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23831      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23832      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23833      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23834      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23835      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23836      * })
23837      */
23838     // private
23839     
23840     meterWidth: 300,
23841     errorMsg :'',    
23842     errors: false,
23843     imageRoot: '/',
23844     /**
23845      * @cfg {String/Object} Label for the strength meter (defaults to
23846      * 'Password strength:')
23847      */
23848     // private
23849     meterLabel: '',
23850     /**
23851      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23852      * ['Weak', 'Medium', 'Strong'])
23853      */
23854     // private    
23855     pwdStrengths: false,    
23856     // private
23857     strength: 0,
23858     // private
23859     _lastPwd: null,
23860     // private
23861     kCapitalLetter: 0,
23862     kSmallLetter: 1,
23863     kDigit: 2,
23864     kPunctuation: 3,
23865     
23866     insecure: false,
23867     // private
23868     initEvents: function ()
23869     {
23870         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23871
23872         if (this.el.is('input[type=password]') && Roo.isSafari) {
23873             this.el.on('keydown', this.SafariOnKeyDown, this);
23874         }
23875
23876         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23877     },
23878     // private
23879     onRender: function (ct, position)
23880     {
23881         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23882         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23883         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23884
23885         this.trigger.createChild({
23886                    cn: [
23887                     {
23888                     //id: 'PwdMeter',
23889                     tag: 'div',
23890                     cls: 'roo-password-meter-grey col-xs-12',
23891                     style: {
23892                         //width: 0,
23893                         //width: this.meterWidth + 'px'                                                
23894                         }
23895                     },
23896                     {                            
23897                          cls: 'roo-password-meter-text'                          
23898                     }
23899                 ]            
23900         });
23901
23902          
23903         if (this.hideTrigger) {
23904             this.trigger.setDisplayed(false);
23905         }
23906         this.setSize(this.width || '', this.height || '');
23907     },
23908     // private
23909     onDestroy: function ()
23910     {
23911         if (this.trigger) {
23912             this.trigger.removeAllListeners();
23913             this.trigger.remove();
23914         }
23915         if (this.wrap) {
23916             this.wrap.remove();
23917         }
23918         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23919     },
23920     // private
23921     checkStrength: function ()
23922     {
23923         var pwd = this.inputEl().getValue();
23924         if (pwd == this._lastPwd) {
23925             return;
23926         }
23927
23928         var strength;
23929         if (this.ClientSideStrongPassword(pwd)) {
23930             strength = 3;
23931         } else if (this.ClientSideMediumPassword(pwd)) {
23932             strength = 2;
23933         } else if (this.ClientSideWeakPassword(pwd)) {
23934             strength = 1;
23935         } else {
23936             strength = 0;
23937         }
23938         
23939         Roo.log('strength1: ' + strength);
23940         
23941         //var pm = this.trigger.child('div/div/div').dom;
23942         var pm = this.trigger.child('div/div');
23943         pm.removeClass(this.meterClass);
23944         pm.addClass(this.meterClass[strength]);
23945                 
23946         
23947         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23948                 
23949         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23950         
23951         this._lastPwd = pwd;
23952     },
23953     reset: function ()
23954     {
23955         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23956         
23957         this._lastPwd = '';
23958         
23959         var pm = this.trigger.child('div/div');
23960         pm.removeClass(this.meterClass);
23961         pm.addClass('roo-password-meter-grey');        
23962         
23963         
23964         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23965         
23966         pt.innerHTML = '';
23967         this.inputEl().dom.type='password';
23968     },
23969     // private
23970     validateValue: function (value)
23971     {
23972         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23973             return false;
23974         }
23975         if (value.length == 0) {
23976             if (this.allowBlank) {
23977                 this.clearInvalid();
23978                 return true;
23979             }
23980
23981             this.markInvalid(this.errors.PwdEmpty);
23982             this.errorMsg = this.errors.PwdEmpty;
23983             return false;
23984         }
23985         
23986         if(this.insecure){
23987             return true;
23988         }
23989         
23990         if (!value.match(/[\x21-\x7e]+/)) {
23991             this.markInvalid(this.errors.PwdBadChar);
23992             this.errorMsg = this.errors.PwdBadChar;
23993             return false;
23994         }
23995         if (value.length < 6) {
23996             this.markInvalid(this.errors.PwdShort);
23997             this.errorMsg = this.errors.PwdShort;
23998             return false;
23999         }
24000         if (value.length > 16) {
24001             this.markInvalid(this.errors.PwdLong);
24002             this.errorMsg = this.errors.PwdLong;
24003             return false;
24004         }
24005         var strength;
24006         if (this.ClientSideStrongPassword(value)) {
24007             strength = 3;
24008         } else if (this.ClientSideMediumPassword(value)) {
24009             strength = 2;
24010         } else if (this.ClientSideWeakPassword(value)) {
24011             strength = 1;
24012         } else {
24013             strength = 0;
24014         }
24015
24016         
24017         if (strength < 2) {
24018             //this.markInvalid(this.errors.TooWeak);
24019             this.errorMsg = this.errors.TooWeak;
24020             //return false;
24021         }
24022         
24023         
24024         console.log('strength2: ' + strength);
24025         
24026         //var pm = this.trigger.child('div/div/div').dom;
24027         
24028         var pm = this.trigger.child('div/div');
24029         pm.removeClass(this.meterClass);
24030         pm.addClass(this.meterClass[strength]);
24031                 
24032         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24033                 
24034         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24035         
24036         this.errorMsg = ''; 
24037         return true;
24038     },
24039     // private
24040     CharacterSetChecks: function (type)
24041     {
24042         this.type = type;
24043         this.fResult = false;
24044     },
24045     // private
24046     isctype: function (character, type)
24047     {
24048         switch (type) {  
24049             case this.kCapitalLetter:
24050                 if (character >= 'A' && character <= 'Z') {
24051                     return true;
24052                 }
24053                 break;
24054             
24055             case this.kSmallLetter:
24056                 if (character >= 'a' && character <= 'z') {
24057                     return true;
24058                 }
24059                 break;
24060             
24061             case this.kDigit:
24062                 if (character >= '0' && character <= '9') {
24063                     return true;
24064                 }
24065                 break;
24066             
24067             case this.kPunctuation:
24068                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24069                     return true;
24070                 }
24071                 break;
24072             
24073             default:
24074                 return false;
24075         }
24076
24077     },
24078     // private
24079     IsLongEnough: function (pwd, size)
24080     {
24081         return !(pwd == null || isNaN(size) || pwd.length < size);
24082     },
24083     // private
24084     SpansEnoughCharacterSets: function (word, nb)
24085     {
24086         if (!this.IsLongEnough(word, nb))
24087         {
24088             return false;
24089         }
24090
24091         var characterSetChecks = new Array(
24092             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24093             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24094         );
24095         
24096         for (var index = 0; index < word.length; ++index) {
24097             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24098                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24099                     characterSetChecks[nCharSet].fResult = true;
24100                     break;
24101                 }
24102             }
24103         }
24104
24105         var nCharSets = 0;
24106         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24107             if (characterSetChecks[nCharSet].fResult) {
24108                 ++nCharSets;
24109             }
24110         }
24111
24112         if (nCharSets < nb) {
24113             return false;
24114         }
24115         return true;
24116     },
24117     // private
24118     ClientSideStrongPassword: function (pwd)
24119     {
24120         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24121     },
24122     // private
24123     ClientSideMediumPassword: function (pwd)
24124     {
24125         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24126     },
24127     // private
24128     ClientSideWeakPassword: function (pwd)
24129     {
24130         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24131     }
24132           
24133 })//<script type="text/javascript">
24134
24135 /*
24136  * Based  Ext JS Library 1.1.1
24137  * Copyright(c) 2006-2007, Ext JS, LLC.
24138  * LGPL
24139  *
24140  */
24141  
24142 /**
24143  * @class Roo.HtmlEditorCore
24144  * @extends Roo.Component
24145  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24146  *
24147  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24148  */
24149
24150 Roo.HtmlEditorCore = function(config){
24151     
24152     
24153     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24154     
24155     
24156     this.addEvents({
24157         /**
24158          * @event initialize
24159          * Fires when the editor is fully initialized (including the iframe)
24160          * @param {Roo.HtmlEditorCore} this
24161          */
24162         initialize: true,
24163         /**
24164          * @event activate
24165          * Fires when the editor is first receives the focus. Any insertion must wait
24166          * until after this event.
24167          * @param {Roo.HtmlEditorCore} this
24168          */
24169         activate: true,
24170          /**
24171          * @event beforesync
24172          * Fires before the textarea is updated with content from the editor iframe. Return false
24173          * to cancel the sync.
24174          * @param {Roo.HtmlEditorCore} this
24175          * @param {String} html
24176          */
24177         beforesync: true,
24178          /**
24179          * @event beforepush
24180          * Fires before the iframe editor is updated with content from the textarea. Return false
24181          * to cancel the push.
24182          * @param {Roo.HtmlEditorCore} this
24183          * @param {String} html
24184          */
24185         beforepush: true,
24186          /**
24187          * @event sync
24188          * Fires when the textarea is updated with content from the editor iframe.
24189          * @param {Roo.HtmlEditorCore} this
24190          * @param {String} html
24191          */
24192         sync: true,
24193          /**
24194          * @event push
24195          * Fires when the iframe editor is updated with content from the textarea.
24196          * @param {Roo.HtmlEditorCore} this
24197          * @param {String} html
24198          */
24199         push: true,
24200         
24201         /**
24202          * @event editorevent
24203          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24204          * @param {Roo.HtmlEditorCore} this
24205          */
24206         editorevent: true
24207         
24208     });
24209     
24210     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24211     
24212     // defaults : white / black...
24213     this.applyBlacklists();
24214     
24215     
24216     
24217 };
24218
24219
24220 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24221
24222
24223      /**
24224      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24225      */
24226     
24227     owner : false,
24228     
24229      /**
24230      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24231      *                        Roo.resizable.
24232      */
24233     resizable : false,
24234      /**
24235      * @cfg {Number} height (in pixels)
24236      */   
24237     height: 300,
24238    /**
24239      * @cfg {Number} width (in pixels)
24240      */   
24241     width: 500,
24242     
24243     /**
24244      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24245      * 
24246      */
24247     stylesheets: false,
24248     
24249     // id of frame..
24250     frameId: false,
24251     
24252     // private properties
24253     validationEvent : false,
24254     deferHeight: true,
24255     initialized : false,
24256     activated : false,
24257     sourceEditMode : false,
24258     onFocus : Roo.emptyFn,
24259     iframePad:3,
24260     hideMode:'offsets',
24261     
24262     clearUp: true,
24263     
24264     // blacklist + whitelisted elements..
24265     black: false,
24266     white: false,
24267      
24268     bodyCls : '',
24269
24270     /**
24271      * Protected method that will not generally be called directly. It
24272      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24273      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24274      */
24275     getDocMarkup : function(){
24276         // body styles..
24277         var st = '';
24278         
24279         // inherit styels from page...?? 
24280         if (this.stylesheets === false) {
24281             
24282             Roo.get(document.head).select('style').each(function(node) {
24283                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24284             });
24285             
24286             Roo.get(document.head).select('link').each(function(node) { 
24287                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24288             });
24289             
24290         } else if (!this.stylesheets.length) {
24291                 // simple..
24292                 st = '<style type="text/css">' +
24293                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24294                    '</style>';
24295         } else {
24296             for (var i in this.stylesheets) { 
24297                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24298             }
24299             
24300         }
24301         
24302         st +=  '<style type="text/css">' +
24303             'IMG { cursor: pointer } ' +
24304         '</style>';
24305
24306         var cls = 'roo-htmleditor-body';
24307         
24308         if(this.bodyCls.length){
24309             cls += ' ' + this.bodyCls;
24310         }
24311         
24312         return '<html><head>' + st  +
24313             //<style type="text/css">' +
24314             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24315             //'</style>' +
24316             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24317     },
24318
24319     // private
24320     onRender : function(ct, position)
24321     {
24322         var _t = this;
24323         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24324         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24325         
24326         
24327         this.el.dom.style.border = '0 none';
24328         this.el.dom.setAttribute('tabIndex', -1);
24329         this.el.addClass('x-hidden hide');
24330         
24331         
24332         
24333         if(Roo.isIE){ // fix IE 1px bogus margin
24334             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24335         }
24336        
24337         
24338         this.frameId = Roo.id();
24339         
24340          
24341         
24342         var iframe = this.owner.wrap.createChild({
24343             tag: 'iframe',
24344             cls: 'form-control', // bootstrap..
24345             id: this.frameId,
24346             name: this.frameId,
24347             frameBorder : 'no',
24348             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24349         }, this.el
24350         );
24351         
24352         
24353         this.iframe = iframe.dom;
24354
24355          this.assignDocWin();
24356         
24357         this.doc.designMode = 'on';
24358        
24359         this.doc.open();
24360         this.doc.write(this.getDocMarkup());
24361         this.doc.close();
24362
24363         
24364         var task = { // must defer to wait for browser to be ready
24365             run : function(){
24366                 //console.log("run task?" + this.doc.readyState);
24367                 this.assignDocWin();
24368                 if(this.doc.body || this.doc.readyState == 'complete'){
24369                     try {
24370                         this.doc.designMode="on";
24371                     } catch (e) {
24372                         return;
24373                     }
24374                     Roo.TaskMgr.stop(task);
24375                     this.initEditor.defer(10, this);
24376                 }
24377             },
24378             interval : 10,
24379             duration: 10000,
24380             scope: this
24381         };
24382         Roo.TaskMgr.start(task);
24383
24384     },
24385
24386     // private
24387     onResize : function(w, h)
24388     {
24389          Roo.log('resize: ' +w + ',' + h );
24390         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24391         if(!this.iframe){
24392             return;
24393         }
24394         if(typeof w == 'number'){
24395             
24396             this.iframe.style.width = w + 'px';
24397         }
24398         if(typeof h == 'number'){
24399             
24400             this.iframe.style.height = h + 'px';
24401             if(this.doc){
24402                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24403             }
24404         }
24405         
24406     },
24407
24408     /**
24409      * Toggles the editor between standard and source edit mode.
24410      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24411      */
24412     toggleSourceEdit : function(sourceEditMode){
24413         
24414         this.sourceEditMode = sourceEditMode === true;
24415         
24416         if(this.sourceEditMode){
24417  
24418             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24419             
24420         }else{
24421             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24422             //this.iframe.className = '';
24423             this.deferFocus();
24424         }
24425         //this.setSize(this.owner.wrap.getSize());
24426         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24427     },
24428
24429     
24430   
24431
24432     /**
24433      * Protected method that will not generally be called directly. If you need/want
24434      * custom HTML cleanup, this is the method you should override.
24435      * @param {String} html The HTML to be cleaned
24436      * return {String} The cleaned HTML
24437      */
24438     cleanHtml : function(html){
24439         html = String(html);
24440         if(html.length > 5){
24441             if(Roo.isSafari){ // strip safari nonsense
24442                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24443             }
24444         }
24445         if(html == '&nbsp;'){
24446             html = '';
24447         }
24448         return html;
24449     },
24450
24451     /**
24452      * HTML Editor -> Textarea
24453      * Protected method that will not generally be called directly. Syncs the contents
24454      * of the editor iframe with the textarea.
24455      */
24456     syncValue : function(){
24457         if(this.initialized){
24458             var bd = (this.doc.body || this.doc.documentElement);
24459             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24460             var html = bd.innerHTML;
24461             if(Roo.isSafari){
24462                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24463                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24464                 if(m && m[1]){
24465                     html = '<div style="'+m[0]+'">' + html + '</div>';
24466                 }
24467             }
24468             html = this.cleanHtml(html);
24469             // fix up the special chars.. normaly like back quotes in word...
24470             // however we do not want to do this with chinese..
24471             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24472                 
24473                 var cc = match.charCodeAt();
24474
24475                 // Get the character value, handling surrogate pairs
24476                 if (match.length == 2) {
24477                     // It's a surrogate pair, calculate the Unicode code point
24478                     var high = match.charCodeAt(0) - 0xD800;
24479                     var low  = match.charCodeAt(1) - 0xDC00;
24480                     cc = (high * 0x400) + low + 0x10000;
24481                 }  else if (
24482                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24483                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24484                     (cc >= 0xf900 && cc < 0xfb00 )
24485                 ) {
24486                         return match;
24487                 }  
24488          
24489                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24490                 return "&#" + cc + ";";
24491                 
24492                 
24493             });
24494             
24495             
24496              
24497             if(this.owner.fireEvent('beforesync', this, html) !== false){
24498                 this.el.dom.value = html;
24499                 this.owner.fireEvent('sync', this, html);
24500             }
24501         }
24502     },
24503
24504     /**
24505      * Protected method that will not generally be called directly. Pushes the value of the textarea
24506      * into the iframe editor.
24507      */
24508     pushValue : function(){
24509         if(this.initialized){
24510             var v = this.el.dom.value.trim();
24511             
24512 //            if(v.length < 1){
24513 //                v = '&#160;';
24514 //            }
24515             
24516             if(this.owner.fireEvent('beforepush', this, v) !== false){
24517                 var d = (this.doc.body || this.doc.documentElement);
24518                 d.innerHTML = v;
24519                 this.cleanUpPaste();
24520                 this.el.dom.value = d.innerHTML;
24521                 this.owner.fireEvent('push', this, v);
24522             }
24523         }
24524     },
24525
24526     // private
24527     deferFocus : function(){
24528         this.focus.defer(10, this);
24529     },
24530
24531     // doc'ed in Field
24532     focus : function(){
24533         if(this.win && !this.sourceEditMode){
24534             this.win.focus();
24535         }else{
24536             this.el.focus();
24537         }
24538     },
24539     
24540     assignDocWin: function()
24541     {
24542         var iframe = this.iframe;
24543         
24544          if(Roo.isIE){
24545             this.doc = iframe.contentWindow.document;
24546             this.win = iframe.contentWindow;
24547         } else {
24548 //            if (!Roo.get(this.frameId)) {
24549 //                return;
24550 //            }
24551 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24552 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24553             
24554             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24555                 return;
24556             }
24557             
24558             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24559             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24560         }
24561     },
24562     
24563     // private
24564     initEditor : function(){
24565         //console.log("INIT EDITOR");
24566         this.assignDocWin();
24567         
24568         
24569         
24570         this.doc.designMode="on";
24571         this.doc.open();
24572         this.doc.write(this.getDocMarkup());
24573         this.doc.close();
24574         
24575         var dbody = (this.doc.body || this.doc.documentElement);
24576         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24577         // this copies styles from the containing element into thsi one..
24578         // not sure why we need all of this..
24579         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24580         
24581         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24582         //ss['background-attachment'] = 'fixed'; // w3c
24583         dbody.bgProperties = 'fixed'; // ie
24584         //Roo.DomHelper.applyStyles(dbody, ss);
24585         Roo.EventManager.on(this.doc, {
24586             //'mousedown': this.onEditorEvent,
24587             'mouseup': this.onEditorEvent,
24588             'dblclick': this.onEditorEvent,
24589             'click': this.onEditorEvent,
24590             'keyup': this.onEditorEvent,
24591             buffer:100,
24592             scope: this
24593         });
24594         if(Roo.isGecko){
24595             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24596         }
24597         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24598             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24599         }
24600         this.initialized = true;
24601
24602         this.owner.fireEvent('initialize', this);
24603         this.pushValue();
24604     },
24605
24606     // private
24607     onDestroy : function(){
24608         
24609         
24610         
24611         if(this.rendered){
24612             
24613             //for (var i =0; i < this.toolbars.length;i++) {
24614             //    // fixme - ask toolbars for heights?
24615             //    this.toolbars[i].onDestroy();
24616            // }
24617             
24618             //this.wrap.dom.innerHTML = '';
24619             //this.wrap.remove();
24620         }
24621     },
24622
24623     // private
24624     onFirstFocus : function(){
24625         
24626         this.assignDocWin();
24627         
24628         
24629         this.activated = true;
24630          
24631     
24632         if(Roo.isGecko){ // prevent silly gecko errors
24633             this.win.focus();
24634             var s = this.win.getSelection();
24635             if(!s.focusNode || s.focusNode.nodeType != 3){
24636                 var r = s.getRangeAt(0);
24637                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24638                 r.collapse(true);
24639                 this.deferFocus();
24640             }
24641             try{
24642                 this.execCmd('useCSS', true);
24643                 this.execCmd('styleWithCSS', false);
24644             }catch(e){}
24645         }
24646         this.owner.fireEvent('activate', this);
24647     },
24648
24649     // private
24650     adjustFont: function(btn){
24651         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24652         //if(Roo.isSafari){ // safari
24653         //    adjust *= 2;
24654        // }
24655         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24656         if(Roo.isSafari){ // safari
24657             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24658             v =  (v < 10) ? 10 : v;
24659             v =  (v > 48) ? 48 : v;
24660             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24661             
24662         }
24663         
24664         
24665         v = Math.max(1, v+adjust);
24666         
24667         this.execCmd('FontSize', v  );
24668     },
24669
24670     onEditorEvent : function(e)
24671     {
24672         this.owner.fireEvent('editorevent', this, e);
24673       //  this.updateToolbar();
24674         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24675     },
24676
24677     insertTag : function(tg)
24678     {
24679         // could be a bit smarter... -> wrap the current selected tRoo..
24680         if (tg.toLowerCase() == 'span' ||
24681             tg.toLowerCase() == 'code' ||
24682             tg.toLowerCase() == 'sup' ||
24683             tg.toLowerCase() == 'sub' 
24684             ) {
24685             
24686             range = this.createRange(this.getSelection());
24687             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24688             wrappingNode.appendChild(range.extractContents());
24689             range.insertNode(wrappingNode);
24690
24691             return;
24692             
24693             
24694             
24695         }
24696         this.execCmd("formatblock",   tg);
24697         
24698     },
24699     
24700     insertText : function(txt)
24701     {
24702         
24703         
24704         var range = this.createRange();
24705         range.deleteContents();
24706                //alert(Sender.getAttribute('label'));
24707                
24708         range.insertNode(this.doc.createTextNode(txt));
24709     } ,
24710     
24711      
24712
24713     /**
24714      * Executes a Midas editor command on the editor document and performs necessary focus and
24715      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24716      * @param {String} cmd The Midas command
24717      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24718      */
24719     relayCmd : function(cmd, value){
24720         this.win.focus();
24721         this.execCmd(cmd, value);
24722         this.owner.fireEvent('editorevent', this);
24723         //this.updateToolbar();
24724         this.owner.deferFocus();
24725     },
24726
24727     /**
24728      * Executes a Midas editor command directly on the editor document.
24729      * For visual commands, you should use {@link #relayCmd} instead.
24730      * <b>This should only be called after the editor is initialized.</b>
24731      * @param {String} cmd The Midas command
24732      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24733      */
24734     execCmd : function(cmd, value){
24735         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24736         this.syncValue();
24737     },
24738  
24739  
24740    
24741     /**
24742      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24743      * to insert tRoo.
24744      * @param {String} text | dom node.. 
24745      */
24746     insertAtCursor : function(text)
24747     {
24748         
24749         if(!this.activated){
24750             return;
24751         }
24752         /*
24753         if(Roo.isIE){
24754             this.win.focus();
24755             var r = this.doc.selection.createRange();
24756             if(r){
24757                 r.collapse(true);
24758                 r.pasteHTML(text);
24759                 this.syncValue();
24760                 this.deferFocus();
24761             
24762             }
24763             return;
24764         }
24765         */
24766         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24767             this.win.focus();
24768             
24769             
24770             // from jquery ui (MIT licenced)
24771             var range, node;
24772             var win = this.win;
24773             
24774             if (win.getSelection && win.getSelection().getRangeAt) {
24775                 range = win.getSelection().getRangeAt(0);
24776                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24777                 range.insertNode(node);
24778             } else if (win.document.selection && win.document.selection.createRange) {
24779                 // no firefox support
24780                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24781                 win.document.selection.createRange().pasteHTML(txt);
24782             } else {
24783                 // no firefox support
24784                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24785                 this.execCmd('InsertHTML', txt);
24786             } 
24787             
24788             this.syncValue();
24789             
24790             this.deferFocus();
24791         }
24792     },
24793  // private
24794     mozKeyPress : function(e){
24795         if(e.ctrlKey){
24796             var c = e.getCharCode(), cmd;
24797           
24798             if(c > 0){
24799                 c = String.fromCharCode(c).toLowerCase();
24800                 switch(c){
24801                     case 'b':
24802                         cmd = 'bold';
24803                         break;
24804                     case 'i':
24805                         cmd = 'italic';
24806                         break;
24807                     
24808                     case 'u':
24809                         cmd = 'underline';
24810                         break;
24811                     
24812                     case 'v':
24813                         this.cleanUpPaste.defer(100, this);
24814                         return;
24815                         
24816                 }
24817                 if(cmd){
24818                     this.win.focus();
24819                     this.execCmd(cmd);
24820                     this.deferFocus();
24821                     e.preventDefault();
24822                 }
24823                 
24824             }
24825         }
24826     },
24827
24828     // private
24829     fixKeys : function(){ // load time branching for fastest keydown performance
24830         if(Roo.isIE){
24831             return function(e){
24832                 var k = e.getKey(), r;
24833                 if(k == e.TAB){
24834                     e.stopEvent();
24835                     r = this.doc.selection.createRange();
24836                     if(r){
24837                         r.collapse(true);
24838                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24839                         this.deferFocus();
24840                     }
24841                     return;
24842                 }
24843                 
24844                 if(k == e.ENTER){
24845                     r = this.doc.selection.createRange();
24846                     if(r){
24847                         var target = r.parentElement();
24848                         if(!target || target.tagName.toLowerCase() != 'li'){
24849                             e.stopEvent();
24850                             r.pasteHTML('<br />');
24851                             r.collapse(false);
24852                             r.select();
24853                         }
24854                     }
24855                 }
24856                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24857                     this.cleanUpPaste.defer(100, this);
24858                     return;
24859                 }
24860                 
24861                 
24862             };
24863         }else if(Roo.isOpera){
24864             return function(e){
24865                 var k = e.getKey();
24866                 if(k == e.TAB){
24867                     e.stopEvent();
24868                     this.win.focus();
24869                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24870                     this.deferFocus();
24871                 }
24872                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24873                     this.cleanUpPaste.defer(100, this);
24874                     return;
24875                 }
24876                 
24877             };
24878         }else if(Roo.isSafari){
24879             return function(e){
24880                 var k = e.getKey();
24881                 
24882                 if(k == e.TAB){
24883                     e.stopEvent();
24884                     this.execCmd('InsertText','\t');
24885                     this.deferFocus();
24886                     return;
24887                 }
24888                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24889                     this.cleanUpPaste.defer(100, this);
24890                     return;
24891                 }
24892                 
24893              };
24894         }
24895     }(),
24896     
24897     getAllAncestors: function()
24898     {
24899         var p = this.getSelectedNode();
24900         var a = [];
24901         if (!p) {
24902             a.push(p); // push blank onto stack..
24903             p = this.getParentElement();
24904         }
24905         
24906         
24907         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24908             a.push(p);
24909             p = p.parentNode;
24910         }
24911         a.push(this.doc.body);
24912         return a;
24913     },
24914     lastSel : false,
24915     lastSelNode : false,
24916     
24917     
24918     getSelection : function() 
24919     {
24920         this.assignDocWin();
24921         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24922     },
24923     
24924     getSelectedNode: function() 
24925     {
24926         // this may only work on Gecko!!!
24927         
24928         // should we cache this!!!!
24929         
24930         
24931         
24932          
24933         var range = this.createRange(this.getSelection()).cloneRange();
24934         
24935         if (Roo.isIE) {
24936             var parent = range.parentElement();
24937             while (true) {
24938                 var testRange = range.duplicate();
24939                 testRange.moveToElementText(parent);
24940                 if (testRange.inRange(range)) {
24941                     break;
24942                 }
24943                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24944                     break;
24945                 }
24946                 parent = parent.parentElement;
24947             }
24948             return parent;
24949         }
24950         
24951         // is ancestor a text element.
24952         var ac =  range.commonAncestorContainer;
24953         if (ac.nodeType == 3) {
24954             ac = ac.parentNode;
24955         }
24956         
24957         var ar = ac.childNodes;
24958          
24959         var nodes = [];
24960         var other_nodes = [];
24961         var has_other_nodes = false;
24962         for (var i=0;i<ar.length;i++) {
24963             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24964                 continue;
24965             }
24966             // fullly contained node.
24967             
24968             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24969                 nodes.push(ar[i]);
24970                 continue;
24971             }
24972             
24973             // probably selected..
24974             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24975                 other_nodes.push(ar[i]);
24976                 continue;
24977             }
24978             // outer..
24979             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24980                 continue;
24981             }
24982             
24983             
24984             has_other_nodes = true;
24985         }
24986         if (!nodes.length && other_nodes.length) {
24987             nodes= other_nodes;
24988         }
24989         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24990             return false;
24991         }
24992         
24993         return nodes[0];
24994     },
24995     createRange: function(sel)
24996     {
24997         // this has strange effects when using with 
24998         // top toolbar - not sure if it's a great idea.
24999         //this.editor.contentWindow.focus();
25000         if (typeof sel != "undefined") {
25001             try {
25002                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25003             } catch(e) {
25004                 return this.doc.createRange();
25005             }
25006         } else {
25007             return this.doc.createRange();
25008         }
25009     },
25010     getParentElement: function()
25011     {
25012         
25013         this.assignDocWin();
25014         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25015         
25016         var range = this.createRange(sel);
25017          
25018         try {
25019             var p = range.commonAncestorContainer;
25020             while (p.nodeType == 3) { // text node
25021                 p = p.parentNode;
25022             }
25023             return p;
25024         } catch (e) {
25025             return null;
25026         }
25027     
25028     },
25029     /***
25030      *
25031      * Range intersection.. the hard stuff...
25032      *  '-1' = before
25033      *  '0' = hits..
25034      *  '1' = after.
25035      *         [ -- selected range --- ]
25036      *   [fail]                        [fail]
25037      *
25038      *    basically..
25039      *      if end is before start or  hits it. fail.
25040      *      if start is after end or hits it fail.
25041      *
25042      *   if either hits (but other is outside. - then it's not 
25043      *   
25044      *    
25045      **/
25046     
25047     
25048     // @see http://www.thismuchiknow.co.uk/?p=64.
25049     rangeIntersectsNode : function(range, node)
25050     {
25051         var nodeRange = node.ownerDocument.createRange();
25052         try {
25053             nodeRange.selectNode(node);
25054         } catch (e) {
25055             nodeRange.selectNodeContents(node);
25056         }
25057     
25058         var rangeStartRange = range.cloneRange();
25059         rangeStartRange.collapse(true);
25060     
25061         var rangeEndRange = range.cloneRange();
25062         rangeEndRange.collapse(false);
25063     
25064         var nodeStartRange = nodeRange.cloneRange();
25065         nodeStartRange.collapse(true);
25066     
25067         var nodeEndRange = nodeRange.cloneRange();
25068         nodeEndRange.collapse(false);
25069     
25070         return rangeStartRange.compareBoundaryPoints(
25071                  Range.START_TO_START, nodeEndRange) == -1 &&
25072                rangeEndRange.compareBoundaryPoints(
25073                  Range.START_TO_START, nodeStartRange) == 1;
25074         
25075          
25076     },
25077     rangeCompareNode : function(range, node)
25078     {
25079         var nodeRange = node.ownerDocument.createRange();
25080         try {
25081             nodeRange.selectNode(node);
25082         } catch (e) {
25083             nodeRange.selectNodeContents(node);
25084         }
25085         
25086         
25087         range.collapse(true);
25088     
25089         nodeRange.collapse(true);
25090      
25091         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25092         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25093          
25094         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25095         
25096         var nodeIsBefore   =  ss == 1;
25097         var nodeIsAfter    = ee == -1;
25098         
25099         if (nodeIsBefore && nodeIsAfter) {
25100             return 0; // outer
25101         }
25102         if (!nodeIsBefore && nodeIsAfter) {
25103             return 1; //right trailed.
25104         }
25105         
25106         if (nodeIsBefore && !nodeIsAfter) {
25107             return 2;  // left trailed.
25108         }
25109         // fully contined.
25110         return 3;
25111     },
25112
25113     // private? - in a new class?
25114     cleanUpPaste :  function()
25115     {
25116         // cleans up the whole document..
25117         Roo.log('cleanuppaste');
25118         
25119         this.cleanUpChildren(this.doc.body);
25120         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25121         if (clean != this.doc.body.innerHTML) {
25122             this.doc.body.innerHTML = clean;
25123         }
25124         
25125     },
25126     
25127     cleanWordChars : function(input) {// change the chars to hex code
25128         var he = Roo.HtmlEditorCore;
25129         
25130         var output = input;
25131         Roo.each(he.swapCodes, function(sw) { 
25132             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25133             
25134             output = output.replace(swapper, sw[1]);
25135         });
25136         
25137         return output;
25138     },
25139     
25140     
25141     cleanUpChildren : function (n)
25142     {
25143         if (!n.childNodes.length) {
25144             return;
25145         }
25146         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25147            this.cleanUpChild(n.childNodes[i]);
25148         }
25149     },
25150     
25151     
25152         
25153     
25154     cleanUpChild : function (node)
25155     {
25156         var ed = this;
25157         //console.log(node);
25158         if (node.nodeName == "#text") {
25159             // clean up silly Windows -- stuff?
25160             return; 
25161         }
25162         if (node.nodeName == "#comment") {
25163             node.parentNode.removeChild(node);
25164             // clean up silly Windows -- stuff?
25165             return; 
25166         }
25167         var lcname = node.tagName.toLowerCase();
25168         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25169         // whitelist of tags..
25170         
25171         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25172             // remove node.
25173             node.parentNode.removeChild(node);
25174             return;
25175             
25176         }
25177         
25178         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25179         
25180         // spans with no attributes - just remove them..
25181         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25182             remove_keep_children = true;
25183         }
25184         
25185         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25186         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25187         
25188         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25189         //    remove_keep_children = true;
25190         //}
25191         
25192         if (remove_keep_children) {
25193             this.cleanUpChildren(node);
25194             // inserts everything just before this node...
25195             while (node.childNodes.length) {
25196                 var cn = node.childNodes[0];
25197                 node.removeChild(cn);
25198                 node.parentNode.insertBefore(cn, node);
25199             }
25200             node.parentNode.removeChild(node);
25201             return;
25202         }
25203         
25204         if (!node.attributes || !node.attributes.length) {
25205             
25206           
25207             
25208             
25209             this.cleanUpChildren(node);
25210             return;
25211         }
25212         
25213         function cleanAttr(n,v)
25214         {
25215             
25216             if (v.match(/^\./) || v.match(/^\//)) {
25217                 return;
25218             }
25219             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25220                 return;
25221             }
25222             if (v.match(/^#/)) {
25223                 return;
25224             }
25225             if (v.match(/^\{/)) { // allow template editing.
25226                 return;
25227             }
25228 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25229             node.removeAttribute(n);
25230             
25231         }
25232         
25233         var cwhite = this.cwhite;
25234         var cblack = this.cblack;
25235             
25236         function cleanStyle(n,v)
25237         {
25238             if (v.match(/expression/)) { //XSS?? should we even bother..
25239                 node.removeAttribute(n);
25240                 return;
25241             }
25242             
25243             var parts = v.split(/;/);
25244             var clean = [];
25245             
25246             Roo.each(parts, function(p) {
25247                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25248                 if (!p.length) {
25249                     return true;
25250                 }
25251                 var l = p.split(':').shift().replace(/\s+/g,'');
25252                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25253                 
25254                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25255 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25256                     //node.removeAttribute(n);
25257                     return true;
25258                 }
25259                 //Roo.log()
25260                 // only allow 'c whitelisted system attributes'
25261                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25262 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25263                     //node.removeAttribute(n);
25264                     return true;
25265                 }
25266                 
25267                 
25268                  
25269                 
25270                 clean.push(p);
25271                 return true;
25272             });
25273             if (clean.length) { 
25274                 node.setAttribute(n, clean.join(';'));
25275             } else {
25276                 node.removeAttribute(n);
25277             }
25278             
25279         }
25280         
25281         
25282         for (var i = node.attributes.length-1; i > -1 ; i--) {
25283             var a = node.attributes[i];
25284             //console.log(a);
25285             
25286             if (a.name.toLowerCase().substr(0,2)=='on')  {
25287                 node.removeAttribute(a.name);
25288                 continue;
25289             }
25290             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25291                 node.removeAttribute(a.name);
25292                 continue;
25293             }
25294             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25295                 cleanAttr(a.name,a.value); // fixme..
25296                 continue;
25297             }
25298             if (a.name == 'style') {
25299                 cleanStyle(a.name,a.value);
25300                 continue;
25301             }
25302             /// clean up MS crap..
25303             // tecnically this should be a list of valid class'es..
25304             
25305             
25306             if (a.name == 'class') {
25307                 if (a.value.match(/^Mso/)) {
25308                     node.removeAttribute('class');
25309                 }
25310                 
25311                 if (a.value.match(/^body$/)) {
25312                     node.removeAttribute('class');
25313                 }
25314                 continue;
25315             }
25316             
25317             // style cleanup!?
25318             // class cleanup?
25319             
25320         }
25321         
25322         
25323         this.cleanUpChildren(node);
25324         
25325         
25326     },
25327     
25328     /**
25329      * Clean up MS wordisms...
25330      */
25331     cleanWord : function(node)
25332     {
25333         if (!node) {
25334             this.cleanWord(this.doc.body);
25335             return;
25336         }
25337         
25338         if(
25339                 node.nodeName == 'SPAN' &&
25340                 !node.hasAttributes() &&
25341                 node.childNodes.length == 1 &&
25342                 node.firstChild.nodeName == "#text"  
25343         ) {
25344             var textNode = node.firstChild;
25345             node.removeChild(textNode);
25346             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25347                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25348             }
25349             node.parentNode.insertBefore(textNode, node);
25350             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25351                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25352             }
25353             node.parentNode.removeChild(node);
25354         }
25355         
25356         if (node.nodeName == "#text") {
25357             // clean up silly Windows -- stuff?
25358             return; 
25359         }
25360         if (node.nodeName == "#comment") {
25361             node.parentNode.removeChild(node);
25362             // clean up silly Windows -- stuff?
25363             return; 
25364         }
25365         
25366         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25367             node.parentNode.removeChild(node);
25368             return;
25369         }
25370         //Roo.log(node.tagName);
25371         // remove - but keep children..
25372         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25373             //Roo.log('-- removed');
25374             while (node.childNodes.length) {
25375                 var cn = node.childNodes[0];
25376                 node.removeChild(cn);
25377                 node.parentNode.insertBefore(cn, node);
25378                 // move node to parent - and clean it..
25379                 this.cleanWord(cn);
25380             }
25381             node.parentNode.removeChild(node);
25382             /// no need to iterate chidlren = it's got none..
25383             //this.iterateChildren(node, this.cleanWord);
25384             return;
25385         }
25386         // clean styles
25387         if (node.className.length) {
25388             
25389             var cn = node.className.split(/\W+/);
25390             var cna = [];
25391             Roo.each(cn, function(cls) {
25392                 if (cls.match(/Mso[a-zA-Z]+/)) {
25393                     return;
25394                 }
25395                 cna.push(cls);
25396             });
25397             node.className = cna.length ? cna.join(' ') : '';
25398             if (!cna.length) {
25399                 node.removeAttribute("class");
25400             }
25401         }
25402         
25403         if (node.hasAttribute("lang")) {
25404             node.removeAttribute("lang");
25405         }
25406         
25407         if (node.hasAttribute("style")) {
25408             
25409             var styles = node.getAttribute("style").split(";");
25410             var nstyle = [];
25411             Roo.each(styles, function(s) {
25412                 if (!s.match(/:/)) {
25413                     return;
25414                 }
25415                 var kv = s.split(":");
25416                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25417                     return;
25418                 }
25419                 // what ever is left... we allow.
25420                 nstyle.push(s);
25421             });
25422             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25423             if (!nstyle.length) {
25424                 node.removeAttribute('style');
25425             }
25426         }
25427         this.iterateChildren(node, this.cleanWord);
25428         
25429         
25430         
25431     },
25432     /**
25433      * iterateChildren of a Node, calling fn each time, using this as the scole..
25434      * @param {DomNode} node node to iterate children of.
25435      * @param {Function} fn method of this class to call on each item.
25436      */
25437     iterateChildren : function(node, fn)
25438     {
25439         if (!node.childNodes.length) {
25440                 return;
25441         }
25442         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25443            fn.call(this, node.childNodes[i])
25444         }
25445     },
25446     
25447     
25448     /**
25449      * cleanTableWidths.
25450      *
25451      * Quite often pasting from word etc.. results in tables with column and widths.
25452      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25453      *
25454      */
25455     cleanTableWidths : function(node)
25456     {
25457          
25458          
25459         if (!node) {
25460             this.cleanTableWidths(this.doc.body);
25461             return;
25462         }
25463         
25464         // ignore list...
25465         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25466             return; 
25467         }
25468         Roo.log(node.tagName);
25469         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25470             this.iterateChildren(node, this.cleanTableWidths);
25471             return;
25472         }
25473         if (node.hasAttribute('width')) {
25474             node.removeAttribute('width');
25475         }
25476         
25477          
25478         if (node.hasAttribute("style")) {
25479             // pretty basic...
25480             
25481             var styles = node.getAttribute("style").split(";");
25482             var nstyle = [];
25483             Roo.each(styles, function(s) {
25484                 if (!s.match(/:/)) {
25485                     return;
25486                 }
25487                 var kv = s.split(":");
25488                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25489                     return;
25490                 }
25491                 // what ever is left... we allow.
25492                 nstyle.push(s);
25493             });
25494             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25495             if (!nstyle.length) {
25496                 node.removeAttribute('style');
25497             }
25498         }
25499         
25500         this.iterateChildren(node, this.cleanTableWidths);
25501         
25502         
25503     },
25504     
25505     
25506     
25507     
25508     domToHTML : function(currentElement, depth, nopadtext) {
25509         
25510         depth = depth || 0;
25511         nopadtext = nopadtext || false;
25512     
25513         if (!currentElement) {
25514             return this.domToHTML(this.doc.body);
25515         }
25516         
25517         //Roo.log(currentElement);
25518         var j;
25519         var allText = false;
25520         var nodeName = currentElement.nodeName;
25521         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25522         
25523         if  (nodeName == '#text') {
25524             
25525             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25526         }
25527         
25528         
25529         var ret = '';
25530         if (nodeName != 'BODY') {
25531              
25532             var i = 0;
25533             // Prints the node tagName, such as <A>, <IMG>, etc
25534             if (tagName) {
25535                 var attr = [];
25536                 for(i = 0; i < currentElement.attributes.length;i++) {
25537                     // quoting?
25538                     var aname = currentElement.attributes.item(i).name;
25539                     if (!currentElement.attributes.item(i).value.length) {
25540                         continue;
25541                     }
25542                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25543                 }
25544                 
25545                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25546             } 
25547             else {
25548                 
25549                 // eack
25550             }
25551         } else {
25552             tagName = false;
25553         }
25554         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25555             return ret;
25556         }
25557         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25558             nopadtext = true;
25559         }
25560         
25561         
25562         // Traverse the tree
25563         i = 0;
25564         var currentElementChild = currentElement.childNodes.item(i);
25565         var allText = true;
25566         var innerHTML  = '';
25567         lastnode = '';
25568         while (currentElementChild) {
25569             // Formatting code (indent the tree so it looks nice on the screen)
25570             var nopad = nopadtext;
25571             if (lastnode == 'SPAN') {
25572                 nopad  = true;
25573             }
25574             // text
25575             if  (currentElementChild.nodeName == '#text') {
25576                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25577                 toadd = nopadtext ? toadd : toadd.trim();
25578                 if (!nopad && toadd.length > 80) {
25579                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25580                 }
25581                 innerHTML  += toadd;
25582                 
25583                 i++;
25584                 currentElementChild = currentElement.childNodes.item(i);
25585                 lastNode = '';
25586                 continue;
25587             }
25588             allText = false;
25589             
25590             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25591                 
25592             // Recursively traverse the tree structure of the child node
25593             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25594             lastnode = currentElementChild.nodeName;
25595             i++;
25596             currentElementChild=currentElement.childNodes.item(i);
25597         }
25598         
25599         ret += innerHTML;
25600         
25601         if (!allText) {
25602                 // The remaining code is mostly for formatting the tree
25603             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25604         }
25605         
25606         
25607         if (tagName) {
25608             ret+= "</"+tagName+">";
25609         }
25610         return ret;
25611         
25612     },
25613         
25614     applyBlacklists : function()
25615     {
25616         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25617         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25618         
25619         this.white = [];
25620         this.black = [];
25621         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25622             if (b.indexOf(tag) > -1) {
25623                 return;
25624             }
25625             this.white.push(tag);
25626             
25627         }, this);
25628         
25629         Roo.each(w, function(tag) {
25630             if (b.indexOf(tag) > -1) {
25631                 return;
25632             }
25633             if (this.white.indexOf(tag) > -1) {
25634                 return;
25635             }
25636             this.white.push(tag);
25637             
25638         }, this);
25639         
25640         
25641         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25642             if (w.indexOf(tag) > -1) {
25643                 return;
25644             }
25645             this.black.push(tag);
25646             
25647         }, this);
25648         
25649         Roo.each(b, function(tag) {
25650             if (w.indexOf(tag) > -1) {
25651                 return;
25652             }
25653             if (this.black.indexOf(tag) > -1) {
25654                 return;
25655             }
25656             this.black.push(tag);
25657             
25658         }, this);
25659         
25660         
25661         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25662         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25663         
25664         this.cwhite = [];
25665         this.cblack = [];
25666         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25667             if (b.indexOf(tag) > -1) {
25668                 return;
25669             }
25670             this.cwhite.push(tag);
25671             
25672         }, this);
25673         
25674         Roo.each(w, function(tag) {
25675             if (b.indexOf(tag) > -1) {
25676                 return;
25677             }
25678             if (this.cwhite.indexOf(tag) > -1) {
25679                 return;
25680             }
25681             this.cwhite.push(tag);
25682             
25683         }, this);
25684         
25685         
25686         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25687             if (w.indexOf(tag) > -1) {
25688                 return;
25689             }
25690             this.cblack.push(tag);
25691             
25692         }, this);
25693         
25694         Roo.each(b, function(tag) {
25695             if (w.indexOf(tag) > -1) {
25696                 return;
25697             }
25698             if (this.cblack.indexOf(tag) > -1) {
25699                 return;
25700             }
25701             this.cblack.push(tag);
25702             
25703         }, this);
25704     },
25705     
25706     setStylesheets : function(stylesheets)
25707     {
25708         if(typeof(stylesheets) == 'string'){
25709             Roo.get(this.iframe.contentDocument.head).createChild({
25710                 tag : 'link',
25711                 rel : 'stylesheet',
25712                 type : 'text/css',
25713                 href : stylesheets
25714             });
25715             
25716             return;
25717         }
25718         var _this = this;
25719      
25720         Roo.each(stylesheets, function(s) {
25721             if(!s.length){
25722                 return;
25723             }
25724             
25725             Roo.get(_this.iframe.contentDocument.head).createChild({
25726                 tag : 'link',
25727                 rel : 'stylesheet',
25728                 type : 'text/css',
25729                 href : s
25730             });
25731         });
25732
25733         
25734     },
25735     
25736     removeStylesheets : function()
25737     {
25738         var _this = this;
25739         
25740         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25741             s.remove();
25742         });
25743     },
25744     
25745     setStyle : function(style)
25746     {
25747         Roo.get(this.iframe.contentDocument.head).createChild({
25748             tag : 'style',
25749             type : 'text/css',
25750             html : style
25751         });
25752
25753         return;
25754     }
25755     
25756     // hide stuff that is not compatible
25757     /**
25758      * @event blur
25759      * @hide
25760      */
25761     /**
25762      * @event change
25763      * @hide
25764      */
25765     /**
25766      * @event focus
25767      * @hide
25768      */
25769     /**
25770      * @event specialkey
25771      * @hide
25772      */
25773     /**
25774      * @cfg {String} fieldClass @hide
25775      */
25776     /**
25777      * @cfg {String} focusClass @hide
25778      */
25779     /**
25780      * @cfg {String} autoCreate @hide
25781      */
25782     /**
25783      * @cfg {String} inputType @hide
25784      */
25785     /**
25786      * @cfg {String} invalidClass @hide
25787      */
25788     /**
25789      * @cfg {String} invalidText @hide
25790      */
25791     /**
25792      * @cfg {String} msgFx @hide
25793      */
25794     /**
25795      * @cfg {String} validateOnBlur @hide
25796      */
25797 });
25798
25799 Roo.HtmlEditorCore.white = [
25800         'area', 'br', 'img', 'input', 'hr', 'wbr',
25801         
25802        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25803        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25804        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25805        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25806        'table',   'ul',         'xmp', 
25807        
25808        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25809       'thead',   'tr', 
25810      
25811       'dir', 'menu', 'ol', 'ul', 'dl',
25812        
25813       'embed',  'object'
25814 ];
25815
25816
25817 Roo.HtmlEditorCore.black = [
25818     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25819         'applet', // 
25820         'base',   'basefont', 'bgsound', 'blink',  'body', 
25821         'frame',  'frameset', 'head',    'html',   'ilayer', 
25822         'iframe', 'layer',  'link',     'meta',    'object',   
25823         'script', 'style' ,'title',  'xml' // clean later..
25824 ];
25825 Roo.HtmlEditorCore.clean = [
25826     'script', 'style', 'title', 'xml'
25827 ];
25828 Roo.HtmlEditorCore.remove = [
25829     'font'
25830 ];
25831 // attributes..
25832
25833 Roo.HtmlEditorCore.ablack = [
25834     'on'
25835 ];
25836     
25837 Roo.HtmlEditorCore.aclean = [ 
25838     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25839 ];
25840
25841 // protocols..
25842 Roo.HtmlEditorCore.pwhite= [
25843         'http',  'https',  'mailto'
25844 ];
25845
25846 // white listed style attributes.
25847 Roo.HtmlEditorCore.cwhite= [
25848       //  'text-align', /// default is to allow most things..
25849       
25850          
25851 //        'font-size'//??
25852 ];
25853
25854 // black listed style attributes.
25855 Roo.HtmlEditorCore.cblack= [
25856       //  'font-size' -- this can be set by the project 
25857 ];
25858
25859
25860 Roo.HtmlEditorCore.swapCodes   =[ 
25861     [    8211, "&#8211;" ], 
25862     [    8212, "&#8212;" ], 
25863     [    8216,  "'" ],  
25864     [    8217, "'" ],  
25865     [    8220, '"' ],  
25866     [    8221, '"' ],  
25867     [    8226, "*" ],  
25868     [    8230, "..." ]
25869 ]; 
25870
25871     /*
25872  * - LGPL
25873  *
25874  * HtmlEditor
25875  * 
25876  */
25877
25878 /**
25879  * @class Roo.bootstrap.HtmlEditor
25880  * @extends Roo.bootstrap.TextArea
25881  * Bootstrap HtmlEditor class
25882
25883  * @constructor
25884  * Create a new HtmlEditor
25885  * @param {Object} config The config object
25886  */
25887
25888 Roo.bootstrap.HtmlEditor = function(config){
25889     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25890     if (!this.toolbars) {
25891         this.toolbars = [];
25892     }
25893     
25894     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25895     this.addEvents({
25896             /**
25897              * @event initialize
25898              * Fires when the editor is fully initialized (including the iframe)
25899              * @param {HtmlEditor} this
25900              */
25901             initialize: true,
25902             /**
25903              * @event activate
25904              * Fires when the editor is first receives the focus. Any insertion must wait
25905              * until after this event.
25906              * @param {HtmlEditor} this
25907              */
25908             activate: true,
25909              /**
25910              * @event beforesync
25911              * Fires before the textarea is updated with content from the editor iframe. Return false
25912              * to cancel the sync.
25913              * @param {HtmlEditor} this
25914              * @param {String} html
25915              */
25916             beforesync: true,
25917              /**
25918              * @event beforepush
25919              * Fires before the iframe editor is updated with content from the textarea. Return false
25920              * to cancel the push.
25921              * @param {HtmlEditor} this
25922              * @param {String} html
25923              */
25924             beforepush: true,
25925              /**
25926              * @event sync
25927              * Fires when the textarea is updated with content from the editor iframe.
25928              * @param {HtmlEditor} this
25929              * @param {String} html
25930              */
25931             sync: true,
25932              /**
25933              * @event push
25934              * Fires when the iframe editor is updated with content from the textarea.
25935              * @param {HtmlEditor} this
25936              * @param {String} html
25937              */
25938             push: true,
25939              /**
25940              * @event editmodechange
25941              * Fires when the editor switches edit modes
25942              * @param {HtmlEditor} this
25943              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25944              */
25945             editmodechange: true,
25946             /**
25947              * @event editorevent
25948              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25949              * @param {HtmlEditor} this
25950              */
25951             editorevent: true,
25952             /**
25953              * @event firstfocus
25954              * Fires when on first focus - needed by toolbars..
25955              * @param {HtmlEditor} this
25956              */
25957             firstfocus: true,
25958             /**
25959              * @event autosave
25960              * Auto save the htmlEditor value as a file into Events
25961              * @param {HtmlEditor} this
25962              */
25963             autosave: true,
25964             /**
25965              * @event savedpreview
25966              * preview the saved version of htmlEditor
25967              * @param {HtmlEditor} this
25968              */
25969             savedpreview: true
25970         });
25971 };
25972
25973
25974 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25975     
25976     
25977       /**
25978      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25979      */
25980     toolbars : false,
25981     
25982      /**
25983     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25984     */
25985     btns : [],
25986    
25987      /**
25988      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25989      *                        Roo.resizable.
25990      */
25991     resizable : false,
25992      /**
25993      * @cfg {Number} height (in pixels)
25994      */   
25995     height: 300,
25996    /**
25997      * @cfg {Number} width (in pixels)
25998      */   
25999     width: false,
26000     
26001     /**
26002      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26003      * 
26004      */
26005     stylesheets: false,
26006     
26007     // id of frame..
26008     frameId: false,
26009     
26010     // private properties
26011     validationEvent : false,
26012     deferHeight: true,
26013     initialized : false,
26014     activated : false,
26015     
26016     onFocus : Roo.emptyFn,
26017     iframePad:3,
26018     hideMode:'offsets',
26019     
26020     tbContainer : false,
26021     
26022     bodyCls : '',
26023     
26024     toolbarContainer :function() {
26025         return this.wrap.select('.x-html-editor-tb',true).first();
26026     },
26027
26028     /**
26029      * Protected method that will not generally be called directly. It
26030      * is called when the editor creates its toolbar. Override this method if you need to
26031      * add custom toolbar buttons.
26032      * @param {HtmlEditor} editor
26033      */
26034     createToolbar : function(){
26035         Roo.log('renewing');
26036         Roo.log("create toolbars");
26037         
26038         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26039         this.toolbars[0].render(this.toolbarContainer());
26040         
26041         return;
26042         
26043 //        if (!editor.toolbars || !editor.toolbars.length) {
26044 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26045 //        }
26046 //        
26047 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26048 //            editor.toolbars[i] = Roo.factory(
26049 //                    typeof(editor.toolbars[i]) == 'string' ?
26050 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26051 //                Roo.bootstrap.HtmlEditor);
26052 //            editor.toolbars[i].init(editor);
26053 //        }
26054     },
26055
26056      
26057     // private
26058     onRender : function(ct, position)
26059     {
26060        // Roo.log("Call onRender: " + this.xtype);
26061         var _t = this;
26062         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26063       
26064         this.wrap = this.inputEl().wrap({
26065             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26066         });
26067         
26068         this.editorcore.onRender(ct, position);
26069          
26070         if (this.resizable) {
26071             this.resizeEl = new Roo.Resizable(this.wrap, {
26072                 pinned : true,
26073                 wrap: true,
26074                 dynamic : true,
26075                 minHeight : this.height,
26076                 height: this.height,
26077                 handles : this.resizable,
26078                 width: this.width,
26079                 listeners : {
26080                     resize : function(r, w, h) {
26081                         _t.onResize(w,h); // -something
26082                     }
26083                 }
26084             });
26085             
26086         }
26087         this.createToolbar(this);
26088        
26089         
26090         if(!this.width && this.resizable){
26091             this.setSize(this.wrap.getSize());
26092         }
26093         if (this.resizeEl) {
26094             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26095             // should trigger onReize..
26096         }
26097         
26098     },
26099
26100     // private
26101     onResize : function(w, h)
26102     {
26103         Roo.log('resize: ' +w + ',' + h );
26104         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26105         var ew = false;
26106         var eh = false;
26107         
26108         if(this.inputEl() ){
26109             if(typeof w == 'number'){
26110                 var aw = w - this.wrap.getFrameWidth('lr');
26111                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26112                 ew = aw;
26113             }
26114             if(typeof h == 'number'){
26115                  var tbh = -11;  // fixme it needs to tool bar size!
26116                 for (var i =0; i < this.toolbars.length;i++) {
26117                     // fixme - ask toolbars for heights?
26118                     tbh += this.toolbars[i].el.getHeight();
26119                     //if (this.toolbars[i].footer) {
26120                     //    tbh += this.toolbars[i].footer.el.getHeight();
26121                     //}
26122                 }
26123               
26124                 
26125                 
26126                 
26127                 
26128                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26129                 ah -= 5; // knock a few pixes off for look..
26130                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26131                 var eh = ah;
26132             }
26133         }
26134         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26135         this.editorcore.onResize(ew,eh);
26136         
26137     },
26138
26139     /**
26140      * Toggles the editor between standard and source edit mode.
26141      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26142      */
26143     toggleSourceEdit : function(sourceEditMode)
26144     {
26145         this.editorcore.toggleSourceEdit(sourceEditMode);
26146         
26147         if(this.editorcore.sourceEditMode){
26148             Roo.log('editor - showing textarea');
26149             
26150 //            Roo.log('in');
26151 //            Roo.log(this.syncValue());
26152             this.syncValue();
26153             this.inputEl().removeClass(['hide', 'x-hidden']);
26154             this.inputEl().dom.removeAttribute('tabIndex');
26155             this.inputEl().focus();
26156         }else{
26157             Roo.log('editor - hiding textarea');
26158 //            Roo.log('out')
26159 //            Roo.log(this.pushValue()); 
26160             this.pushValue();
26161             
26162             this.inputEl().addClass(['hide', 'x-hidden']);
26163             this.inputEl().dom.setAttribute('tabIndex', -1);
26164             //this.deferFocus();
26165         }
26166          
26167         if(this.resizable){
26168             this.setSize(this.wrap.getSize());
26169         }
26170         
26171         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26172     },
26173  
26174     // private (for BoxComponent)
26175     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26176
26177     // private (for BoxComponent)
26178     getResizeEl : function(){
26179         return this.wrap;
26180     },
26181
26182     // private (for BoxComponent)
26183     getPositionEl : function(){
26184         return this.wrap;
26185     },
26186
26187     // private
26188     initEvents : function(){
26189         this.originalValue = this.getValue();
26190     },
26191
26192 //    /**
26193 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26194 //     * @method
26195 //     */
26196 //    markInvalid : Roo.emptyFn,
26197 //    /**
26198 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26199 //     * @method
26200 //     */
26201 //    clearInvalid : Roo.emptyFn,
26202
26203     setValue : function(v){
26204         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26205         this.editorcore.pushValue();
26206     },
26207
26208      
26209     // private
26210     deferFocus : function(){
26211         this.focus.defer(10, this);
26212     },
26213
26214     // doc'ed in Field
26215     focus : function(){
26216         this.editorcore.focus();
26217         
26218     },
26219       
26220
26221     // private
26222     onDestroy : function(){
26223         
26224         
26225         
26226         if(this.rendered){
26227             
26228             for (var i =0; i < this.toolbars.length;i++) {
26229                 // fixme - ask toolbars for heights?
26230                 this.toolbars[i].onDestroy();
26231             }
26232             
26233             this.wrap.dom.innerHTML = '';
26234             this.wrap.remove();
26235         }
26236     },
26237
26238     // private
26239     onFirstFocus : function(){
26240         //Roo.log("onFirstFocus");
26241         this.editorcore.onFirstFocus();
26242          for (var i =0; i < this.toolbars.length;i++) {
26243             this.toolbars[i].onFirstFocus();
26244         }
26245         
26246     },
26247     
26248     // private
26249     syncValue : function()
26250     {   
26251         this.editorcore.syncValue();
26252     },
26253     
26254     pushValue : function()
26255     {   
26256         this.editorcore.pushValue();
26257     }
26258      
26259     
26260     // hide stuff that is not compatible
26261     /**
26262      * @event blur
26263      * @hide
26264      */
26265     /**
26266      * @event change
26267      * @hide
26268      */
26269     /**
26270      * @event focus
26271      * @hide
26272      */
26273     /**
26274      * @event specialkey
26275      * @hide
26276      */
26277     /**
26278      * @cfg {String} fieldClass @hide
26279      */
26280     /**
26281      * @cfg {String} focusClass @hide
26282      */
26283     /**
26284      * @cfg {String} autoCreate @hide
26285      */
26286     /**
26287      * @cfg {String} inputType @hide
26288      */
26289      
26290     /**
26291      * @cfg {String} invalidText @hide
26292      */
26293     /**
26294      * @cfg {String} msgFx @hide
26295      */
26296     /**
26297      * @cfg {String} validateOnBlur @hide
26298      */
26299 });
26300  
26301     
26302    
26303    
26304    
26305       
26306 Roo.namespace('Roo.bootstrap.htmleditor');
26307 /**
26308  * @class Roo.bootstrap.HtmlEditorToolbar1
26309  * Basic Toolbar
26310  * 
26311  * @example
26312  * Usage:
26313  *
26314  new Roo.bootstrap.HtmlEditor({
26315     ....
26316     toolbars : [
26317         new Roo.bootstrap.HtmlEditorToolbar1({
26318             disable : { fonts: 1 , format: 1, ..., ... , ...],
26319             btns : [ .... ]
26320         })
26321     }
26322      
26323  * 
26324  * @cfg {Object} disable List of elements to disable..
26325  * @cfg {Array} btns List of additional buttons.
26326  * 
26327  * 
26328  * NEEDS Extra CSS? 
26329  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26330  */
26331  
26332 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26333 {
26334     
26335     Roo.apply(this, config);
26336     
26337     // default disabled, based on 'good practice'..
26338     this.disable = this.disable || {};
26339     Roo.applyIf(this.disable, {
26340         fontSize : true,
26341         colors : true,
26342         specialElements : true
26343     });
26344     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26345     
26346     this.editor = config.editor;
26347     this.editorcore = config.editor.editorcore;
26348     
26349     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26350     
26351     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26352     // dont call parent... till later.
26353 }
26354 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26355      
26356     bar : true,
26357     
26358     editor : false,
26359     editorcore : false,
26360     
26361     
26362     formats : [
26363         "p" ,  
26364         "h1","h2","h3","h4","h5","h6", 
26365         "pre", "code", 
26366         "abbr", "acronym", "address", "cite", "samp", "var",
26367         'div','span'
26368     ],
26369     
26370     onRender : function(ct, position)
26371     {
26372        // Roo.log("Call onRender: " + this.xtype);
26373         
26374        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26375        Roo.log(this.el);
26376        this.el.dom.style.marginBottom = '0';
26377        var _this = this;
26378        var editorcore = this.editorcore;
26379        var editor= this.editor;
26380        
26381        var children = [];
26382        var btn = function(id,cmd , toggle, handler, html){
26383        
26384             var  event = toggle ? 'toggle' : 'click';
26385        
26386             var a = {
26387                 size : 'sm',
26388                 xtype: 'Button',
26389                 xns: Roo.bootstrap,
26390                 //glyphicon : id,
26391                 fa: id,
26392                 cmd : id || cmd,
26393                 enableToggle:toggle !== false,
26394                 html : html || '',
26395                 pressed : toggle ? false : null,
26396                 listeners : {}
26397             };
26398             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26399                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26400             };
26401             children.push(a);
26402             return a;
26403        }
26404        
26405     //    var cb_box = function...
26406         
26407         var style = {
26408                 xtype: 'Button',
26409                 size : 'sm',
26410                 xns: Roo.bootstrap,
26411                 fa : 'font',
26412                 //html : 'submit'
26413                 menu : {
26414                     xtype: 'Menu',
26415                     xns: Roo.bootstrap,
26416                     items:  []
26417                 }
26418         };
26419         Roo.each(this.formats, function(f) {
26420             style.menu.items.push({
26421                 xtype :'MenuItem',
26422                 xns: Roo.bootstrap,
26423                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26424                 tagname : f,
26425                 listeners : {
26426                     click : function()
26427                     {
26428                         editorcore.insertTag(this.tagname);
26429                         editor.focus();
26430                     }
26431                 }
26432                 
26433             });
26434         });
26435         children.push(style);   
26436         
26437         btn('bold',false,true);
26438         btn('italic',false,true);
26439         btn('align-left', 'justifyleft',true);
26440         btn('align-center', 'justifycenter',true);
26441         btn('align-right' , 'justifyright',true);
26442         btn('link', false, false, function(btn) {
26443             //Roo.log("create link?");
26444             var url = prompt(this.createLinkText, this.defaultLinkValue);
26445             if(url && url != 'http:/'+'/'){
26446                 this.editorcore.relayCmd('createlink', url);
26447             }
26448         }),
26449         btn('list','insertunorderedlist',true);
26450         btn('pencil', false,true, function(btn){
26451                 Roo.log(this);
26452                 this.toggleSourceEdit(btn.pressed);
26453         });
26454         
26455         if (this.editor.btns.length > 0) {
26456             for (var i = 0; i<this.editor.btns.length; i++) {
26457                 children.push(this.editor.btns[i]);
26458             }
26459         }
26460         
26461         /*
26462         var cog = {
26463                 xtype: 'Button',
26464                 size : 'sm',
26465                 xns: Roo.bootstrap,
26466                 glyphicon : 'cog',
26467                 //html : 'submit'
26468                 menu : {
26469                     xtype: 'Menu',
26470                     xns: Roo.bootstrap,
26471                     items:  []
26472                 }
26473         };
26474         
26475         cog.menu.items.push({
26476             xtype :'MenuItem',
26477             xns: Roo.bootstrap,
26478             html : Clean styles,
26479             tagname : f,
26480             listeners : {
26481                 click : function()
26482                 {
26483                     editorcore.insertTag(this.tagname);
26484                     editor.focus();
26485                 }
26486             }
26487             
26488         });
26489        */
26490         
26491          
26492        this.xtype = 'NavSimplebar';
26493         
26494         for(var i=0;i< children.length;i++) {
26495             
26496             this.buttons.add(this.addxtypeChild(children[i]));
26497             
26498         }
26499         
26500         editor.on('editorevent', this.updateToolbar, this);
26501     },
26502     onBtnClick : function(id)
26503     {
26504        this.editorcore.relayCmd(id);
26505        this.editorcore.focus();
26506     },
26507     
26508     /**
26509      * Protected method that will not generally be called directly. It triggers
26510      * a toolbar update by reading the markup state of the current selection in the editor.
26511      */
26512     updateToolbar: function(){
26513
26514         if(!this.editorcore.activated){
26515             this.editor.onFirstFocus(); // is this neeed?
26516             return;
26517         }
26518
26519         var btns = this.buttons; 
26520         var doc = this.editorcore.doc;
26521         btns.get('bold').setActive(doc.queryCommandState('bold'));
26522         btns.get('italic').setActive(doc.queryCommandState('italic'));
26523         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26524         
26525         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26526         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26527         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26528         
26529         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26530         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26531          /*
26532         
26533         var ans = this.editorcore.getAllAncestors();
26534         if (this.formatCombo) {
26535             
26536             
26537             var store = this.formatCombo.store;
26538             this.formatCombo.setValue("");
26539             for (var i =0; i < ans.length;i++) {
26540                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26541                     // select it..
26542                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26543                     break;
26544                 }
26545             }
26546         }
26547         
26548         
26549         
26550         // hides menus... - so this cant be on a menu...
26551         Roo.bootstrap.MenuMgr.hideAll();
26552         */
26553         Roo.bootstrap.MenuMgr.hideAll();
26554         //this.editorsyncValue();
26555     },
26556     onFirstFocus: function() {
26557         this.buttons.each(function(item){
26558            item.enable();
26559         });
26560     },
26561     toggleSourceEdit : function(sourceEditMode){
26562         
26563           
26564         if(sourceEditMode){
26565             Roo.log("disabling buttons");
26566            this.buttons.each( function(item){
26567                 if(item.cmd != 'pencil'){
26568                     item.disable();
26569                 }
26570             });
26571           
26572         }else{
26573             Roo.log("enabling buttons");
26574             if(this.editorcore.initialized){
26575                 this.buttons.each( function(item){
26576                     item.enable();
26577                 });
26578             }
26579             
26580         }
26581         Roo.log("calling toggole on editor");
26582         // tell the editor that it's been pressed..
26583         this.editor.toggleSourceEdit(sourceEditMode);
26584        
26585     }
26586 });
26587
26588
26589
26590
26591  
26592 /*
26593  * - LGPL
26594  */
26595
26596 /**
26597  * @class Roo.bootstrap.Markdown
26598  * @extends Roo.bootstrap.TextArea
26599  * Bootstrap Showdown editable area
26600  * @cfg {string} content
26601  * 
26602  * @constructor
26603  * Create a new Showdown
26604  */
26605
26606 Roo.bootstrap.Markdown = function(config){
26607     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26608    
26609 };
26610
26611 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26612     
26613     editing :false,
26614     
26615     initEvents : function()
26616     {
26617         
26618         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26619         this.markdownEl = this.el.createChild({
26620             cls : 'roo-markdown-area'
26621         });
26622         this.inputEl().addClass('d-none');
26623         if (this.getValue() == '') {
26624             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26625             
26626         } else {
26627             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26628         }
26629         this.markdownEl.on('click', this.toggleTextEdit, this);
26630         this.on('blur', this.toggleTextEdit, this);
26631         this.on('specialkey', this.resizeTextArea, this);
26632     },
26633     
26634     toggleTextEdit : function()
26635     {
26636         var sh = this.markdownEl.getHeight();
26637         this.inputEl().addClass('d-none');
26638         this.markdownEl.addClass('d-none');
26639         if (!this.editing) {
26640             // show editor?
26641             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26642             this.inputEl().removeClass('d-none');
26643             this.inputEl().focus();
26644             this.editing = true;
26645             return;
26646         }
26647         // show showdown...
26648         this.updateMarkdown();
26649         this.markdownEl.removeClass('d-none');
26650         this.editing = false;
26651         return;
26652     },
26653     updateMarkdown : function()
26654     {
26655         if (this.getValue() == '') {
26656             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26657             return;
26658         }
26659  
26660         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26661     },
26662     
26663     resizeTextArea: function () {
26664         
26665         var sh = 100;
26666         Roo.log([sh, this.getValue().split("\n").length * 30]);
26667         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26668     },
26669     setValue : function(val)
26670     {
26671         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26672         if (!this.editing) {
26673             this.updateMarkdown();
26674         }
26675         
26676     },
26677     focus : function()
26678     {
26679         if (!this.editing) {
26680             this.toggleTextEdit();
26681         }
26682         
26683     }
26684
26685
26686 });
26687 /**
26688  * @class Roo.bootstrap.Table.AbstractSelectionModel
26689  * @extends Roo.util.Observable
26690  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26691  * implemented by descendant classes.  This class should not be directly instantiated.
26692  * @constructor
26693  */
26694 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26695     this.locked = false;
26696     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26697 };
26698
26699
26700 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26701     /** @ignore Called by the grid automatically. Do not call directly. */
26702     init : function(grid){
26703         this.grid = grid;
26704         this.initEvents();
26705     },
26706
26707     /**
26708      * Locks the selections.
26709      */
26710     lock : function(){
26711         this.locked = true;
26712     },
26713
26714     /**
26715      * Unlocks the selections.
26716      */
26717     unlock : function(){
26718         this.locked = false;
26719     },
26720
26721     /**
26722      * Returns true if the selections are locked.
26723      * @return {Boolean}
26724      */
26725     isLocked : function(){
26726         return this.locked;
26727     },
26728     
26729     
26730     initEvents : function ()
26731     {
26732         
26733     }
26734 });
26735 /**
26736  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26737  * @class Roo.bootstrap.Table.RowSelectionModel
26738  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26739  * It supports multiple selections and keyboard selection/navigation. 
26740  * @constructor
26741  * @param {Object} config
26742  */
26743
26744 Roo.bootstrap.Table.RowSelectionModel = function(config){
26745     Roo.apply(this, config);
26746     this.selections = new Roo.util.MixedCollection(false, function(o){
26747         return o.id;
26748     });
26749
26750     this.last = false;
26751     this.lastActive = false;
26752
26753     this.addEvents({
26754         /**
26755              * @event selectionchange
26756              * Fires when the selection changes
26757              * @param {SelectionModel} this
26758              */
26759             "selectionchange" : true,
26760         /**
26761              * @event afterselectionchange
26762              * Fires after the selection changes (eg. by key press or clicking)
26763              * @param {SelectionModel} this
26764              */
26765             "afterselectionchange" : true,
26766         /**
26767              * @event beforerowselect
26768              * Fires when a row is selected being selected, return false to cancel.
26769              * @param {SelectionModel} this
26770              * @param {Number} rowIndex The selected index
26771              * @param {Boolean} keepExisting False if other selections will be cleared
26772              */
26773             "beforerowselect" : true,
26774         /**
26775              * @event rowselect
26776              * Fires when a row is selected.
26777              * @param {SelectionModel} this
26778              * @param {Number} rowIndex The selected index
26779              * @param {Roo.data.Record} r The record
26780              */
26781             "rowselect" : true,
26782         /**
26783              * @event rowdeselect
26784              * Fires when a row is deselected.
26785              * @param {SelectionModel} this
26786              * @param {Number} rowIndex The selected index
26787              */
26788         "rowdeselect" : true
26789     });
26790     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26791     this.locked = false;
26792  };
26793
26794 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26795     /**
26796      * @cfg {Boolean} singleSelect
26797      * True to allow selection of only one row at a time (defaults to false)
26798      */
26799     singleSelect : false,
26800
26801     // private
26802     initEvents : function()
26803     {
26804
26805         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26806         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26807         //}else{ // allow click to work like normal
26808          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26809         //}
26810         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26811         this.grid.on("rowclick", this.handleMouseDown, this);
26812         
26813         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26814             "up" : function(e){
26815                 if(!e.shiftKey){
26816                     this.selectPrevious(e.shiftKey);
26817                 }else if(this.last !== false && this.lastActive !== false){
26818                     var last = this.last;
26819                     this.selectRange(this.last,  this.lastActive-1);
26820                     this.grid.getView().focusRow(this.lastActive);
26821                     if(last !== false){
26822                         this.last = last;
26823                     }
26824                 }else{
26825                     this.selectFirstRow();
26826                 }
26827                 this.fireEvent("afterselectionchange", this);
26828             },
26829             "down" : function(e){
26830                 if(!e.shiftKey){
26831                     this.selectNext(e.shiftKey);
26832                 }else if(this.last !== false && this.lastActive !== false){
26833                     var last = this.last;
26834                     this.selectRange(this.last,  this.lastActive+1);
26835                     this.grid.getView().focusRow(this.lastActive);
26836                     if(last !== false){
26837                         this.last = last;
26838                     }
26839                 }else{
26840                     this.selectFirstRow();
26841                 }
26842                 this.fireEvent("afterselectionchange", this);
26843             },
26844             scope: this
26845         });
26846         this.grid.store.on('load', function(){
26847             this.selections.clear();
26848         },this);
26849         /*
26850         var view = this.grid.view;
26851         view.on("refresh", this.onRefresh, this);
26852         view.on("rowupdated", this.onRowUpdated, this);
26853         view.on("rowremoved", this.onRemove, this);
26854         */
26855     },
26856
26857     // private
26858     onRefresh : function()
26859     {
26860         var ds = this.grid.store, i, v = this.grid.view;
26861         var s = this.selections;
26862         s.each(function(r){
26863             if((i = ds.indexOfId(r.id)) != -1){
26864                 v.onRowSelect(i);
26865             }else{
26866                 s.remove(r);
26867             }
26868         });
26869     },
26870
26871     // private
26872     onRemove : function(v, index, r){
26873         this.selections.remove(r);
26874     },
26875
26876     // private
26877     onRowUpdated : function(v, index, r){
26878         if(this.isSelected(r)){
26879             v.onRowSelect(index);
26880         }
26881     },
26882
26883     /**
26884      * Select records.
26885      * @param {Array} records The records to select
26886      * @param {Boolean} keepExisting (optional) True to keep existing selections
26887      */
26888     selectRecords : function(records, keepExisting)
26889     {
26890         if(!keepExisting){
26891             this.clearSelections();
26892         }
26893             var ds = this.grid.store;
26894         for(var i = 0, len = records.length; i < len; i++){
26895             this.selectRow(ds.indexOf(records[i]), true);
26896         }
26897     },
26898
26899     /**
26900      * Gets the number of selected rows.
26901      * @return {Number}
26902      */
26903     getCount : function(){
26904         return this.selections.length;
26905     },
26906
26907     /**
26908      * Selects the first row in the grid.
26909      */
26910     selectFirstRow : function(){
26911         this.selectRow(0);
26912     },
26913
26914     /**
26915      * Select the last row.
26916      * @param {Boolean} keepExisting (optional) True to keep existing selections
26917      */
26918     selectLastRow : function(keepExisting){
26919         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26920         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26921     },
26922
26923     /**
26924      * Selects the row immediately following the last selected row.
26925      * @param {Boolean} keepExisting (optional) True to keep existing selections
26926      */
26927     selectNext : function(keepExisting)
26928     {
26929             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26930             this.selectRow(this.last+1, keepExisting);
26931             this.grid.getView().focusRow(this.last);
26932         }
26933     },
26934
26935     /**
26936      * Selects the row that precedes the last selected row.
26937      * @param {Boolean} keepExisting (optional) True to keep existing selections
26938      */
26939     selectPrevious : function(keepExisting){
26940         if(this.last){
26941             this.selectRow(this.last-1, keepExisting);
26942             this.grid.getView().focusRow(this.last);
26943         }
26944     },
26945
26946     /**
26947      * Returns the selected records
26948      * @return {Array} Array of selected records
26949      */
26950     getSelections : function(){
26951         return [].concat(this.selections.items);
26952     },
26953
26954     /**
26955      * Returns the first selected record.
26956      * @return {Record}
26957      */
26958     getSelected : function(){
26959         return this.selections.itemAt(0);
26960     },
26961
26962
26963     /**
26964      * Clears all selections.
26965      */
26966     clearSelections : function(fast)
26967     {
26968         if(this.locked) {
26969             return;
26970         }
26971         if(fast !== true){
26972                 var ds = this.grid.store;
26973             var s = this.selections;
26974             s.each(function(r){
26975                 this.deselectRow(ds.indexOfId(r.id));
26976             }, this);
26977             s.clear();
26978         }else{
26979             this.selections.clear();
26980         }
26981         this.last = false;
26982     },
26983
26984
26985     /**
26986      * Selects all rows.
26987      */
26988     selectAll : function(){
26989         if(this.locked) {
26990             return;
26991         }
26992         this.selections.clear();
26993         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26994             this.selectRow(i, true);
26995         }
26996     },
26997
26998     /**
26999      * Returns True if there is a selection.
27000      * @return {Boolean}
27001      */
27002     hasSelection : function(){
27003         return this.selections.length > 0;
27004     },
27005
27006     /**
27007      * Returns True if the specified row is selected.
27008      * @param {Number/Record} record The record or index of the record to check
27009      * @return {Boolean}
27010      */
27011     isSelected : function(index){
27012             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27013         return (r && this.selections.key(r.id) ? true : false);
27014     },
27015
27016     /**
27017      * Returns True if the specified record id is selected.
27018      * @param {String} id The id of record to check
27019      * @return {Boolean}
27020      */
27021     isIdSelected : function(id){
27022         return (this.selections.key(id) ? true : false);
27023     },
27024
27025
27026     // private
27027     handleMouseDBClick : function(e, t){
27028         
27029     },
27030     // private
27031     handleMouseDown : function(e, t)
27032     {
27033             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27034         if(this.isLocked() || rowIndex < 0 ){
27035             return;
27036         };
27037         if(e.shiftKey && this.last !== false){
27038             var last = this.last;
27039             this.selectRange(last, rowIndex, e.ctrlKey);
27040             this.last = last; // reset the last
27041             t.focus();
27042     
27043         }else{
27044             var isSelected = this.isSelected(rowIndex);
27045             //Roo.log("select row:" + rowIndex);
27046             if(isSelected){
27047                 this.deselectRow(rowIndex);
27048             } else {
27049                         this.selectRow(rowIndex, true);
27050             }
27051     
27052             /*
27053                 if(e.button !== 0 && isSelected){
27054                 alert('rowIndex 2: ' + rowIndex);
27055                     view.focusRow(rowIndex);
27056                 }else if(e.ctrlKey && isSelected){
27057                     this.deselectRow(rowIndex);
27058                 }else if(!isSelected){
27059                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27060                     view.focusRow(rowIndex);
27061                 }
27062             */
27063         }
27064         this.fireEvent("afterselectionchange", this);
27065     },
27066     // private
27067     handleDragableRowClick :  function(grid, rowIndex, e) 
27068     {
27069         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27070             this.selectRow(rowIndex, false);
27071             grid.view.focusRow(rowIndex);
27072              this.fireEvent("afterselectionchange", this);
27073         }
27074     },
27075     
27076     /**
27077      * Selects multiple rows.
27078      * @param {Array} rows Array of the indexes of the row to select
27079      * @param {Boolean} keepExisting (optional) True to keep existing selections
27080      */
27081     selectRows : function(rows, keepExisting){
27082         if(!keepExisting){
27083             this.clearSelections();
27084         }
27085         for(var i = 0, len = rows.length; i < len; i++){
27086             this.selectRow(rows[i], true);
27087         }
27088     },
27089
27090     /**
27091      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27092      * @param {Number} startRow The index of the first row in the range
27093      * @param {Number} endRow The index of the last row in the range
27094      * @param {Boolean} keepExisting (optional) True to retain existing selections
27095      */
27096     selectRange : function(startRow, endRow, keepExisting){
27097         if(this.locked) {
27098             return;
27099         }
27100         if(!keepExisting){
27101             this.clearSelections();
27102         }
27103         if(startRow <= endRow){
27104             for(var i = startRow; i <= endRow; i++){
27105                 this.selectRow(i, true);
27106             }
27107         }else{
27108             for(var i = startRow; i >= endRow; i--){
27109                 this.selectRow(i, true);
27110             }
27111         }
27112     },
27113
27114     /**
27115      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27116      * @param {Number} startRow The index of the first row in the range
27117      * @param {Number} endRow The index of the last row in the range
27118      */
27119     deselectRange : function(startRow, endRow, preventViewNotify){
27120         if(this.locked) {
27121             return;
27122         }
27123         for(var i = startRow; i <= endRow; i++){
27124             this.deselectRow(i, preventViewNotify);
27125         }
27126     },
27127
27128     /**
27129      * Selects a row.
27130      * @param {Number} row The index of the row to select
27131      * @param {Boolean} keepExisting (optional) True to keep existing selections
27132      */
27133     selectRow : function(index, keepExisting, preventViewNotify)
27134     {
27135             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27136             return;
27137         }
27138         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27139             if(!keepExisting || this.singleSelect){
27140                 this.clearSelections();
27141             }
27142             
27143             var r = this.grid.store.getAt(index);
27144             //console.log('selectRow - record id :' + r.id);
27145             
27146             this.selections.add(r);
27147             this.last = this.lastActive = index;
27148             if(!preventViewNotify){
27149                 var proxy = new Roo.Element(
27150                                 this.grid.getRowDom(index)
27151                 );
27152                 proxy.addClass('bg-info info');
27153             }
27154             this.fireEvent("rowselect", this, index, r);
27155             this.fireEvent("selectionchange", this);
27156         }
27157     },
27158
27159     /**
27160      * Deselects a row.
27161      * @param {Number} row The index of the row to deselect
27162      */
27163     deselectRow : function(index, preventViewNotify)
27164     {
27165         if(this.locked) {
27166             return;
27167         }
27168         if(this.last == index){
27169             this.last = false;
27170         }
27171         if(this.lastActive == index){
27172             this.lastActive = false;
27173         }
27174         
27175         var r = this.grid.store.getAt(index);
27176         if (!r) {
27177             return;
27178         }
27179         
27180         this.selections.remove(r);
27181         //.console.log('deselectRow - record id :' + r.id);
27182         if(!preventViewNotify){
27183         
27184             var proxy = new Roo.Element(
27185                 this.grid.getRowDom(index)
27186             );
27187             proxy.removeClass('bg-info info');
27188         }
27189         this.fireEvent("rowdeselect", this, index);
27190         this.fireEvent("selectionchange", this);
27191     },
27192
27193     // private
27194     restoreLast : function(){
27195         if(this._last){
27196             this.last = this._last;
27197         }
27198     },
27199
27200     // private
27201     acceptsNav : function(row, col, cm){
27202         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27203     },
27204
27205     // private
27206     onEditorKey : function(field, e){
27207         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27208         if(k == e.TAB){
27209             e.stopEvent();
27210             ed.completeEdit();
27211             if(e.shiftKey){
27212                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27213             }else{
27214                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27215             }
27216         }else if(k == e.ENTER && !e.ctrlKey){
27217             e.stopEvent();
27218             ed.completeEdit();
27219             if(e.shiftKey){
27220                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27221             }else{
27222                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27223             }
27224         }else if(k == e.ESC){
27225             ed.cancelEdit();
27226         }
27227         if(newCell){
27228             g.startEditing(newCell[0], newCell[1]);
27229         }
27230     }
27231 });
27232 /*
27233  * Based on:
27234  * Ext JS Library 1.1.1
27235  * Copyright(c) 2006-2007, Ext JS, LLC.
27236  *
27237  * Originally Released Under LGPL - original licence link has changed is not relivant.
27238  *
27239  * Fork - LGPL
27240  * <script type="text/javascript">
27241  */
27242  
27243 /**
27244  * @class Roo.bootstrap.PagingToolbar
27245  * @extends Roo.bootstrap.NavSimplebar
27246  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27247  * @constructor
27248  * Create a new PagingToolbar
27249  * @param {Object} config The config object
27250  * @param {Roo.data.Store} store
27251  */
27252 Roo.bootstrap.PagingToolbar = function(config)
27253 {
27254     // old args format still supported... - xtype is prefered..
27255         // created from xtype...
27256     
27257     this.ds = config.dataSource;
27258     
27259     if (config.store && !this.ds) {
27260         this.store= Roo.factory(config.store, Roo.data);
27261         this.ds = this.store;
27262         this.ds.xmodule = this.xmodule || false;
27263     }
27264     
27265     this.toolbarItems = [];
27266     if (config.items) {
27267         this.toolbarItems = config.items;
27268     }
27269     
27270     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27271     
27272     this.cursor = 0;
27273     
27274     if (this.ds) { 
27275         this.bind(this.ds);
27276     }
27277     
27278     if (Roo.bootstrap.version == 4) {
27279         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27280     } else {
27281         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27282     }
27283     
27284 };
27285
27286 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27287     /**
27288      * @cfg {Roo.data.Store} dataSource
27289      * The underlying data store providing the paged data
27290      */
27291     /**
27292      * @cfg {String/HTMLElement/Element} container
27293      * container The id or element that will contain the toolbar
27294      */
27295     /**
27296      * @cfg {Boolean} displayInfo
27297      * True to display the displayMsg (defaults to false)
27298      */
27299     /**
27300      * @cfg {Number} pageSize
27301      * The number of records to display per page (defaults to 20)
27302      */
27303     pageSize: 20,
27304     /**
27305      * @cfg {String} displayMsg
27306      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27307      */
27308     displayMsg : 'Displaying {0} - {1} of {2}',
27309     /**
27310      * @cfg {String} emptyMsg
27311      * The message to display when no records are found (defaults to "No data to display")
27312      */
27313     emptyMsg : 'No data to display',
27314     /**
27315      * Customizable piece of the default paging text (defaults to "Page")
27316      * @type String
27317      */
27318     beforePageText : "Page",
27319     /**
27320      * Customizable piece of the default paging text (defaults to "of %0")
27321      * @type String
27322      */
27323     afterPageText : "of {0}",
27324     /**
27325      * Customizable piece of the default paging text (defaults to "First Page")
27326      * @type String
27327      */
27328     firstText : "First Page",
27329     /**
27330      * Customizable piece of the default paging text (defaults to "Previous Page")
27331      * @type String
27332      */
27333     prevText : "Previous Page",
27334     /**
27335      * Customizable piece of the default paging text (defaults to "Next Page")
27336      * @type String
27337      */
27338     nextText : "Next Page",
27339     /**
27340      * Customizable piece of the default paging text (defaults to "Last Page")
27341      * @type String
27342      */
27343     lastText : "Last Page",
27344     /**
27345      * Customizable piece of the default paging text (defaults to "Refresh")
27346      * @type String
27347      */
27348     refreshText : "Refresh",
27349
27350     buttons : false,
27351     // private
27352     onRender : function(ct, position) 
27353     {
27354         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27355         this.navgroup.parentId = this.id;
27356         this.navgroup.onRender(this.el, null);
27357         // add the buttons to the navgroup
27358         
27359         if(this.displayInfo){
27360             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27361             this.displayEl = this.el.select('.x-paging-info', true).first();
27362 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27363 //            this.displayEl = navel.el.select('span',true).first();
27364         }
27365         
27366         var _this = this;
27367         
27368         if(this.buttons){
27369             Roo.each(_this.buttons, function(e){ // this might need to use render????
27370                Roo.factory(e).render(_this.el);
27371             });
27372         }
27373             
27374         Roo.each(_this.toolbarItems, function(e) {
27375             _this.navgroup.addItem(e);
27376         });
27377         
27378         
27379         this.first = this.navgroup.addItem({
27380             tooltip: this.firstText,
27381             cls: "prev btn-outline-secondary",
27382             html : ' <i class="fa fa-step-backward"></i>',
27383             disabled: true,
27384             preventDefault: true,
27385             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27386         });
27387         
27388         this.prev =  this.navgroup.addItem({
27389             tooltip: this.prevText,
27390             cls: "prev btn-outline-secondary",
27391             html : ' <i class="fa fa-backward"></i>',
27392             disabled: true,
27393             preventDefault: true,
27394             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27395         });
27396     //this.addSeparator();
27397         
27398         
27399         var field = this.navgroup.addItem( {
27400             tagtype : 'span',
27401             cls : 'x-paging-position  btn-outline-secondary',
27402              disabled: true,
27403             html : this.beforePageText  +
27404                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27405                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27406          } ); //?? escaped?
27407         
27408         this.field = field.el.select('input', true).first();
27409         this.field.on("keydown", this.onPagingKeydown, this);
27410         this.field.on("focus", function(){this.dom.select();});
27411     
27412     
27413         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27414         //this.field.setHeight(18);
27415         //this.addSeparator();
27416         this.next = this.navgroup.addItem({
27417             tooltip: this.nextText,
27418             cls: "next btn-outline-secondary",
27419             html : ' <i class="fa fa-forward"></i>',
27420             disabled: true,
27421             preventDefault: true,
27422             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27423         });
27424         this.last = this.navgroup.addItem({
27425             tooltip: this.lastText,
27426             html : ' <i class="fa fa-step-forward"></i>',
27427             cls: "next btn-outline-secondary",
27428             disabled: true,
27429             preventDefault: true,
27430             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27431         });
27432     //this.addSeparator();
27433         this.loading = this.navgroup.addItem({
27434             tooltip: this.refreshText,
27435             cls: "btn-outline-secondary",
27436             html : ' <i class="fa fa-refresh"></i>',
27437             preventDefault: true,
27438             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27439         });
27440         
27441     },
27442
27443     // private
27444     updateInfo : function(){
27445         if(this.displayEl){
27446             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27447             var msg = count == 0 ?
27448                 this.emptyMsg :
27449                 String.format(
27450                     this.displayMsg,
27451                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27452                 );
27453             this.displayEl.update(msg);
27454         }
27455     },
27456
27457     // private
27458     onLoad : function(ds, r, o)
27459     {
27460         this.cursor = o.params && o.params.start ? o.params.start : 0;
27461         
27462         var d = this.getPageData(),
27463             ap = d.activePage,
27464             ps = d.pages;
27465         
27466         
27467         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27468         this.field.dom.value = ap;
27469         this.first.setDisabled(ap == 1);
27470         this.prev.setDisabled(ap == 1);
27471         this.next.setDisabled(ap == ps);
27472         this.last.setDisabled(ap == ps);
27473         this.loading.enable();
27474         this.updateInfo();
27475     },
27476
27477     // private
27478     getPageData : function(){
27479         var total = this.ds.getTotalCount();
27480         return {
27481             total : total,
27482             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27483             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27484         };
27485     },
27486
27487     // private
27488     onLoadError : function(){
27489         this.loading.enable();
27490     },
27491
27492     // private
27493     onPagingKeydown : function(e){
27494         var k = e.getKey();
27495         var d = this.getPageData();
27496         if(k == e.RETURN){
27497             var v = this.field.dom.value, pageNum;
27498             if(!v || isNaN(pageNum = parseInt(v, 10))){
27499                 this.field.dom.value = d.activePage;
27500                 return;
27501             }
27502             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27503             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27504             e.stopEvent();
27505         }
27506         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))
27507         {
27508           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27509           this.field.dom.value = pageNum;
27510           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27511           e.stopEvent();
27512         }
27513         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27514         {
27515           var v = this.field.dom.value, pageNum; 
27516           var increment = (e.shiftKey) ? 10 : 1;
27517           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27518                 increment *= -1;
27519           }
27520           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27521             this.field.dom.value = d.activePage;
27522             return;
27523           }
27524           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27525           {
27526             this.field.dom.value = parseInt(v, 10) + increment;
27527             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27528             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27529           }
27530           e.stopEvent();
27531         }
27532     },
27533
27534     // private
27535     beforeLoad : function(){
27536         if(this.loading){
27537             this.loading.disable();
27538         }
27539     },
27540
27541     // private
27542     onClick : function(which){
27543         
27544         var ds = this.ds;
27545         if (!ds) {
27546             return;
27547         }
27548         
27549         switch(which){
27550             case "first":
27551                 ds.load({params:{start: 0, limit: this.pageSize}});
27552             break;
27553             case "prev":
27554                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27555             break;
27556             case "next":
27557                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27558             break;
27559             case "last":
27560                 var total = ds.getTotalCount();
27561                 var extra = total % this.pageSize;
27562                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27563                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27564             break;
27565             case "refresh":
27566                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27567             break;
27568         }
27569     },
27570
27571     /**
27572      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27573      * @param {Roo.data.Store} store The data store to unbind
27574      */
27575     unbind : function(ds){
27576         ds.un("beforeload", this.beforeLoad, this);
27577         ds.un("load", this.onLoad, this);
27578         ds.un("loadexception", this.onLoadError, this);
27579         ds.un("remove", this.updateInfo, this);
27580         ds.un("add", this.updateInfo, this);
27581         this.ds = undefined;
27582     },
27583
27584     /**
27585      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27586      * @param {Roo.data.Store} store The data store to bind
27587      */
27588     bind : function(ds){
27589         ds.on("beforeload", this.beforeLoad, this);
27590         ds.on("load", this.onLoad, this);
27591         ds.on("loadexception", this.onLoadError, this);
27592         ds.on("remove", this.updateInfo, this);
27593         ds.on("add", this.updateInfo, this);
27594         this.ds = ds;
27595     }
27596 });/*
27597  * - LGPL
27598  *
27599  * element
27600  * 
27601  */
27602
27603 /**
27604  * @class Roo.bootstrap.MessageBar
27605  * @extends Roo.bootstrap.Component
27606  * Bootstrap MessageBar class
27607  * @cfg {String} html contents of the MessageBar
27608  * @cfg {String} weight (info | success | warning | danger) default info
27609  * @cfg {String} beforeClass insert the bar before the given class
27610  * @cfg {Boolean} closable (true | false) default false
27611  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27612  * 
27613  * @constructor
27614  * Create a new Element
27615  * @param {Object} config The config object
27616  */
27617
27618 Roo.bootstrap.MessageBar = function(config){
27619     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27620 };
27621
27622 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27623     
27624     html: '',
27625     weight: 'info',
27626     closable: false,
27627     fixed: false,
27628     beforeClass: 'bootstrap-sticky-wrap',
27629     
27630     getAutoCreate : function(){
27631         
27632         var cfg = {
27633             tag: 'div',
27634             cls: 'alert alert-dismissable alert-' + this.weight,
27635             cn: [
27636                 {
27637                     tag: 'span',
27638                     cls: 'message',
27639                     html: this.html || ''
27640                 }
27641             ]
27642         };
27643         
27644         if(this.fixed){
27645             cfg.cls += ' alert-messages-fixed';
27646         }
27647         
27648         if(this.closable){
27649             cfg.cn.push({
27650                 tag: 'button',
27651                 cls: 'close',
27652                 html: 'x'
27653             });
27654         }
27655         
27656         return cfg;
27657     },
27658     
27659     onRender : function(ct, position)
27660     {
27661         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27662         
27663         if(!this.el){
27664             var cfg = Roo.apply({},  this.getAutoCreate());
27665             cfg.id = Roo.id();
27666             
27667             if (this.cls) {
27668                 cfg.cls += ' ' + this.cls;
27669             }
27670             if (this.style) {
27671                 cfg.style = this.style;
27672             }
27673             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27674             
27675             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27676         }
27677         
27678         this.el.select('>button.close').on('click', this.hide, this);
27679         
27680     },
27681     
27682     show : function()
27683     {
27684         if (!this.rendered) {
27685             this.render();
27686         }
27687         
27688         this.el.show();
27689         
27690         this.fireEvent('show', this);
27691         
27692     },
27693     
27694     hide : function()
27695     {
27696         if (!this.rendered) {
27697             this.render();
27698         }
27699         
27700         this.el.hide();
27701         
27702         this.fireEvent('hide', this);
27703     },
27704     
27705     update : function()
27706     {
27707 //        var e = this.el.dom.firstChild;
27708 //        
27709 //        if(this.closable){
27710 //            e = e.nextSibling;
27711 //        }
27712 //        
27713 //        e.data = this.html || '';
27714
27715         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27716     }
27717    
27718 });
27719
27720  
27721
27722      /*
27723  * - LGPL
27724  *
27725  * Graph
27726  * 
27727  */
27728
27729
27730 /**
27731  * @class Roo.bootstrap.Graph
27732  * @extends Roo.bootstrap.Component
27733  * Bootstrap Graph class
27734 > Prameters
27735  -sm {number} sm 4
27736  -md {number} md 5
27737  @cfg {String} graphtype  bar | vbar | pie
27738  @cfg {number} g_x coodinator | centre x (pie)
27739  @cfg {number} g_y coodinator | centre y (pie)
27740  @cfg {number} g_r radius (pie)
27741  @cfg {number} g_height height of the chart (respected by all elements in the set)
27742  @cfg {number} g_width width of the chart (respected by all elements in the set)
27743  @cfg {Object} title The title of the chart
27744     
27745  -{Array}  values
27746  -opts (object) options for the chart 
27747      o {
27748      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27749      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27750      o vgutter (number)
27751      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.
27752      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27753      o to
27754      o stretch (boolean)
27755      o }
27756  -opts (object) options for the pie
27757      o{
27758      o cut
27759      o startAngle (number)
27760      o endAngle (number)
27761      } 
27762  *
27763  * @constructor
27764  * Create a new Input
27765  * @param {Object} config The config object
27766  */
27767
27768 Roo.bootstrap.Graph = function(config){
27769     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27770     
27771     this.addEvents({
27772         // img events
27773         /**
27774          * @event click
27775          * The img click event for the img.
27776          * @param {Roo.EventObject} e
27777          */
27778         "click" : true
27779     });
27780 };
27781
27782 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27783     
27784     sm: 4,
27785     md: 5,
27786     graphtype: 'bar',
27787     g_height: 250,
27788     g_width: 400,
27789     g_x: 50,
27790     g_y: 50,
27791     g_r: 30,
27792     opts:{
27793         //g_colors: this.colors,
27794         g_type: 'soft',
27795         g_gutter: '20%'
27796
27797     },
27798     title : false,
27799
27800     getAutoCreate : function(){
27801         
27802         var cfg = {
27803             tag: 'div',
27804             html : null
27805         };
27806         
27807         
27808         return  cfg;
27809     },
27810
27811     onRender : function(ct,position){
27812         
27813         
27814         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27815         
27816         if (typeof(Raphael) == 'undefined') {
27817             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27818             return;
27819         }
27820         
27821         this.raphael = Raphael(this.el.dom);
27822         
27823                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27824                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27825                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27826                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27827                 /*
27828                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27829                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27830                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27831                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27832                 
27833                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27834                 r.barchart(330, 10, 300, 220, data1);
27835                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27836                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27837                 */
27838                 
27839                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27840                 // r.barchart(30, 30, 560, 250,  xdata, {
27841                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27842                 //     axis : "0 0 1 1",
27843                 //     axisxlabels :  xdata
27844                 //     //yvalues : cols,
27845                    
27846                 // });
27847 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27848 //        
27849 //        this.load(null,xdata,{
27850 //                axis : "0 0 1 1",
27851 //                axisxlabels :  xdata
27852 //                });
27853
27854     },
27855
27856     load : function(graphtype,xdata,opts)
27857     {
27858         this.raphael.clear();
27859         if(!graphtype) {
27860             graphtype = this.graphtype;
27861         }
27862         if(!opts){
27863             opts = this.opts;
27864         }
27865         var r = this.raphael,
27866             fin = function () {
27867                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27868             },
27869             fout = function () {
27870                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27871             },
27872             pfin = function() {
27873                 this.sector.stop();
27874                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27875
27876                 if (this.label) {
27877                     this.label[0].stop();
27878                     this.label[0].attr({ r: 7.5 });
27879                     this.label[1].attr({ "font-weight": 800 });
27880                 }
27881             },
27882             pfout = function() {
27883                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27884
27885                 if (this.label) {
27886                     this.label[0].animate({ r: 5 }, 500, "bounce");
27887                     this.label[1].attr({ "font-weight": 400 });
27888                 }
27889             };
27890
27891         switch(graphtype){
27892             case 'bar':
27893                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27894                 break;
27895             case 'hbar':
27896                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27897                 break;
27898             case 'pie':
27899 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27900 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27901 //            
27902                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27903                 
27904                 break;
27905
27906         }
27907         
27908         if(this.title){
27909             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27910         }
27911         
27912     },
27913     
27914     setTitle: function(o)
27915     {
27916         this.title = o;
27917     },
27918     
27919     initEvents: function() {
27920         
27921         if(!this.href){
27922             this.el.on('click', this.onClick, this);
27923         }
27924     },
27925     
27926     onClick : function(e)
27927     {
27928         Roo.log('img onclick');
27929         this.fireEvent('click', this, e);
27930     }
27931    
27932 });
27933
27934  
27935 /*
27936  * - LGPL
27937  *
27938  * numberBox
27939  * 
27940  */
27941 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27942
27943 /**
27944  * @class Roo.bootstrap.dash.NumberBox
27945  * @extends Roo.bootstrap.Component
27946  * Bootstrap NumberBox class
27947  * @cfg {String} headline Box headline
27948  * @cfg {String} content Box content
27949  * @cfg {String} icon Box icon
27950  * @cfg {String} footer Footer text
27951  * @cfg {String} fhref Footer href
27952  * 
27953  * @constructor
27954  * Create a new NumberBox
27955  * @param {Object} config The config object
27956  */
27957
27958
27959 Roo.bootstrap.dash.NumberBox = function(config){
27960     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27961     
27962 };
27963
27964 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27965     
27966     headline : '',
27967     content : '',
27968     icon : '',
27969     footer : '',
27970     fhref : '',
27971     ficon : '',
27972     
27973     getAutoCreate : function(){
27974         
27975         var cfg = {
27976             tag : 'div',
27977             cls : 'small-box ',
27978             cn : [
27979                 {
27980                     tag : 'div',
27981                     cls : 'inner',
27982                     cn :[
27983                         {
27984                             tag : 'h3',
27985                             cls : 'roo-headline',
27986                             html : this.headline
27987                         },
27988                         {
27989                             tag : 'p',
27990                             cls : 'roo-content',
27991                             html : this.content
27992                         }
27993                     ]
27994                 }
27995             ]
27996         };
27997         
27998         if(this.icon){
27999             cfg.cn.push({
28000                 tag : 'div',
28001                 cls : 'icon',
28002                 cn :[
28003                     {
28004                         tag : 'i',
28005                         cls : 'ion ' + this.icon
28006                     }
28007                 ]
28008             });
28009         }
28010         
28011         if(this.footer){
28012             var footer = {
28013                 tag : 'a',
28014                 cls : 'small-box-footer',
28015                 href : this.fhref || '#',
28016                 html : this.footer
28017             };
28018             
28019             cfg.cn.push(footer);
28020             
28021         }
28022         
28023         return  cfg;
28024     },
28025
28026     onRender : function(ct,position){
28027         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28028
28029
28030        
28031                 
28032     },
28033
28034     setHeadline: function (value)
28035     {
28036         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28037     },
28038     
28039     setFooter: function (value, href)
28040     {
28041         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28042         
28043         if(href){
28044             this.el.select('a.small-box-footer',true).first().attr('href', href);
28045         }
28046         
28047     },
28048
28049     setContent: function (value)
28050     {
28051         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28052     },
28053
28054     initEvents: function() 
28055     {   
28056         
28057     }
28058     
28059 });
28060
28061  
28062 /*
28063  * - LGPL
28064  *
28065  * TabBox
28066  * 
28067  */
28068 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28069
28070 /**
28071  * @class Roo.bootstrap.dash.TabBox
28072  * @extends Roo.bootstrap.Component
28073  * Bootstrap TabBox class
28074  * @cfg {String} title Title of the TabBox
28075  * @cfg {String} icon Icon of the TabBox
28076  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28077  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28078  * 
28079  * @constructor
28080  * Create a new TabBox
28081  * @param {Object} config The config object
28082  */
28083
28084
28085 Roo.bootstrap.dash.TabBox = function(config){
28086     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28087     this.addEvents({
28088         // raw events
28089         /**
28090          * @event addpane
28091          * When a pane is added
28092          * @param {Roo.bootstrap.dash.TabPane} pane
28093          */
28094         "addpane" : true,
28095         /**
28096          * @event activatepane
28097          * When a pane is activated
28098          * @param {Roo.bootstrap.dash.TabPane} pane
28099          */
28100         "activatepane" : true
28101         
28102          
28103     });
28104     
28105     this.panes = [];
28106 };
28107
28108 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28109
28110     title : '',
28111     icon : false,
28112     showtabs : true,
28113     tabScrollable : false,
28114     
28115     getChildContainer : function()
28116     {
28117         return this.el.select('.tab-content', true).first();
28118     },
28119     
28120     getAutoCreate : function(){
28121         
28122         var header = {
28123             tag: 'li',
28124             cls: 'pull-left header',
28125             html: this.title,
28126             cn : []
28127         };
28128         
28129         if(this.icon){
28130             header.cn.push({
28131                 tag: 'i',
28132                 cls: 'fa ' + this.icon
28133             });
28134         }
28135         
28136         var h = {
28137             tag: 'ul',
28138             cls: 'nav nav-tabs pull-right',
28139             cn: [
28140                 header
28141             ]
28142         };
28143         
28144         if(this.tabScrollable){
28145             h = {
28146                 tag: 'div',
28147                 cls: 'tab-header',
28148                 cn: [
28149                     {
28150                         tag: 'ul',
28151                         cls: 'nav nav-tabs pull-right',
28152                         cn: [
28153                             header
28154                         ]
28155                     }
28156                 ]
28157             };
28158         }
28159         
28160         var cfg = {
28161             tag: 'div',
28162             cls: 'nav-tabs-custom',
28163             cn: [
28164                 h,
28165                 {
28166                     tag: 'div',
28167                     cls: 'tab-content no-padding',
28168                     cn: []
28169                 }
28170             ]
28171         };
28172
28173         return  cfg;
28174     },
28175     initEvents : function()
28176     {
28177         //Roo.log('add add pane handler');
28178         this.on('addpane', this.onAddPane, this);
28179     },
28180      /**
28181      * Updates the box title
28182      * @param {String} html to set the title to.
28183      */
28184     setTitle : function(value)
28185     {
28186         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28187     },
28188     onAddPane : function(pane)
28189     {
28190         this.panes.push(pane);
28191         //Roo.log('addpane');
28192         //Roo.log(pane);
28193         // tabs are rendere left to right..
28194         if(!this.showtabs){
28195             return;
28196         }
28197         
28198         var ctr = this.el.select('.nav-tabs', true).first();
28199          
28200          
28201         var existing = ctr.select('.nav-tab',true);
28202         var qty = existing.getCount();;
28203         
28204         
28205         var tab = ctr.createChild({
28206             tag : 'li',
28207             cls : 'nav-tab' + (qty ? '' : ' active'),
28208             cn : [
28209                 {
28210                     tag : 'a',
28211                     href:'#',
28212                     html : pane.title
28213                 }
28214             ]
28215         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28216         pane.tab = tab;
28217         
28218         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28219         if (!qty) {
28220             pane.el.addClass('active');
28221         }
28222         
28223                 
28224     },
28225     onTabClick : function(ev,un,ob,pane)
28226     {
28227         //Roo.log('tab - prev default');
28228         ev.preventDefault();
28229         
28230         
28231         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28232         pane.tab.addClass('active');
28233         //Roo.log(pane.title);
28234         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28235         // technically we should have a deactivate event.. but maybe add later.
28236         // and it should not de-activate the selected tab...
28237         this.fireEvent('activatepane', pane);
28238         pane.el.addClass('active');
28239         pane.fireEvent('activate');
28240         
28241         
28242     },
28243     
28244     getActivePane : function()
28245     {
28246         var r = false;
28247         Roo.each(this.panes, function(p) {
28248             if(p.el.hasClass('active')){
28249                 r = p;
28250                 return false;
28251             }
28252             
28253             return;
28254         });
28255         
28256         return r;
28257     }
28258     
28259     
28260 });
28261
28262  
28263 /*
28264  * - LGPL
28265  *
28266  * Tab pane
28267  * 
28268  */
28269 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28270 /**
28271  * @class Roo.bootstrap.TabPane
28272  * @extends Roo.bootstrap.Component
28273  * Bootstrap TabPane class
28274  * @cfg {Boolean} active (false | true) Default false
28275  * @cfg {String} title title of panel
28276
28277  * 
28278  * @constructor
28279  * Create a new TabPane
28280  * @param {Object} config The config object
28281  */
28282
28283 Roo.bootstrap.dash.TabPane = function(config){
28284     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28285     
28286     this.addEvents({
28287         // raw events
28288         /**
28289          * @event activate
28290          * When a pane is activated
28291          * @param {Roo.bootstrap.dash.TabPane} pane
28292          */
28293         "activate" : true
28294          
28295     });
28296 };
28297
28298 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28299     
28300     active : false,
28301     title : '',
28302     
28303     // the tabBox that this is attached to.
28304     tab : false,
28305      
28306     getAutoCreate : function() 
28307     {
28308         var cfg = {
28309             tag: 'div',
28310             cls: 'tab-pane'
28311         };
28312         
28313         if(this.active){
28314             cfg.cls += ' active';
28315         }
28316         
28317         return cfg;
28318     },
28319     initEvents  : function()
28320     {
28321         //Roo.log('trigger add pane handler');
28322         this.parent().fireEvent('addpane', this)
28323     },
28324     
28325      /**
28326      * Updates the tab title 
28327      * @param {String} html to set the title to.
28328      */
28329     setTitle: function(str)
28330     {
28331         if (!this.tab) {
28332             return;
28333         }
28334         this.title = str;
28335         this.tab.select('a', true).first().dom.innerHTML = str;
28336         
28337     }
28338     
28339     
28340     
28341 });
28342
28343  
28344
28345
28346  /*
28347  * - LGPL
28348  *
28349  * menu
28350  * 
28351  */
28352 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28353
28354 /**
28355  * @class Roo.bootstrap.menu.Menu
28356  * @extends Roo.bootstrap.Component
28357  * Bootstrap Menu class - container for Menu
28358  * @cfg {String} html Text of the menu
28359  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28360  * @cfg {String} icon Font awesome icon
28361  * @cfg {String} pos Menu align to (top | bottom) default bottom
28362  * 
28363  * 
28364  * @constructor
28365  * Create a new Menu
28366  * @param {Object} config The config object
28367  */
28368
28369
28370 Roo.bootstrap.menu.Menu = function(config){
28371     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28372     
28373     this.addEvents({
28374         /**
28375          * @event beforeshow
28376          * Fires before this menu is displayed
28377          * @param {Roo.bootstrap.menu.Menu} this
28378          */
28379         beforeshow : true,
28380         /**
28381          * @event beforehide
28382          * Fires before this menu is hidden
28383          * @param {Roo.bootstrap.menu.Menu} this
28384          */
28385         beforehide : true,
28386         /**
28387          * @event show
28388          * Fires after this menu is displayed
28389          * @param {Roo.bootstrap.menu.Menu} this
28390          */
28391         show : true,
28392         /**
28393          * @event hide
28394          * Fires after this menu is hidden
28395          * @param {Roo.bootstrap.menu.Menu} this
28396          */
28397         hide : true,
28398         /**
28399          * @event click
28400          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28401          * @param {Roo.bootstrap.menu.Menu} this
28402          * @param {Roo.EventObject} e
28403          */
28404         click : true
28405     });
28406     
28407 };
28408
28409 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28410     
28411     submenu : false,
28412     html : '',
28413     weight : 'default',
28414     icon : false,
28415     pos : 'bottom',
28416     
28417     
28418     getChildContainer : function() {
28419         if(this.isSubMenu){
28420             return this.el;
28421         }
28422         
28423         return this.el.select('ul.dropdown-menu', true).first();  
28424     },
28425     
28426     getAutoCreate : function()
28427     {
28428         var text = [
28429             {
28430                 tag : 'span',
28431                 cls : 'roo-menu-text',
28432                 html : this.html
28433             }
28434         ];
28435         
28436         if(this.icon){
28437             text.unshift({
28438                 tag : 'i',
28439                 cls : 'fa ' + this.icon
28440             })
28441         }
28442         
28443         
28444         var cfg = {
28445             tag : 'div',
28446             cls : 'btn-group',
28447             cn : [
28448                 {
28449                     tag : 'button',
28450                     cls : 'dropdown-button btn btn-' + this.weight,
28451                     cn : text
28452                 },
28453                 {
28454                     tag : 'button',
28455                     cls : 'dropdown-toggle btn btn-' + this.weight,
28456                     cn : [
28457                         {
28458                             tag : 'span',
28459                             cls : 'caret'
28460                         }
28461                     ]
28462                 },
28463                 {
28464                     tag : 'ul',
28465                     cls : 'dropdown-menu'
28466                 }
28467             ]
28468             
28469         };
28470         
28471         if(this.pos == 'top'){
28472             cfg.cls += ' dropup';
28473         }
28474         
28475         if(this.isSubMenu){
28476             cfg = {
28477                 tag : 'ul',
28478                 cls : 'dropdown-menu'
28479             }
28480         }
28481         
28482         return cfg;
28483     },
28484     
28485     onRender : function(ct, position)
28486     {
28487         this.isSubMenu = ct.hasClass('dropdown-submenu');
28488         
28489         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28490     },
28491     
28492     initEvents : function() 
28493     {
28494         if(this.isSubMenu){
28495             return;
28496         }
28497         
28498         this.hidden = true;
28499         
28500         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28501         this.triggerEl.on('click', this.onTriggerPress, this);
28502         
28503         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28504         this.buttonEl.on('click', this.onClick, this);
28505         
28506     },
28507     
28508     list : function()
28509     {
28510         if(this.isSubMenu){
28511             return this.el;
28512         }
28513         
28514         return this.el.select('ul.dropdown-menu', true).first();
28515     },
28516     
28517     onClick : function(e)
28518     {
28519         this.fireEvent("click", this, e);
28520     },
28521     
28522     onTriggerPress  : function(e)
28523     {   
28524         if (this.isVisible()) {
28525             this.hide();
28526         } else {
28527             this.show();
28528         }
28529     },
28530     
28531     isVisible : function(){
28532         return !this.hidden;
28533     },
28534     
28535     show : function()
28536     {
28537         this.fireEvent("beforeshow", this);
28538         
28539         this.hidden = false;
28540         this.el.addClass('open');
28541         
28542         Roo.get(document).on("mouseup", this.onMouseUp, this);
28543         
28544         this.fireEvent("show", this);
28545         
28546         
28547     },
28548     
28549     hide : function()
28550     {
28551         this.fireEvent("beforehide", this);
28552         
28553         this.hidden = true;
28554         this.el.removeClass('open');
28555         
28556         Roo.get(document).un("mouseup", this.onMouseUp);
28557         
28558         this.fireEvent("hide", this);
28559     },
28560     
28561     onMouseUp : function()
28562     {
28563         this.hide();
28564     }
28565     
28566 });
28567
28568  
28569  /*
28570  * - LGPL
28571  *
28572  * menu item
28573  * 
28574  */
28575 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28576
28577 /**
28578  * @class Roo.bootstrap.menu.Item
28579  * @extends Roo.bootstrap.Component
28580  * Bootstrap MenuItem class
28581  * @cfg {Boolean} submenu (true | false) default false
28582  * @cfg {String} html text of the item
28583  * @cfg {String} href the link
28584  * @cfg {Boolean} disable (true | false) default false
28585  * @cfg {Boolean} preventDefault (true | false) default true
28586  * @cfg {String} icon Font awesome icon
28587  * @cfg {String} pos Submenu align to (left | right) default right 
28588  * 
28589  * 
28590  * @constructor
28591  * Create a new Item
28592  * @param {Object} config The config object
28593  */
28594
28595
28596 Roo.bootstrap.menu.Item = function(config){
28597     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28598     this.addEvents({
28599         /**
28600          * @event mouseover
28601          * Fires when the mouse is hovering over this menu
28602          * @param {Roo.bootstrap.menu.Item} this
28603          * @param {Roo.EventObject} e
28604          */
28605         mouseover : true,
28606         /**
28607          * @event mouseout
28608          * Fires when the mouse exits this menu
28609          * @param {Roo.bootstrap.menu.Item} this
28610          * @param {Roo.EventObject} e
28611          */
28612         mouseout : true,
28613         // raw events
28614         /**
28615          * @event click
28616          * The raw click event for the entire grid.
28617          * @param {Roo.EventObject} e
28618          */
28619         click : true
28620     });
28621 };
28622
28623 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28624     
28625     submenu : false,
28626     href : '',
28627     html : '',
28628     preventDefault: true,
28629     disable : false,
28630     icon : false,
28631     pos : 'right',
28632     
28633     getAutoCreate : function()
28634     {
28635         var text = [
28636             {
28637                 tag : 'span',
28638                 cls : 'roo-menu-item-text',
28639                 html : this.html
28640             }
28641         ];
28642         
28643         if(this.icon){
28644             text.unshift({
28645                 tag : 'i',
28646                 cls : 'fa ' + this.icon
28647             })
28648         }
28649         
28650         var cfg = {
28651             tag : 'li',
28652             cn : [
28653                 {
28654                     tag : 'a',
28655                     href : this.href || '#',
28656                     cn : text
28657                 }
28658             ]
28659         };
28660         
28661         if(this.disable){
28662             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28663         }
28664         
28665         if(this.submenu){
28666             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28667             
28668             if(this.pos == 'left'){
28669                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28670             }
28671         }
28672         
28673         return cfg;
28674     },
28675     
28676     initEvents : function() 
28677     {
28678         this.el.on('mouseover', this.onMouseOver, this);
28679         this.el.on('mouseout', this.onMouseOut, this);
28680         
28681         this.el.select('a', true).first().on('click', this.onClick, this);
28682         
28683     },
28684     
28685     onClick : function(e)
28686     {
28687         if(this.preventDefault){
28688             e.preventDefault();
28689         }
28690         
28691         this.fireEvent("click", this, e);
28692     },
28693     
28694     onMouseOver : function(e)
28695     {
28696         if(this.submenu && this.pos == 'left'){
28697             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28698         }
28699         
28700         this.fireEvent("mouseover", this, e);
28701     },
28702     
28703     onMouseOut : function(e)
28704     {
28705         this.fireEvent("mouseout", this, e);
28706     }
28707 });
28708
28709  
28710
28711  /*
28712  * - LGPL
28713  *
28714  * menu separator
28715  * 
28716  */
28717 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28718
28719 /**
28720  * @class Roo.bootstrap.menu.Separator
28721  * @extends Roo.bootstrap.Component
28722  * Bootstrap Separator class
28723  * 
28724  * @constructor
28725  * Create a new Separator
28726  * @param {Object} config The config object
28727  */
28728
28729
28730 Roo.bootstrap.menu.Separator = function(config){
28731     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28732 };
28733
28734 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28735     
28736     getAutoCreate : function(){
28737         var cfg = {
28738             tag : 'li',
28739             cls: 'divider'
28740         };
28741         
28742         return cfg;
28743     }
28744    
28745 });
28746
28747  
28748
28749  /*
28750  * - LGPL
28751  *
28752  * Tooltip
28753  * 
28754  */
28755
28756 /**
28757  * @class Roo.bootstrap.Tooltip
28758  * Bootstrap Tooltip class
28759  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28760  * to determine which dom element triggers the tooltip.
28761  * 
28762  * It needs to add support for additional attributes like tooltip-position
28763  * 
28764  * @constructor
28765  * Create a new Toolti
28766  * @param {Object} config The config object
28767  */
28768
28769 Roo.bootstrap.Tooltip = function(config){
28770     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28771     
28772     this.alignment = Roo.bootstrap.Tooltip.alignment;
28773     
28774     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28775         this.alignment = config.alignment;
28776     }
28777     
28778 };
28779
28780 Roo.apply(Roo.bootstrap.Tooltip, {
28781     /**
28782      * @function init initialize tooltip monitoring.
28783      * @static
28784      */
28785     currentEl : false,
28786     currentTip : false,
28787     currentRegion : false,
28788     
28789     //  init : delay?
28790     
28791     init : function()
28792     {
28793         Roo.get(document).on('mouseover', this.enter ,this);
28794         Roo.get(document).on('mouseout', this.leave, this);
28795          
28796         
28797         this.currentTip = new Roo.bootstrap.Tooltip();
28798     },
28799     
28800     enter : function(ev)
28801     {
28802         var dom = ev.getTarget();
28803         
28804         //Roo.log(['enter',dom]);
28805         var el = Roo.fly(dom);
28806         if (this.currentEl) {
28807             //Roo.log(dom);
28808             //Roo.log(this.currentEl);
28809             //Roo.log(this.currentEl.contains(dom));
28810             if (this.currentEl == el) {
28811                 return;
28812             }
28813             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28814                 return;
28815             }
28816
28817         }
28818         
28819         if (this.currentTip.el) {
28820             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28821         }    
28822         //Roo.log(ev);
28823         
28824         if(!el || el.dom == document){
28825             return;
28826         }
28827         
28828         var bindEl = el;
28829         
28830         // you can not look for children, as if el is the body.. then everythign is the child..
28831         if (!el.attr('tooltip')) { //
28832             if (!el.select("[tooltip]").elements.length) {
28833                 return;
28834             }
28835             // is the mouse over this child...?
28836             bindEl = el.select("[tooltip]").first();
28837             var xy = ev.getXY();
28838             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28839                 //Roo.log("not in region.");
28840                 return;
28841             }
28842             //Roo.log("child element over..");
28843             
28844         }
28845         this.currentEl = bindEl;
28846         this.currentTip.bind(bindEl);
28847         this.currentRegion = Roo.lib.Region.getRegion(dom);
28848         this.currentTip.enter();
28849         
28850     },
28851     leave : function(ev)
28852     {
28853         var dom = ev.getTarget();
28854         //Roo.log(['leave',dom]);
28855         if (!this.currentEl) {
28856             return;
28857         }
28858         
28859         
28860         if (dom != this.currentEl.dom) {
28861             return;
28862         }
28863         var xy = ev.getXY();
28864         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28865             return;
28866         }
28867         // only activate leave if mouse cursor is outside... bounding box..
28868         
28869         
28870         
28871         
28872         if (this.currentTip) {
28873             this.currentTip.leave();
28874         }
28875         //Roo.log('clear currentEl');
28876         this.currentEl = false;
28877         
28878         
28879     },
28880     alignment : {
28881         'left' : ['r-l', [-2,0], 'right'],
28882         'right' : ['l-r', [2,0], 'left'],
28883         'bottom' : ['t-b', [0,2], 'top'],
28884         'top' : [ 'b-t', [0,-2], 'bottom']
28885     }
28886     
28887 });
28888
28889
28890 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28891     
28892     
28893     bindEl : false,
28894     
28895     delay : null, // can be { show : 300 , hide: 500}
28896     
28897     timeout : null,
28898     
28899     hoverState : null, //???
28900     
28901     placement : 'bottom', 
28902     
28903     alignment : false,
28904     
28905     getAutoCreate : function(){
28906     
28907         var cfg = {
28908            cls : 'tooltip',   
28909            role : 'tooltip',
28910            cn : [
28911                 {
28912                     cls : 'tooltip-arrow arrow'
28913                 },
28914                 {
28915                     cls : 'tooltip-inner'
28916                 }
28917            ]
28918         };
28919         
28920         return cfg;
28921     },
28922     bind : function(el)
28923     {
28924         this.bindEl = el;
28925     },
28926     
28927     initEvents : function()
28928     {
28929         this.arrowEl = this.el.select('.arrow', true).first();
28930         this.innerEl = this.el.select('.tooltip-inner', true).first();
28931     },
28932     
28933     enter : function () {
28934        
28935         if (this.timeout != null) {
28936             clearTimeout(this.timeout);
28937         }
28938         
28939         this.hoverState = 'in';
28940          //Roo.log("enter - show");
28941         if (!this.delay || !this.delay.show) {
28942             this.show();
28943             return;
28944         }
28945         var _t = this;
28946         this.timeout = setTimeout(function () {
28947             if (_t.hoverState == 'in') {
28948                 _t.show();
28949             }
28950         }, this.delay.show);
28951     },
28952     leave : function()
28953     {
28954         clearTimeout(this.timeout);
28955     
28956         this.hoverState = 'out';
28957          if (!this.delay || !this.delay.hide) {
28958             this.hide();
28959             return;
28960         }
28961        
28962         var _t = this;
28963         this.timeout = setTimeout(function () {
28964             //Roo.log("leave - timeout");
28965             
28966             if (_t.hoverState == 'out') {
28967                 _t.hide();
28968                 Roo.bootstrap.Tooltip.currentEl = false;
28969             }
28970         }, delay);
28971     },
28972     
28973     show : function (msg)
28974     {
28975         if (!this.el) {
28976             this.render(document.body);
28977         }
28978         // set content.
28979         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28980         
28981         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28982         
28983         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28984         
28985         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28986                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28987         
28988         var placement = typeof this.placement == 'function' ?
28989             this.placement.call(this, this.el, on_el) :
28990             this.placement;
28991             
28992         var autoToken = /\s?auto?\s?/i;
28993         var autoPlace = autoToken.test(placement);
28994         if (autoPlace) {
28995             placement = placement.replace(autoToken, '') || 'top';
28996         }
28997         
28998         //this.el.detach()
28999         //this.el.setXY([0,0]);
29000         this.el.show();
29001         //this.el.dom.style.display='block';
29002         
29003         //this.el.appendTo(on_el);
29004         
29005         var p = this.getPosition();
29006         var box = this.el.getBox();
29007         
29008         if (autoPlace) {
29009             // fixme..
29010         }
29011         
29012         var align = this.alignment[placement];
29013         
29014         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29015         
29016         if(placement == 'top' || placement == 'bottom'){
29017             if(xy[0] < 0){
29018                 placement = 'right';
29019             }
29020             
29021             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29022                 placement = 'left';
29023             }
29024             
29025             var scroll = Roo.select('body', true).first().getScroll();
29026             
29027             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29028                 placement = 'top';
29029             }
29030             
29031             align = this.alignment[placement];
29032             
29033             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29034             
29035         }
29036         
29037         this.el.alignTo(this.bindEl, align[0],align[1]);
29038         //var arrow = this.el.select('.arrow',true).first();
29039         //arrow.set(align[2], 
29040         
29041         this.el.addClass(placement);
29042         this.el.addClass("bs-tooltip-"+ placement);
29043         
29044         this.el.addClass('in fade show');
29045         
29046         this.hoverState = null;
29047         
29048         if (this.el.hasClass('fade')) {
29049             // fade it?
29050         }
29051         
29052         
29053         
29054         
29055         
29056     },
29057     hide : function()
29058     {
29059          
29060         if (!this.el) {
29061             return;
29062         }
29063         //this.el.setXY([0,0]);
29064         this.el.removeClass(['show', 'in']);
29065         //this.el.hide();
29066         
29067     }
29068     
29069 });
29070  
29071
29072  /*
29073  * - LGPL
29074  *
29075  * Location Picker
29076  * 
29077  */
29078
29079 /**
29080  * @class Roo.bootstrap.LocationPicker
29081  * @extends Roo.bootstrap.Component
29082  * Bootstrap LocationPicker class
29083  * @cfg {Number} latitude Position when init default 0
29084  * @cfg {Number} longitude Position when init default 0
29085  * @cfg {Number} zoom default 15
29086  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29087  * @cfg {Boolean} mapTypeControl default false
29088  * @cfg {Boolean} disableDoubleClickZoom default false
29089  * @cfg {Boolean} scrollwheel default true
29090  * @cfg {Boolean} streetViewControl default false
29091  * @cfg {Number} radius default 0
29092  * @cfg {String} locationName
29093  * @cfg {Boolean} draggable default true
29094  * @cfg {Boolean} enableAutocomplete default false
29095  * @cfg {Boolean} enableReverseGeocode default true
29096  * @cfg {String} markerTitle
29097  * 
29098  * @constructor
29099  * Create a new LocationPicker
29100  * @param {Object} config The config object
29101  */
29102
29103
29104 Roo.bootstrap.LocationPicker = function(config){
29105     
29106     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29107     
29108     this.addEvents({
29109         /**
29110          * @event initial
29111          * Fires when the picker initialized.
29112          * @param {Roo.bootstrap.LocationPicker} this
29113          * @param {Google Location} location
29114          */
29115         initial : true,
29116         /**
29117          * @event positionchanged
29118          * Fires when the picker position changed.
29119          * @param {Roo.bootstrap.LocationPicker} this
29120          * @param {Google Location} location
29121          */
29122         positionchanged : true,
29123         /**
29124          * @event resize
29125          * Fires when the map resize.
29126          * @param {Roo.bootstrap.LocationPicker} this
29127          */
29128         resize : true,
29129         /**
29130          * @event show
29131          * Fires when the map show.
29132          * @param {Roo.bootstrap.LocationPicker} this
29133          */
29134         show : true,
29135         /**
29136          * @event hide
29137          * Fires when the map hide.
29138          * @param {Roo.bootstrap.LocationPicker} this
29139          */
29140         hide : true,
29141         /**
29142          * @event mapClick
29143          * Fires when click the map.
29144          * @param {Roo.bootstrap.LocationPicker} this
29145          * @param {Map event} e
29146          */
29147         mapClick : true,
29148         /**
29149          * @event mapRightClick
29150          * Fires when right click the map.
29151          * @param {Roo.bootstrap.LocationPicker} this
29152          * @param {Map event} e
29153          */
29154         mapRightClick : true,
29155         /**
29156          * @event markerClick
29157          * Fires when click the marker.
29158          * @param {Roo.bootstrap.LocationPicker} this
29159          * @param {Map event} e
29160          */
29161         markerClick : true,
29162         /**
29163          * @event markerRightClick
29164          * Fires when right click the marker.
29165          * @param {Roo.bootstrap.LocationPicker} this
29166          * @param {Map event} e
29167          */
29168         markerRightClick : true,
29169         /**
29170          * @event OverlayViewDraw
29171          * Fires when OverlayView Draw
29172          * @param {Roo.bootstrap.LocationPicker} this
29173          */
29174         OverlayViewDraw : true,
29175         /**
29176          * @event OverlayViewOnAdd
29177          * Fires when OverlayView Draw
29178          * @param {Roo.bootstrap.LocationPicker} this
29179          */
29180         OverlayViewOnAdd : true,
29181         /**
29182          * @event OverlayViewOnRemove
29183          * Fires when OverlayView Draw
29184          * @param {Roo.bootstrap.LocationPicker} this
29185          */
29186         OverlayViewOnRemove : true,
29187         /**
29188          * @event OverlayViewShow
29189          * Fires when OverlayView Draw
29190          * @param {Roo.bootstrap.LocationPicker} this
29191          * @param {Pixel} cpx
29192          */
29193         OverlayViewShow : true,
29194         /**
29195          * @event OverlayViewHide
29196          * Fires when OverlayView Draw
29197          * @param {Roo.bootstrap.LocationPicker} this
29198          */
29199         OverlayViewHide : true,
29200         /**
29201          * @event loadexception
29202          * Fires when load google lib failed.
29203          * @param {Roo.bootstrap.LocationPicker} this
29204          */
29205         loadexception : true
29206     });
29207         
29208 };
29209
29210 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29211     
29212     gMapContext: false,
29213     
29214     latitude: 0,
29215     longitude: 0,
29216     zoom: 15,
29217     mapTypeId: false,
29218     mapTypeControl: false,
29219     disableDoubleClickZoom: false,
29220     scrollwheel: true,
29221     streetViewControl: false,
29222     radius: 0,
29223     locationName: '',
29224     draggable: true,
29225     enableAutocomplete: false,
29226     enableReverseGeocode: true,
29227     markerTitle: '',
29228     
29229     getAutoCreate: function()
29230     {
29231
29232         var cfg = {
29233             tag: 'div',
29234             cls: 'roo-location-picker'
29235         };
29236         
29237         return cfg
29238     },
29239     
29240     initEvents: function(ct, position)
29241     {       
29242         if(!this.el.getWidth() || this.isApplied()){
29243             return;
29244         }
29245         
29246         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29247         
29248         this.initial();
29249     },
29250     
29251     initial: function()
29252     {
29253         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29254             this.fireEvent('loadexception', this);
29255             return;
29256         }
29257         
29258         if(!this.mapTypeId){
29259             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29260         }
29261         
29262         this.gMapContext = this.GMapContext();
29263         
29264         this.initOverlayView();
29265         
29266         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29267         
29268         var _this = this;
29269                 
29270         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29271             _this.setPosition(_this.gMapContext.marker.position);
29272         });
29273         
29274         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29275             _this.fireEvent('mapClick', this, event);
29276             
29277         });
29278
29279         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29280             _this.fireEvent('mapRightClick', this, event);
29281             
29282         });
29283         
29284         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29285             _this.fireEvent('markerClick', this, event);
29286             
29287         });
29288
29289         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29290             _this.fireEvent('markerRightClick', this, event);
29291             
29292         });
29293         
29294         this.setPosition(this.gMapContext.location);
29295         
29296         this.fireEvent('initial', this, this.gMapContext.location);
29297     },
29298     
29299     initOverlayView: function()
29300     {
29301         var _this = this;
29302         
29303         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29304             
29305             draw: function()
29306             {
29307                 _this.fireEvent('OverlayViewDraw', _this);
29308             },
29309             
29310             onAdd: function()
29311             {
29312                 _this.fireEvent('OverlayViewOnAdd', _this);
29313             },
29314             
29315             onRemove: function()
29316             {
29317                 _this.fireEvent('OverlayViewOnRemove', _this);
29318             },
29319             
29320             show: function(cpx)
29321             {
29322                 _this.fireEvent('OverlayViewShow', _this, cpx);
29323             },
29324             
29325             hide: function()
29326             {
29327                 _this.fireEvent('OverlayViewHide', _this);
29328             }
29329             
29330         });
29331     },
29332     
29333     fromLatLngToContainerPixel: function(event)
29334     {
29335         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29336     },
29337     
29338     isApplied: function() 
29339     {
29340         return this.getGmapContext() == false ? false : true;
29341     },
29342     
29343     getGmapContext: function() 
29344     {
29345         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29346     },
29347     
29348     GMapContext: function() 
29349     {
29350         var position = new google.maps.LatLng(this.latitude, this.longitude);
29351         
29352         var _map = new google.maps.Map(this.el.dom, {
29353             center: position,
29354             zoom: this.zoom,
29355             mapTypeId: this.mapTypeId,
29356             mapTypeControl: this.mapTypeControl,
29357             disableDoubleClickZoom: this.disableDoubleClickZoom,
29358             scrollwheel: this.scrollwheel,
29359             streetViewControl: this.streetViewControl,
29360             locationName: this.locationName,
29361             draggable: this.draggable,
29362             enableAutocomplete: this.enableAutocomplete,
29363             enableReverseGeocode: this.enableReverseGeocode
29364         });
29365         
29366         var _marker = new google.maps.Marker({
29367             position: position,
29368             map: _map,
29369             title: this.markerTitle,
29370             draggable: this.draggable
29371         });
29372         
29373         return {
29374             map: _map,
29375             marker: _marker,
29376             circle: null,
29377             location: position,
29378             radius: this.radius,
29379             locationName: this.locationName,
29380             addressComponents: {
29381                 formatted_address: null,
29382                 addressLine1: null,
29383                 addressLine2: null,
29384                 streetName: null,
29385                 streetNumber: null,
29386                 city: null,
29387                 district: null,
29388                 state: null,
29389                 stateOrProvince: null
29390             },
29391             settings: this,
29392             domContainer: this.el.dom,
29393             geodecoder: new google.maps.Geocoder()
29394         };
29395     },
29396     
29397     drawCircle: function(center, radius, options) 
29398     {
29399         if (this.gMapContext.circle != null) {
29400             this.gMapContext.circle.setMap(null);
29401         }
29402         if (radius > 0) {
29403             radius *= 1;
29404             options = Roo.apply({}, options, {
29405                 strokeColor: "#0000FF",
29406                 strokeOpacity: .35,
29407                 strokeWeight: 2,
29408                 fillColor: "#0000FF",
29409                 fillOpacity: .2
29410             });
29411             
29412             options.map = this.gMapContext.map;
29413             options.radius = radius;
29414             options.center = center;
29415             this.gMapContext.circle = new google.maps.Circle(options);
29416             return this.gMapContext.circle;
29417         }
29418         
29419         return null;
29420     },
29421     
29422     setPosition: function(location) 
29423     {
29424         this.gMapContext.location = location;
29425         this.gMapContext.marker.setPosition(location);
29426         this.gMapContext.map.panTo(location);
29427         this.drawCircle(location, this.gMapContext.radius, {});
29428         
29429         var _this = this;
29430         
29431         if (this.gMapContext.settings.enableReverseGeocode) {
29432             this.gMapContext.geodecoder.geocode({
29433                 latLng: this.gMapContext.location
29434             }, function(results, status) {
29435                 
29436                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29437                     _this.gMapContext.locationName = results[0].formatted_address;
29438                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29439                     
29440                     _this.fireEvent('positionchanged', this, location);
29441                 }
29442             });
29443             
29444             return;
29445         }
29446         
29447         this.fireEvent('positionchanged', this, location);
29448     },
29449     
29450     resize: function()
29451     {
29452         google.maps.event.trigger(this.gMapContext.map, "resize");
29453         
29454         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29455         
29456         this.fireEvent('resize', this);
29457     },
29458     
29459     setPositionByLatLng: function(latitude, longitude)
29460     {
29461         this.setPosition(new google.maps.LatLng(latitude, longitude));
29462     },
29463     
29464     getCurrentPosition: function() 
29465     {
29466         return {
29467             latitude: this.gMapContext.location.lat(),
29468             longitude: this.gMapContext.location.lng()
29469         };
29470     },
29471     
29472     getAddressName: function() 
29473     {
29474         return this.gMapContext.locationName;
29475     },
29476     
29477     getAddressComponents: function() 
29478     {
29479         return this.gMapContext.addressComponents;
29480     },
29481     
29482     address_component_from_google_geocode: function(address_components) 
29483     {
29484         var result = {};
29485         
29486         for (var i = 0; i < address_components.length; i++) {
29487             var component = address_components[i];
29488             if (component.types.indexOf("postal_code") >= 0) {
29489                 result.postalCode = component.short_name;
29490             } else if (component.types.indexOf("street_number") >= 0) {
29491                 result.streetNumber = component.short_name;
29492             } else if (component.types.indexOf("route") >= 0) {
29493                 result.streetName = component.short_name;
29494             } else if (component.types.indexOf("neighborhood") >= 0) {
29495                 result.city = component.short_name;
29496             } else if (component.types.indexOf("locality") >= 0) {
29497                 result.city = component.short_name;
29498             } else if (component.types.indexOf("sublocality") >= 0) {
29499                 result.district = component.short_name;
29500             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29501                 result.stateOrProvince = component.short_name;
29502             } else if (component.types.indexOf("country") >= 0) {
29503                 result.country = component.short_name;
29504             }
29505         }
29506         
29507         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29508         result.addressLine2 = "";
29509         return result;
29510     },
29511     
29512     setZoomLevel: function(zoom)
29513     {
29514         this.gMapContext.map.setZoom(zoom);
29515     },
29516     
29517     show: function()
29518     {
29519         if(!this.el){
29520             return;
29521         }
29522         
29523         this.el.show();
29524         
29525         this.resize();
29526         
29527         this.fireEvent('show', this);
29528     },
29529     
29530     hide: function()
29531     {
29532         if(!this.el){
29533             return;
29534         }
29535         
29536         this.el.hide();
29537         
29538         this.fireEvent('hide', this);
29539     }
29540     
29541 });
29542
29543 Roo.apply(Roo.bootstrap.LocationPicker, {
29544     
29545     OverlayView : function(map, options)
29546     {
29547         options = options || {};
29548         
29549         this.setMap(map);
29550     }
29551     
29552     
29553 });/**
29554  * @class Roo.bootstrap.Alert
29555  * @extends Roo.bootstrap.Component
29556  * Bootstrap Alert class - shows an alert area box
29557  * eg
29558  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29559   Enter a valid email address
29560 </div>
29561  * @licence LGPL
29562  * @cfg {String} title The title of alert
29563  * @cfg {String} html The content of alert
29564  * @cfg {String} weight (  success | info | warning | danger )
29565  * @cfg {String} faicon font-awesomeicon
29566  * 
29567  * @constructor
29568  * Create a new alert
29569  * @param {Object} config The config object
29570  */
29571
29572
29573 Roo.bootstrap.Alert = function(config){
29574     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29575     
29576 };
29577
29578 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29579     
29580     title: '',
29581     html: '',
29582     weight: false,
29583     faicon: false,
29584     
29585     getAutoCreate : function()
29586     {
29587         
29588         var cfg = {
29589             tag : 'div',
29590             cls : 'alert',
29591             cn : [
29592                 {
29593                     tag : 'i',
29594                     cls : 'roo-alert-icon'
29595                     
29596                 },
29597                 {
29598                     tag : 'b',
29599                     cls : 'roo-alert-title',
29600                     html : this.title
29601                 },
29602                 {
29603                     tag : 'span',
29604                     cls : 'roo-alert-text',
29605                     html : this.html
29606                 }
29607             ]
29608         };
29609         
29610         if(this.faicon){
29611             cfg.cn[0].cls += ' fa ' + this.faicon;
29612         }
29613         
29614         if(this.weight){
29615             cfg.cls += ' alert-' + this.weight;
29616         }
29617         
29618         return cfg;
29619     },
29620     
29621     initEvents: function() 
29622     {
29623         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29624     },
29625     
29626     setTitle : function(str)
29627     {
29628         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29629     },
29630     
29631     setText : function(str)
29632     {
29633         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29634     },
29635     
29636     setWeight : function(weight)
29637     {
29638         if(this.weight){
29639             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29640         }
29641         
29642         this.weight = weight;
29643         
29644         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29645     },
29646     
29647     setIcon : function(icon)
29648     {
29649         if(this.faicon){
29650             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29651         }
29652         
29653         this.faicon = icon;
29654         
29655         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29656     },
29657     
29658     hide: function() 
29659     {
29660         this.el.hide();   
29661     },
29662     
29663     show: function() 
29664     {  
29665         this.el.show();   
29666     }
29667     
29668 });
29669
29670  
29671 /*
29672 * Licence: LGPL
29673 */
29674
29675 /**
29676  * @class Roo.bootstrap.UploadCropbox
29677  * @extends Roo.bootstrap.Component
29678  * Bootstrap UploadCropbox class
29679  * @cfg {String} emptyText show when image has been loaded
29680  * @cfg {String} rotateNotify show when image too small to rotate
29681  * @cfg {Number} errorTimeout default 3000
29682  * @cfg {Number} minWidth default 300
29683  * @cfg {Number} minHeight default 300
29684  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29685  * @cfg {Boolean} isDocument (true|false) default false
29686  * @cfg {String} url action url
29687  * @cfg {String} paramName default 'imageUpload'
29688  * @cfg {String} method default POST
29689  * @cfg {Boolean} loadMask (true|false) default true
29690  * @cfg {Boolean} loadingText default 'Loading...'
29691  * 
29692  * @constructor
29693  * Create a new UploadCropbox
29694  * @param {Object} config The config object
29695  */
29696
29697 Roo.bootstrap.UploadCropbox = function(config){
29698     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29699     
29700     this.addEvents({
29701         /**
29702          * @event beforeselectfile
29703          * Fire before select file
29704          * @param {Roo.bootstrap.UploadCropbox} this
29705          */
29706         "beforeselectfile" : true,
29707         /**
29708          * @event initial
29709          * Fire after initEvent
29710          * @param {Roo.bootstrap.UploadCropbox} this
29711          */
29712         "initial" : true,
29713         /**
29714          * @event crop
29715          * Fire after initEvent
29716          * @param {Roo.bootstrap.UploadCropbox} this
29717          * @param {String} data
29718          */
29719         "crop" : true,
29720         /**
29721          * @event prepare
29722          * Fire when preparing the file data
29723          * @param {Roo.bootstrap.UploadCropbox} this
29724          * @param {Object} file
29725          */
29726         "prepare" : true,
29727         /**
29728          * @event exception
29729          * Fire when get exception
29730          * @param {Roo.bootstrap.UploadCropbox} this
29731          * @param {XMLHttpRequest} xhr
29732          */
29733         "exception" : true,
29734         /**
29735          * @event beforeloadcanvas
29736          * Fire before load the canvas
29737          * @param {Roo.bootstrap.UploadCropbox} this
29738          * @param {String} src
29739          */
29740         "beforeloadcanvas" : true,
29741         /**
29742          * @event trash
29743          * Fire when trash image
29744          * @param {Roo.bootstrap.UploadCropbox} this
29745          */
29746         "trash" : true,
29747         /**
29748          * @event download
29749          * Fire when download the image
29750          * @param {Roo.bootstrap.UploadCropbox} this
29751          */
29752         "download" : true,
29753         /**
29754          * @event footerbuttonclick
29755          * Fire when footerbuttonclick
29756          * @param {Roo.bootstrap.UploadCropbox} this
29757          * @param {String} type
29758          */
29759         "footerbuttonclick" : true,
29760         /**
29761          * @event resize
29762          * Fire when resize
29763          * @param {Roo.bootstrap.UploadCropbox} this
29764          */
29765         "resize" : true,
29766         /**
29767          * @event rotate
29768          * Fire when rotate the image
29769          * @param {Roo.bootstrap.UploadCropbox} this
29770          * @param {String} pos
29771          */
29772         "rotate" : true,
29773         /**
29774          * @event inspect
29775          * Fire when inspect the file
29776          * @param {Roo.bootstrap.UploadCropbox} this
29777          * @param {Object} file
29778          */
29779         "inspect" : true,
29780         /**
29781          * @event upload
29782          * Fire when xhr upload the file
29783          * @param {Roo.bootstrap.UploadCropbox} this
29784          * @param {Object} data
29785          */
29786         "upload" : true,
29787         /**
29788          * @event arrange
29789          * Fire when arrange the file data
29790          * @param {Roo.bootstrap.UploadCropbox} this
29791          * @param {Object} formData
29792          */
29793         "arrange" : true
29794     });
29795     
29796     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29797 };
29798
29799 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29800     
29801     emptyText : 'Click to upload image',
29802     rotateNotify : 'Image is too small to rotate',
29803     errorTimeout : 3000,
29804     scale : 0,
29805     baseScale : 1,
29806     rotate : 0,
29807     dragable : false,
29808     pinching : false,
29809     mouseX : 0,
29810     mouseY : 0,
29811     cropData : false,
29812     minWidth : 300,
29813     minHeight : 300,
29814     file : false,
29815     exif : {},
29816     baseRotate : 1,
29817     cropType : 'image/jpeg',
29818     buttons : false,
29819     canvasLoaded : false,
29820     isDocument : false,
29821     method : 'POST',
29822     paramName : 'imageUpload',
29823     loadMask : true,
29824     loadingText : 'Loading...',
29825     maskEl : false,
29826     
29827     getAutoCreate : function()
29828     {
29829         var cfg = {
29830             tag : 'div',
29831             cls : 'roo-upload-cropbox',
29832             cn : [
29833                 {
29834                     tag : 'input',
29835                     cls : 'roo-upload-cropbox-selector',
29836                     type : 'file'
29837                 },
29838                 {
29839                     tag : 'div',
29840                     cls : 'roo-upload-cropbox-body',
29841                     style : 'cursor:pointer',
29842                     cn : [
29843                         {
29844                             tag : 'div',
29845                             cls : 'roo-upload-cropbox-preview'
29846                         },
29847                         {
29848                             tag : 'div',
29849                             cls : 'roo-upload-cropbox-thumb'
29850                         },
29851                         {
29852                             tag : 'div',
29853                             cls : 'roo-upload-cropbox-empty-notify',
29854                             html : this.emptyText
29855                         },
29856                         {
29857                             tag : 'div',
29858                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29859                             html : this.rotateNotify
29860                         }
29861                     ]
29862                 },
29863                 {
29864                     tag : 'div',
29865                     cls : 'roo-upload-cropbox-footer',
29866                     cn : {
29867                         tag : 'div',
29868                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29869                         cn : []
29870                     }
29871                 }
29872             ]
29873         };
29874         
29875         return cfg;
29876     },
29877     
29878     onRender : function(ct, position)
29879     {
29880         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29881         
29882         if (this.buttons.length) {
29883             
29884             Roo.each(this.buttons, function(bb) {
29885                 
29886                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29887                 
29888                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29889                 
29890             }, this);
29891         }
29892         
29893         if(this.loadMask){
29894             this.maskEl = this.el;
29895         }
29896     },
29897     
29898     initEvents : function()
29899     {
29900         this.urlAPI = (window.createObjectURL && window) || 
29901                                 (window.URL && URL.revokeObjectURL && URL) || 
29902                                 (window.webkitURL && webkitURL);
29903                         
29904         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29905         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29906         
29907         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29908         this.selectorEl.hide();
29909         
29910         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29911         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29912         
29913         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29914         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29915         this.thumbEl.hide();
29916         
29917         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29918         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29919         
29920         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29921         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29922         this.errorEl.hide();
29923         
29924         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29925         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29926         this.footerEl.hide();
29927         
29928         this.setThumbBoxSize();
29929         
29930         this.bind();
29931         
29932         this.resize();
29933         
29934         this.fireEvent('initial', this);
29935     },
29936
29937     bind : function()
29938     {
29939         var _this = this;
29940         
29941         window.addEventListener("resize", function() { _this.resize(); } );
29942         
29943         this.bodyEl.on('click', this.beforeSelectFile, this);
29944         
29945         if(Roo.isTouch){
29946             this.bodyEl.on('touchstart', this.onTouchStart, this);
29947             this.bodyEl.on('touchmove', this.onTouchMove, this);
29948             this.bodyEl.on('touchend', this.onTouchEnd, this);
29949         }
29950         
29951         if(!Roo.isTouch){
29952             this.bodyEl.on('mousedown', this.onMouseDown, this);
29953             this.bodyEl.on('mousemove', this.onMouseMove, this);
29954             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29955             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29956             Roo.get(document).on('mouseup', this.onMouseUp, this);
29957         }
29958         
29959         this.selectorEl.on('change', this.onFileSelected, this);
29960     },
29961     
29962     reset : function()
29963     {    
29964         this.scale = 0;
29965         this.baseScale = 1;
29966         this.rotate = 0;
29967         this.baseRotate = 1;
29968         this.dragable = false;
29969         this.pinching = false;
29970         this.mouseX = 0;
29971         this.mouseY = 0;
29972         this.cropData = false;
29973         this.notifyEl.dom.innerHTML = this.emptyText;
29974         
29975         this.selectorEl.dom.value = '';
29976         
29977     },
29978     
29979     resize : function()
29980     {
29981         if(this.fireEvent('resize', this) != false){
29982             this.setThumbBoxPosition();
29983             this.setCanvasPosition();
29984         }
29985     },
29986     
29987     onFooterButtonClick : function(e, el, o, type)
29988     {
29989         switch (type) {
29990             case 'rotate-left' :
29991                 this.onRotateLeft(e);
29992                 break;
29993             case 'rotate-right' :
29994                 this.onRotateRight(e);
29995                 break;
29996             case 'picture' :
29997                 this.beforeSelectFile(e);
29998                 break;
29999             case 'trash' :
30000                 this.trash(e);
30001                 break;
30002             case 'crop' :
30003                 this.crop(e);
30004                 break;
30005             case 'download' :
30006                 this.download(e);
30007                 break;
30008             default :
30009                 break;
30010         }
30011         
30012         this.fireEvent('footerbuttonclick', this, type);
30013     },
30014     
30015     beforeSelectFile : function(e)
30016     {
30017         e.preventDefault();
30018         
30019         if(this.fireEvent('beforeselectfile', this) != false){
30020             this.selectorEl.dom.click();
30021         }
30022     },
30023     
30024     onFileSelected : function(e)
30025     {
30026         e.preventDefault();
30027         
30028         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30029             return;
30030         }
30031         
30032         var file = this.selectorEl.dom.files[0];
30033         
30034         if(this.fireEvent('inspect', this, file) != false){
30035             this.prepare(file);
30036         }
30037         
30038     },
30039     
30040     trash : function(e)
30041     {
30042         this.fireEvent('trash', this);
30043     },
30044     
30045     download : function(e)
30046     {
30047         this.fireEvent('download', this);
30048     },
30049     
30050     loadCanvas : function(src)
30051     {   
30052         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30053             
30054             this.reset();
30055             
30056             this.imageEl = document.createElement('img');
30057             
30058             var _this = this;
30059             
30060             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30061             
30062             this.imageEl.src = src;
30063         }
30064     },
30065     
30066     onLoadCanvas : function()
30067     {   
30068         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30069         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30070         
30071         this.bodyEl.un('click', this.beforeSelectFile, this);
30072         
30073         this.notifyEl.hide();
30074         this.thumbEl.show();
30075         this.footerEl.show();
30076         
30077         this.baseRotateLevel();
30078         
30079         if(this.isDocument){
30080             this.setThumbBoxSize();
30081         }
30082         
30083         this.setThumbBoxPosition();
30084         
30085         this.baseScaleLevel();
30086         
30087         this.draw();
30088         
30089         this.resize();
30090         
30091         this.canvasLoaded = true;
30092         
30093         if(this.loadMask){
30094             this.maskEl.unmask();
30095         }
30096         
30097     },
30098     
30099     setCanvasPosition : function()
30100     {   
30101         if(!this.canvasEl){
30102             return;
30103         }
30104         
30105         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30106         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30107         
30108         this.previewEl.setLeft(pw);
30109         this.previewEl.setTop(ph);
30110         
30111     },
30112     
30113     onMouseDown : function(e)
30114     {   
30115         e.stopEvent();
30116         
30117         this.dragable = true;
30118         this.pinching = false;
30119         
30120         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30121             this.dragable = false;
30122             return;
30123         }
30124         
30125         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30126         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30127         
30128     },
30129     
30130     onMouseMove : function(e)
30131     {   
30132         e.stopEvent();
30133         
30134         if(!this.canvasLoaded){
30135             return;
30136         }
30137         
30138         if (!this.dragable){
30139             return;
30140         }
30141         
30142         var minX = Math.ceil(this.thumbEl.getLeft(true));
30143         var minY = Math.ceil(this.thumbEl.getTop(true));
30144         
30145         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30146         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30147         
30148         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30149         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30150         
30151         x = x - this.mouseX;
30152         y = y - this.mouseY;
30153         
30154         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30155         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30156         
30157         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30158         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30159         
30160         this.previewEl.setLeft(bgX);
30161         this.previewEl.setTop(bgY);
30162         
30163         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30164         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30165     },
30166     
30167     onMouseUp : function(e)
30168     {   
30169         e.stopEvent();
30170         
30171         this.dragable = false;
30172     },
30173     
30174     onMouseWheel : function(e)
30175     {   
30176         e.stopEvent();
30177         
30178         this.startScale = this.scale;
30179         
30180         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30181         
30182         if(!this.zoomable()){
30183             this.scale = this.startScale;
30184             return;
30185         }
30186         
30187         this.draw();
30188         
30189         return;
30190     },
30191     
30192     zoomable : function()
30193     {
30194         var minScale = this.thumbEl.getWidth() / this.minWidth;
30195         
30196         if(this.minWidth < this.minHeight){
30197             minScale = this.thumbEl.getHeight() / this.minHeight;
30198         }
30199         
30200         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30201         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30202         
30203         if(
30204                 this.isDocument &&
30205                 (this.rotate == 0 || this.rotate == 180) && 
30206                 (
30207                     width > this.imageEl.OriginWidth || 
30208                     height > this.imageEl.OriginHeight ||
30209                     (width < this.minWidth && height < this.minHeight)
30210                 )
30211         ){
30212             return false;
30213         }
30214         
30215         if(
30216                 this.isDocument &&
30217                 (this.rotate == 90 || this.rotate == 270) && 
30218                 (
30219                     width > this.imageEl.OriginWidth || 
30220                     height > this.imageEl.OriginHeight ||
30221                     (width < this.minHeight && height < this.minWidth)
30222                 )
30223         ){
30224             return false;
30225         }
30226         
30227         if(
30228                 !this.isDocument &&
30229                 (this.rotate == 0 || this.rotate == 180) && 
30230                 (
30231                     width < this.minWidth || 
30232                     width > this.imageEl.OriginWidth || 
30233                     height < this.minHeight || 
30234                     height > this.imageEl.OriginHeight
30235                 )
30236         ){
30237             return false;
30238         }
30239         
30240         if(
30241                 !this.isDocument &&
30242                 (this.rotate == 90 || this.rotate == 270) && 
30243                 (
30244                     width < this.minHeight || 
30245                     width > this.imageEl.OriginWidth || 
30246                     height < this.minWidth || 
30247                     height > this.imageEl.OriginHeight
30248                 )
30249         ){
30250             return false;
30251         }
30252         
30253         return true;
30254         
30255     },
30256     
30257     onRotateLeft : function(e)
30258     {   
30259         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30260             
30261             var minScale = this.thumbEl.getWidth() / this.minWidth;
30262             
30263             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30264             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30265             
30266             this.startScale = this.scale;
30267             
30268             while (this.getScaleLevel() < minScale){
30269             
30270                 this.scale = this.scale + 1;
30271                 
30272                 if(!this.zoomable()){
30273                     break;
30274                 }
30275                 
30276                 if(
30277                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30278                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30279                 ){
30280                     continue;
30281                 }
30282                 
30283                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30284
30285                 this.draw();
30286                 
30287                 return;
30288             }
30289             
30290             this.scale = this.startScale;
30291             
30292             this.onRotateFail();
30293             
30294             return false;
30295         }
30296         
30297         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30298
30299         if(this.isDocument){
30300             this.setThumbBoxSize();
30301             this.setThumbBoxPosition();
30302             this.setCanvasPosition();
30303         }
30304         
30305         this.draw();
30306         
30307         this.fireEvent('rotate', this, 'left');
30308         
30309     },
30310     
30311     onRotateRight : function(e)
30312     {
30313         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30314             
30315             var minScale = this.thumbEl.getWidth() / this.minWidth;
30316         
30317             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30318             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30319             
30320             this.startScale = this.scale;
30321             
30322             while (this.getScaleLevel() < minScale){
30323             
30324                 this.scale = this.scale + 1;
30325                 
30326                 if(!this.zoomable()){
30327                     break;
30328                 }
30329                 
30330                 if(
30331                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30332                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30333                 ){
30334                     continue;
30335                 }
30336                 
30337                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30338
30339                 this.draw();
30340                 
30341                 return;
30342             }
30343             
30344             this.scale = this.startScale;
30345             
30346             this.onRotateFail();
30347             
30348             return false;
30349         }
30350         
30351         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30352
30353         if(this.isDocument){
30354             this.setThumbBoxSize();
30355             this.setThumbBoxPosition();
30356             this.setCanvasPosition();
30357         }
30358         
30359         this.draw();
30360         
30361         this.fireEvent('rotate', this, 'right');
30362     },
30363     
30364     onRotateFail : function()
30365     {
30366         this.errorEl.show(true);
30367         
30368         var _this = this;
30369         
30370         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30371     },
30372     
30373     draw : function()
30374     {
30375         this.previewEl.dom.innerHTML = '';
30376         
30377         var canvasEl = document.createElement("canvas");
30378         
30379         var contextEl = canvasEl.getContext("2d");
30380         
30381         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30382         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30383         var center = this.imageEl.OriginWidth / 2;
30384         
30385         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30386             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30387             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30388             center = this.imageEl.OriginHeight / 2;
30389         }
30390         
30391         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30392         
30393         contextEl.translate(center, center);
30394         contextEl.rotate(this.rotate * Math.PI / 180);
30395
30396         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30397         
30398         this.canvasEl = document.createElement("canvas");
30399         
30400         this.contextEl = this.canvasEl.getContext("2d");
30401         
30402         switch (this.rotate) {
30403             case 0 :
30404                 
30405                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30406                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30407                 
30408                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30409                 
30410                 break;
30411             case 90 : 
30412                 
30413                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30414                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30415                 
30416                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30417                     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);
30418                     break;
30419                 }
30420                 
30421                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30422                 
30423                 break;
30424             case 180 :
30425                 
30426                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30427                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30428                 
30429                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30430                     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);
30431                     break;
30432                 }
30433                 
30434                 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);
30435                 
30436                 break;
30437             case 270 :
30438                 
30439                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30440                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30441         
30442                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30443                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30444                     break;
30445                 }
30446                 
30447                 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);
30448                 
30449                 break;
30450             default : 
30451                 break;
30452         }
30453         
30454         this.previewEl.appendChild(this.canvasEl);
30455         
30456         this.setCanvasPosition();
30457     },
30458     
30459     crop : function()
30460     {
30461         if(!this.canvasLoaded){
30462             return;
30463         }
30464         
30465         var imageCanvas = document.createElement("canvas");
30466         
30467         var imageContext = imageCanvas.getContext("2d");
30468         
30469         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30470         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30471         
30472         var center = imageCanvas.width / 2;
30473         
30474         imageContext.translate(center, center);
30475         
30476         imageContext.rotate(this.rotate * Math.PI / 180);
30477         
30478         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30479         
30480         var canvas = document.createElement("canvas");
30481         
30482         var context = canvas.getContext("2d");
30483                 
30484         canvas.width = this.minWidth;
30485         canvas.height = this.minHeight;
30486
30487         switch (this.rotate) {
30488             case 0 :
30489                 
30490                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30491                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30492                 
30493                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30494                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30495                 
30496                 var targetWidth = this.minWidth - 2 * x;
30497                 var targetHeight = this.minHeight - 2 * y;
30498                 
30499                 var scale = 1;
30500                 
30501                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30502                     scale = targetWidth / width;
30503                 }
30504                 
30505                 if(x > 0 && y == 0){
30506                     scale = targetHeight / height;
30507                 }
30508                 
30509                 if(x > 0 && y > 0){
30510                     scale = targetWidth / width;
30511                     
30512                     if(width < height){
30513                         scale = targetHeight / height;
30514                     }
30515                 }
30516                 
30517                 context.scale(scale, scale);
30518                 
30519                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30520                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30521
30522                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30523                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30524
30525                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30526                 
30527                 break;
30528             case 90 : 
30529                 
30530                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30531                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30532                 
30533                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30534                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30535                 
30536                 var targetWidth = this.minWidth - 2 * x;
30537                 var targetHeight = this.minHeight - 2 * y;
30538                 
30539                 var scale = 1;
30540                 
30541                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30542                     scale = targetWidth / width;
30543                 }
30544                 
30545                 if(x > 0 && y == 0){
30546                     scale = targetHeight / height;
30547                 }
30548                 
30549                 if(x > 0 && y > 0){
30550                     scale = targetWidth / width;
30551                     
30552                     if(width < height){
30553                         scale = targetHeight / height;
30554                     }
30555                 }
30556                 
30557                 context.scale(scale, scale);
30558                 
30559                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30560                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30561
30562                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30563                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30564                 
30565                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30566                 
30567                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30568                 
30569                 break;
30570             case 180 :
30571                 
30572                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30573                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30574                 
30575                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30576                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30577                 
30578                 var targetWidth = this.minWidth - 2 * x;
30579                 var targetHeight = this.minHeight - 2 * y;
30580                 
30581                 var scale = 1;
30582                 
30583                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30584                     scale = targetWidth / width;
30585                 }
30586                 
30587                 if(x > 0 && y == 0){
30588                     scale = targetHeight / height;
30589                 }
30590                 
30591                 if(x > 0 && y > 0){
30592                     scale = targetWidth / width;
30593                     
30594                     if(width < height){
30595                         scale = targetHeight / height;
30596                     }
30597                 }
30598                 
30599                 context.scale(scale, scale);
30600                 
30601                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30602                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30603
30604                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30605                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30606
30607                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30608                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30609                 
30610                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30611                 
30612                 break;
30613             case 270 :
30614                 
30615                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30616                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30617                 
30618                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30619                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30620                 
30621                 var targetWidth = this.minWidth - 2 * x;
30622                 var targetHeight = this.minHeight - 2 * y;
30623                 
30624                 var scale = 1;
30625                 
30626                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30627                     scale = targetWidth / width;
30628                 }
30629                 
30630                 if(x > 0 && y == 0){
30631                     scale = targetHeight / height;
30632                 }
30633                 
30634                 if(x > 0 && y > 0){
30635                     scale = targetWidth / width;
30636                     
30637                     if(width < height){
30638                         scale = targetHeight / height;
30639                     }
30640                 }
30641                 
30642                 context.scale(scale, scale);
30643                 
30644                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30645                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30646
30647                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30648                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30649                 
30650                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30651                 
30652                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30653                 
30654                 break;
30655             default : 
30656                 break;
30657         }
30658         
30659         this.cropData = canvas.toDataURL(this.cropType);
30660         
30661         if(this.fireEvent('crop', this, this.cropData) !== false){
30662             this.process(this.file, this.cropData);
30663         }
30664         
30665         return;
30666         
30667     },
30668     
30669     setThumbBoxSize : function()
30670     {
30671         var width, height;
30672         
30673         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30674             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30675             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30676             
30677             this.minWidth = width;
30678             this.minHeight = height;
30679             
30680             if(this.rotate == 90 || this.rotate == 270){
30681                 this.minWidth = height;
30682                 this.minHeight = width;
30683             }
30684         }
30685         
30686         height = 300;
30687         width = Math.ceil(this.minWidth * height / this.minHeight);
30688         
30689         if(this.minWidth > this.minHeight){
30690             width = 300;
30691             height = Math.ceil(this.minHeight * width / this.minWidth);
30692         }
30693         
30694         this.thumbEl.setStyle({
30695             width : width + 'px',
30696             height : height + 'px'
30697         });
30698
30699         return;
30700             
30701     },
30702     
30703     setThumbBoxPosition : function()
30704     {
30705         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30706         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30707         
30708         this.thumbEl.setLeft(x);
30709         this.thumbEl.setTop(y);
30710         
30711     },
30712     
30713     baseRotateLevel : function()
30714     {
30715         this.baseRotate = 1;
30716         
30717         if(
30718                 typeof(this.exif) != 'undefined' &&
30719                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30720                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30721         ){
30722             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30723         }
30724         
30725         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30726         
30727     },
30728     
30729     baseScaleLevel : function()
30730     {
30731         var width, height;
30732         
30733         if(this.isDocument){
30734             
30735             if(this.baseRotate == 6 || this.baseRotate == 8){
30736             
30737                 height = this.thumbEl.getHeight();
30738                 this.baseScale = height / this.imageEl.OriginWidth;
30739
30740                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30741                     width = this.thumbEl.getWidth();
30742                     this.baseScale = width / this.imageEl.OriginHeight;
30743                 }
30744
30745                 return;
30746             }
30747
30748             height = this.thumbEl.getHeight();
30749             this.baseScale = height / this.imageEl.OriginHeight;
30750
30751             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30752                 width = this.thumbEl.getWidth();
30753                 this.baseScale = width / this.imageEl.OriginWidth;
30754             }
30755
30756             return;
30757         }
30758         
30759         if(this.baseRotate == 6 || this.baseRotate == 8){
30760             
30761             width = this.thumbEl.getHeight();
30762             this.baseScale = width / this.imageEl.OriginHeight;
30763             
30764             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30765                 height = this.thumbEl.getWidth();
30766                 this.baseScale = height / this.imageEl.OriginHeight;
30767             }
30768             
30769             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30770                 height = this.thumbEl.getWidth();
30771                 this.baseScale = height / this.imageEl.OriginHeight;
30772                 
30773                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30774                     width = this.thumbEl.getHeight();
30775                     this.baseScale = width / this.imageEl.OriginWidth;
30776                 }
30777             }
30778             
30779             return;
30780         }
30781         
30782         width = this.thumbEl.getWidth();
30783         this.baseScale = width / this.imageEl.OriginWidth;
30784         
30785         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30786             height = this.thumbEl.getHeight();
30787             this.baseScale = height / this.imageEl.OriginHeight;
30788         }
30789         
30790         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30791             
30792             height = this.thumbEl.getHeight();
30793             this.baseScale = height / this.imageEl.OriginHeight;
30794             
30795             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30796                 width = this.thumbEl.getWidth();
30797                 this.baseScale = width / this.imageEl.OriginWidth;
30798             }
30799             
30800         }
30801         
30802         return;
30803     },
30804     
30805     getScaleLevel : function()
30806     {
30807         return this.baseScale * Math.pow(1.1, this.scale);
30808     },
30809     
30810     onTouchStart : function(e)
30811     {
30812         if(!this.canvasLoaded){
30813             this.beforeSelectFile(e);
30814             return;
30815         }
30816         
30817         var touches = e.browserEvent.touches;
30818         
30819         if(!touches){
30820             return;
30821         }
30822         
30823         if(touches.length == 1){
30824             this.onMouseDown(e);
30825             return;
30826         }
30827         
30828         if(touches.length != 2){
30829             return;
30830         }
30831         
30832         var coords = [];
30833         
30834         for(var i = 0, finger; finger = touches[i]; i++){
30835             coords.push(finger.pageX, finger.pageY);
30836         }
30837         
30838         var x = Math.pow(coords[0] - coords[2], 2);
30839         var y = Math.pow(coords[1] - coords[3], 2);
30840         
30841         this.startDistance = Math.sqrt(x + y);
30842         
30843         this.startScale = this.scale;
30844         
30845         this.pinching = true;
30846         this.dragable = false;
30847         
30848     },
30849     
30850     onTouchMove : function(e)
30851     {
30852         if(!this.pinching && !this.dragable){
30853             return;
30854         }
30855         
30856         var touches = e.browserEvent.touches;
30857         
30858         if(!touches){
30859             return;
30860         }
30861         
30862         if(this.dragable){
30863             this.onMouseMove(e);
30864             return;
30865         }
30866         
30867         var coords = [];
30868         
30869         for(var i = 0, finger; finger = touches[i]; i++){
30870             coords.push(finger.pageX, finger.pageY);
30871         }
30872         
30873         var x = Math.pow(coords[0] - coords[2], 2);
30874         var y = Math.pow(coords[1] - coords[3], 2);
30875         
30876         this.endDistance = Math.sqrt(x + y);
30877         
30878         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30879         
30880         if(!this.zoomable()){
30881             this.scale = this.startScale;
30882             return;
30883         }
30884         
30885         this.draw();
30886         
30887     },
30888     
30889     onTouchEnd : function(e)
30890     {
30891         this.pinching = false;
30892         this.dragable = false;
30893         
30894     },
30895     
30896     process : function(file, crop)
30897     {
30898         if(this.loadMask){
30899             this.maskEl.mask(this.loadingText);
30900         }
30901         
30902         this.xhr = new XMLHttpRequest();
30903         
30904         file.xhr = this.xhr;
30905
30906         this.xhr.open(this.method, this.url, true);
30907         
30908         var headers = {
30909             "Accept": "application/json",
30910             "Cache-Control": "no-cache",
30911             "X-Requested-With": "XMLHttpRequest"
30912         };
30913         
30914         for (var headerName in headers) {
30915             var headerValue = headers[headerName];
30916             if (headerValue) {
30917                 this.xhr.setRequestHeader(headerName, headerValue);
30918             }
30919         }
30920         
30921         var _this = this;
30922         
30923         this.xhr.onload = function()
30924         {
30925             _this.xhrOnLoad(_this.xhr);
30926         }
30927         
30928         this.xhr.onerror = function()
30929         {
30930             _this.xhrOnError(_this.xhr);
30931         }
30932         
30933         var formData = new FormData();
30934
30935         formData.append('returnHTML', 'NO');
30936         
30937         if(crop){
30938             formData.append('crop', crop);
30939         }
30940         
30941         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30942             formData.append(this.paramName, file, file.name);
30943         }
30944         
30945         if(typeof(file.filename) != 'undefined'){
30946             formData.append('filename', file.filename);
30947         }
30948         
30949         if(typeof(file.mimetype) != 'undefined'){
30950             formData.append('mimetype', file.mimetype);
30951         }
30952         
30953         if(this.fireEvent('arrange', this, formData) != false){
30954             this.xhr.send(formData);
30955         };
30956     },
30957     
30958     xhrOnLoad : function(xhr)
30959     {
30960         if(this.loadMask){
30961             this.maskEl.unmask();
30962         }
30963         
30964         if (xhr.readyState !== 4) {
30965             this.fireEvent('exception', this, xhr);
30966             return;
30967         }
30968
30969         var response = Roo.decode(xhr.responseText);
30970         
30971         if(!response.success){
30972             this.fireEvent('exception', this, xhr);
30973             return;
30974         }
30975         
30976         var response = Roo.decode(xhr.responseText);
30977         
30978         this.fireEvent('upload', this, response);
30979         
30980     },
30981     
30982     xhrOnError : function()
30983     {
30984         if(this.loadMask){
30985             this.maskEl.unmask();
30986         }
30987         
30988         Roo.log('xhr on error');
30989         
30990         var response = Roo.decode(xhr.responseText);
30991           
30992         Roo.log(response);
30993         
30994     },
30995     
30996     prepare : function(file)
30997     {   
30998         if(this.loadMask){
30999             this.maskEl.mask(this.loadingText);
31000         }
31001         
31002         this.file = false;
31003         this.exif = {};
31004         
31005         if(typeof(file) === 'string'){
31006             this.loadCanvas(file);
31007             return;
31008         }
31009         
31010         if(!file || !this.urlAPI){
31011             return;
31012         }
31013         
31014         this.file = file;
31015         this.cropType = file.type;
31016         
31017         var _this = this;
31018         
31019         if(this.fireEvent('prepare', this, this.file) != false){
31020             
31021             var reader = new FileReader();
31022             
31023             reader.onload = function (e) {
31024                 if (e.target.error) {
31025                     Roo.log(e.target.error);
31026                     return;
31027                 }
31028                 
31029                 var buffer = e.target.result,
31030                     dataView = new DataView(buffer),
31031                     offset = 2,
31032                     maxOffset = dataView.byteLength - 4,
31033                     markerBytes,
31034                     markerLength;
31035                 
31036                 if (dataView.getUint16(0) === 0xffd8) {
31037                     while (offset < maxOffset) {
31038                         markerBytes = dataView.getUint16(offset);
31039                         
31040                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31041                             markerLength = dataView.getUint16(offset + 2) + 2;
31042                             if (offset + markerLength > dataView.byteLength) {
31043                                 Roo.log('Invalid meta data: Invalid segment size.');
31044                                 break;
31045                             }
31046                             
31047                             if(markerBytes == 0xffe1){
31048                                 _this.parseExifData(
31049                                     dataView,
31050                                     offset,
31051                                     markerLength
31052                                 );
31053                             }
31054                             
31055                             offset += markerLength;
31056                             
31057                             continue;
31058                         }
31059                         
31060                         break;
31061                     }
31062                     
31063                 }
31064                 
31065                 var url = _this.urlAPI.createObjectURL(_this.file);
31066                 
31067                 _this.loadCanvas(url);
31068                 
31069                 return;
31070             }
31071             
31072             reader.readAsArrayBuffer(this.file);
31073             
31074         }
31075         
31076     },
31077     
31078     parseExifData : function(dataView, offset, length)
31079     {
31080         var tiffOffset = offset + 10,
31081             littleEndian,
31082             dirOffset;
31083     
31084         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31085             // No Exif data, might be XMP data instead
31086             return;
31087         }
31088         
31089         // Check for the ASCII code for "Exif" (0x45786966):
31090         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31091             // No Exif data, might be XMP data instead
31092             return;
31093         }
31094         if (tiffOffset + 8 > dataView.byteLength) {
31095             Roo.log('Invalid Exif data: Invalid segment size.');
31096             return;
31097         }
31098         // Check for the two null bytes:
31099         if (dataView.getUint16(offset + 8) !== 0x0000) {
31100             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31101             return;
31102         }
31103         // Check the byte alignment:
31104         switch (dataView.getUint16(tiffOffset)) {
31105         case 0x4949:
31106             littleEndian = true;
31107             break;
31108         case 0x4D4D:
31109             littleEndian = false;
31110             break;
31111         default:
31112             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31113             return;
31114         }
31115         // Check for the TIFF tag marker (0x002A):
31116         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31117             Roo.log('Invalid Exif data: Missing TIFF marker.');
31118             return;
31119         }
31120         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31121         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31122         
31123         this.parseExifTags(
31124             dataView,
31125             tiffOffset,
31126             tiffOffset + dirOffset,
31127             littleEndian
31128         );
31129     },
31130     
31131     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31132     {
31133         var tagsNumber,
31134             dirEndOffset,
31135             i;
31136         if (dirOffset + 6 > dataView.byteLength) {
31137             Roo.log('Invalid Exif data: Invalid directory offset.');
31138             return;
31139         }
31140         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31141         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31142         if (dirEndOffset + 4 > dataView.byteLength) {
31143             Roo.log('Invalid Exif data: Invalid directory size.');
31144             return;
31145         }
31146         for (i = 0; i < tagsNumber; i += 1) {
31147             this.parseExifTag(
31148                 dataView,
31149                 tiffOffset,
31150                 dirOffset + 2 + 12 * i, // tag offset
31151                 littleEndian
31152             );
31153         }
31154         // Return the offset to the next directory:
31155         return dataView.getUint32(dirEndOffset, littleEndian);
31156     },
31157     
31158     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31159     {
31160         var tag = dataView.getUint16(offset, littleEndian);
31161         
31162         this.exif[tag] = this.getExifValue(
31163             dataView,
31164             tiffOffset,
31165             offset,
31166             dataView.getUint16(offset + 2, littleEndian), // tag type
31167             dataView.getUint32(offset + 4, littleEndian), // tag length
31168             littleEndian
31169         );
31170     },
31171     
31172     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31173     {
31174         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31175             tagSize,
31176             dataOffset,
31177             values,
31178             i,
31179             str,
31180             c;
31181     
31182         if (!tagType) {
31183             Roo.log('Invalid Exif data: Invalid tag type.');
31184             return;
31185         }
31186         
31187         tagSize = tagType.size * length;
31188         // Determine if the value is contained in the dataOffset bytes,
31189         // or if the value at the dataOffset is a pointer to the actual data:
31190         dataOffset = tagSize > 4 ?
31191                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31192         if (dataOffset + tagSize > dataView.byteLength) {
31193             Roo.log('Invalid Exif data: Invalid data offset.');
31194             return;
31195         }
31196         if (length === 1) {
31197             return tagType.getValue(dataView, dataOffset, littleEndian);
31198         }
31199         values = [];
31200         for (i = 0; i < length; i += 1) {
31201             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31202         }
31203         
31204         if (tagType.ascii) {
31205             str = '';
31206             // Concatenate the chars:
31207             for (i = 0; i < values.length; i += 1) {
31208                 c = values[i];
31209                 // Ignore the terminating NULL byte(s):
31210                 if (c === '\u0000') {
31211                     break;
31212                 }
31213                 str += c;
31214             }
31215             return str;
31216         }
31217         return values;
31218     }
31219     
31220 });
31221
31222 Roo.apply(Roo.bootstrap.UploadCropbox, {
31223     tags : {
31224         'Orientation': 0x0112
31225     },
31226     
31227     Orientation: {
31228             1: 0, //'top-left',
31229 //            2: 'top-right',
31230             3: 180, //'bottom-right',
31231 //            4: 'bottom-left',
31232 //            5: 'left-top',
31233             6: 90, //'right-top',
31234 //            7: 'right-bottom',
31235             8: 270 //'left-bottom'
31236     },
31237     
31238     exifTagTypes : {
31239         // byte, 8-bit unsigned int:
31240         1: {
31241             getValue: function (dataView, dataOffset) {
31242                 return dataView.getUint8(dataOffset);
31243             },
31244             size: 1
31245         },
31246         // ascii, 8-bit byte:
31247         2: {
31248             getValue: function (dataView, dataOffset) {
31249                 return String.fromCharCode(dataView.getUint8(dataOffset));
31250             },
31251             size: 1,
31252             ascii: true
31253         },
31254         // short, 16 bit int:
31255         3: {
31256             getValue: function (dataView, dataOffset, littleEndian) {
31257                 return dataView.getUint16(dataOffset, littleEndian);
31258             },
31259             size: 2
31260         },
31261         // long, 32 bit int:
31262         4: {
31263             getValue: function (dataView, dataOffset, littleEndian) {
31264                 return dataView.getUint32(dataOffset, littleEndian);
31265             },
31266             size: 4
31267         },
31268         // rational = two long values, first is numerator, second is denominator:
31269         5: {
31270             getValue: function (dataView, dataOffset, littleEndian) {
31271                 return dataView.getUint32(dataOffset, littleEndian) /
31272                     dataView.getUint32(dataOffset + 4, littleEndian);
31273             },
31274             size: 8
31275         },
31276         // slong, 32 bit signed int:
31277         9: {
31278             getValue: function (dataView, dataOffset, littleEndian) {
31279                 return dataView.getInt32(dataOffset, littleEndian);
31280             },
31281             size: 4
31282         },
31283         // srational, two slongs, first is numerator, second is denominator:
31284         10: {
31285             getValue: function (dataView, dataOffset, littleEndian) {
31286                 return dataView.getInt32(dataOffset, littleEndian) /
31287                     dataView.getInt32(dataOffset + 4, littleEndian);
31288             },
31289             size: 8
31290         }
31291     },
31292     
31293     footer : {
31294         STANDARD : [
31295             {
31296                 tag : 'div',
31297                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31298                 action : 'rotate-left',
31299                 cn : [
31300                     {
31301                         tag : 'button',
31302                         cls : 'btn btn-default',
31303                         html : '<i class="fa fa-undo"></i>'
31304                     }
31305                 ]
31306             },
31307             {
31308                 tag : 'div',
31309                 cls : 'btn-group roo-upload-cropbox-picture',
31310                 action : 'picture',
31311                 cn : [
31312                     {
31313                         tag : 'button',
31314                         cls : 'btn btn-default',
31315                         html : '<i class="fa fa-picture-o"></i>'
31316                     }
31317                 ]
31318             },
31319             {
31320                 tag : 'div',
31321                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31322                 action : 'rotate-right',
31323                 cn : [
31324                     {
31325                         tag : 'button',
31326                         cls : 'btn btn-default',
31327                         html : '<i class="fa fa-repeat"></i>'
31328                     }
31329                 ]
31330             }
31331         ],
31332         DOCUMENT : [
31333             {
31334                 tag : 'div',
31335                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31336                 action : 'rotate-left',
31337                 cn : [
31338                     {
31339                         tag : 'button',
31340                         cls : 'btn btn-default',
31341                         html : '<i class="fa fa-undo"></i>'
31342                     }
31343                 ]
31344             },
31345             {
31346                 tag : 'div',
31347                 cls : 'btn-group roo-upload-cropbox-download',
31348                 action : 'download',
31349                 cn : [
31350                     {
31351                         tag : 'button',
31352                         cls : 'btn btn-default',
31353                         html : '<i class="fa fa-download"></i>'
31354                     }
31355                 ]
31356             },
31357             {
31358                 tag : 'div',
31359                 cls : 'btn-group roo-upload-cropbox-crop',
31360                 action : 'crop',
31361                 cn : [
31362                     {
31363                         tag : 'button',
31364                         cls : 'btn btn-default',
31365                         html : '<i class="fa fa-crop"></i>'
31366                     }
31367                 ]
31368             },
31369             {
31370                 tag : 'div',
31371                 cls : 'btn-group roo-upload-cropbox-trash',
31372                 action : 'trash',
31373                 cn : [
31374                     {
31375                         tag : 'button',
31376                         cls : 'btn btn-default',
31377                         html : '<i class="fa fa-trash"></i>'
31378                     }
31379                 ]
31380             },
31381             {
31382                 tag : 'div',
31383                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31384                 action : 'rotate-right',
31385                 cn : [
31386                     {
31387                         tag : 'button',
31388                         cls : 'btn btn-default',
31389                         html : '<i class="fa fa-repeat"></i>'
31390                     }
31391                 ]
31392             }
31393         ],
31394         ROTATOR : [
31395             {
31396                 tag : 'div',
31397                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31398                 action : 'rotate-left',
31399                 cn : [
31400                     {
31401                         tag : 'button',
31402                         cls : 'btn btn-default',
31403                         html : '<i class="fa fa-undo"></i>'
31404                     }
31405                 ]
31406             },
31407             {
31408                 tag : 'div',
31409                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31410                 action : 'rotate-right',
31411                 cn : [
31412                     {
31413                         tag : 'button',
31414                         cls : 'btn btn-default',
31415                         html : '<i class="fa fa-repeat"></i>'
31416                     }
31417                 ]
31418             }
31419         ]
31420     }
31421 });
31422
31423 /*
31424 * Licence: LGPL
31425 */
31426
31427 /**
31428  * @class Roo.bootstrap.DocumentManager
31429  * @extends Roo.bootstrap.Component
31430  * Bootstrap DocumentManager class
31431  * @cfg {String} paramName default 'imageUpload'
31432  * @cfg {String} toolTipName default 'filename'
31433  * @cfg {String} method default POST
31434  * @cfg {String} url action url
31435  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31436  * @cfg {Boolean} multiple multiple upload default true
31437  * @cfg {Number} thumbSize default 300
31438  * @cfg {String} fieldLabel
31439  * @cfg {Number} labelWidth default 4
31440  * @cfg {String} labelAlign (left|top) default left
31441  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31442 * @cfg {Number} labellg set the width of label (1-12)
31443  * @cfg {Number} labelmd set the width of label (1-12)
31444  * @cfg {Number} labelsm set the width of label (1-12)
31445  * @cfg {Number} labelxs set the width of label (1-12)
31446  * 
31447  * @constructor
31448  * Create a new DocumentManager
31449  * @param {Object} config The config object
31450  */
31451
31452 Roo.bootstrap.DocumentManager = function(config){
31453     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31454     
31455     this.files = [];
31456     this.delegates = [];
31457     
31458     this.addEvents({
31459         /**
31460          * @event initial
31461          * Fire when initial the DocumentManager
31462          * @param {Roo.bootstrap.DocumentManager} this
31463          */
31464         "initial" : true,
31465         /**
31466          * @event inspect
31467          * inspect selected file
31468          * @param {Roo.bootstrap.DocumentManager} this
31469          * @param {File} file
31470          */
31471         "inspect" : true,
31472         /**
31473          * @event exception
31474          * Fire when xhr load exception
31475          * @param {Roo.bootstrap.DocumentManager} this
31476          * @param {XMLHttpRequest} xhr
31477          */
31478         "exception" : true,
31479         /**
31480          * @event afterupload
31481          * Fire when xhr load exception
31482          * @param {Roo.bootstrap.DocumentManager} this
31483          * @param {XMLHttpRequest} xhr
31484          */
31485         "afterupload" : true,
31486         /**
31487          * @event prepare
31488          * prepare the form data
31489          * @param {Roo.bootstrap.DocumentManager} this
31490          * @param {Object} formData
31491          */
31492         "prepare" : true,
31493         /**
31494          * @event remove
31495          * Fire when remove the file
31496          * @param {Roo.bootstrap.DocumentManager} this
31497          * @param {Object} file
31498          */
31499         "remove" : true,
31500         /**
31501          * @event refresh
31502          * Fire after refresh the file
31503          * @param {Roo.bootstrap.DocumentManager} this
31504          */
31505         "refresh" : true,
31506         /**
31507          * @event click
31508          * Fire after click the image
31509          * @param {Roo.bootstrap.DocumentManager} this
31510          * @param {Object} file
31511          */
31512         "click" : true,
31513         /**
31514          * @event edit
31515          * Fire when upload a image and editable set to true
31516          * @param {Roo.bootstrap.DocumentManager} this
31517          * @param {Object} file
31518          */
31519         "edit" : true,
31520         /**
31521          * @event beforeselectfile
31522          * Fire before select file
31523          * @param {Roo.bootstrap.DocumentManager} this
31524          */
31525         "beforeselectfile" : true,
31526         /**
31527          * @event process
31528          * Fire before process file
31529          * @param {Roo.bootstrap.DocumentManager} this
31530          * @param {Object} file
31531          */
31532         "process" : true,
31533         /**
31534          * @event previewrendered
31535          * Fire when preview rendered
31536          * @param {Roo.bootstrap.DocumentManager} this
31537          * @param {Object} file
31538          */
31539         "previewrendered" : true,
31540         /**
31541          */
31542         "previewResize" : true
31543         
31544     });
31545 };
31546
31547 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31548     
31549     boxes : 0,
31550     inputName : '',
31551     thumbSize : 300,
31552     multiple : true,
31553     files : false,
31554     method : 'POST',
31555     url : '',
31556     paramName : 'imageUpload',
31557     toolTipName : 'filename',
31558     fieldLabel : '',
31559     labelWidth : 4,
31560     labelAlign : 'left',
31561     editable : true,
31562     delegates : false,
31563     xhr : false, 
31564     
31565     labellg : 0,
31566     labelmd : 0,
31567     labelsm : 0,
31568     labelxs : 0,
31569     
31570     getAutoCreate : function()
31571     {   
31572         var managerWidget = {
31573             tag : 'div',
31574             cls : 'roo-document-manager',
31575             cn : [
31576                 {
31577                     tag : 'input',
31578                     cls : 'roo-document-manager-selector',
31579                     type : 'file'
31580                 },
31581                 {
31582                     tag : 'div',
31583                     cls : 'roo-document-manager-uploader',
31584                     cn : [
31585                         {
31586                             tag : 'div',
31587                             cls : 'roo-document-manager-upload-btn',
31588                             html : '<i class="fa fa-plus"></i>'
31589                         }
31590                     ]
31591                     
31592                 }
31593             ]
31594         };
31595         
31596         var content = [
31597             {
31598                 tag : 'div',
31599                 cls : 'column col-md-12',
31600                 cn : managerWidget
31601             }
31602         ];
31603         
31604         if(this.fieldLabel.length){
31605             
31606             content = [
31607                 {
31608                     tag : 'div',
31609                     cls : 'column col-md-12',
31610                     html : this.fieldLabel
31611                 },
31612                 {
31613                     tag : 'div',
31614                     cls : 'column col-md-12',
31615                     cn : managerWidget
31616                 }
31617             ];
31618
31619             if(this.labelAlign == 'left'){
31620                 content = [
31621                     {
31622                         tag : 'div',
31623                         cls : 'column',
31624                         html : this.fieldLabel
31625                     },
31626                     {
31627                         tag : 'div',
31628                         cls : 'column',
31629                         cn : managerWidget
31630                     }
31631                 ];
31632                 
31633                 if(this.labelWidth > 12){
31634                     content[0].style = "width: " + this.labelWidth + 'px';
31635                 }
31636
31637                 if(this.labelWidth < 13 && this.labelmd == 0){
31638                     this.labelmd = this.labelWidth;
31639                 }
31640
31641                 if(this.labellg > 0){
31642                     content[0].cls += ' col-lg-' + this.labellg;
31643                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31644                 }
31645
31646                 if(this.labelmd > 0){
31647                     content[0].cls += ' col-md-' + this.labelmd;
31648                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31649                 }
31650
31651                 if(this.labelsm > 0){
31652                     content[0].cls += ' col-sm-' + this.labelsm;
31653                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31654                 }
31655
31656                 if(this.labelxs > 0){
31657                     content[0].cls += ' col-xs-' + this.labelxs;
31658                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31659                 }
31660                 
31661             }
31662         }
31663         
31664         var cfg = {
31665             tag : 'div',
31666             cls : 'row clearfix',
31667             cn : content
31668         };
31669         
31670         return cfg;
31671         
31672     },
31673     
31674     initEvents : function()
31675     {
31676         this.managerEl = this.el.select('.roo-document-manager', true).first();
31677         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31678         
31679         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31680         this.selectorEl.hide();
31681         
31682         if(this.multiple){
31683             this.selectorEl.attr('multiple', 'multiple');
31684         }
31685         
31686         this.selectorEl.on('change', this.onFileSelected, this);
31687         
31688         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31689         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31690         
31691         this.uploader.on('click', this.onUploaderClick, this);
31692         
31693         this.renderProgressDialog();
31694         
31695         var _this = this;
31696         
31697         window.addEventListener("resize", function() { _this.refresh(); } );
31698         
31699         this.fireEvent('initial', this);
31700     },
31701     
31702     renderProgressDialog : function()
31703     {
31704         var _this = this;
31705         
31706         this.progressDialog = new Roo.bootstrap.Modal({
31707             cls : 'roo-document-manager-progress-dialog',
31708             allow_close : false,
31709             animate : false,
31710             title : '',
31711             buttons : [
31712                 {
31713                     name  :'cancel',
31714                     weight : 'danger',
31715                     html : 'Cancel'
31716                 }
31717             ], 
31718             listeners : { 
31719                 btnclick : function() {
31720                     _this.uploadCancel();
31721                     this.hide();
31722                 }
31723             }
31724         });
31725          
31726         this.progressDialog.render(Roo.get(document.body));
31727          
31728         this.progress = new Roo.bootstrap.Progress({
31729             cls : 'roo-document-manager-progress',
31730             active : true,
31731             striped : true
31732         });
31733         
31734         this.progress.render(this.progressDialog.getChildContainer());
31735         
31736         this.progressBar = new Roo.bootstrap.ProgressBar({
31737             cls : 'roo-document-manager-progress-bar',
31738             aria_valuenow : 0,
31739             aria_valuemin : 0,
31740             aria_valuemax : 12,
31741             panel : 'success'
31742         });
31743         
31744         this.progressBar.render(this.progress.getChildContainer());
31745     },
31746     
31747     onUploaderClick : function(e)
31748     {
31749         e.preventDefault();
31750      
31751         if(this.fireEvent('beforeselectfile', this) != false){
31752             this.selectorEl.dom.click();
31753         }
31754         
31755     },
31756     
31757     onFileSelected : function(e)
31758     {
31759         e.preventDefault();
31760         
31761         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31762             return;
31763         }
31764         
31765         Roo.each(this.selectorEl.dom.files, function(file){
31766             if(this.fireEvent('inspect', this, file) != false){
31767                 this.files.push(file);
31768             }
31769         }, this);
31770         
31771         this.queue();
31772         
31773     },
31774     
31775     queue : function()
31776     {
31777         this.selectorEl.dom.value = '';
31778         
31779         if(!this.files || !this.files.length){
31780             return;
31781         }
31782         
31783         if(this.boxes > 0 && this.files.length > this.boxes){
31784             this.files = this.files.slice(0, this.boxes);
31785         }
31786         
31787         this.uploader.show();
31788         
31789         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31790             this.uploader.hide();
31791         }
31792         
31793         var _this = this;
31794         
31795         var files = [];
31796         
31797         var docs = [];
31798         
31799         Roo.each(this.files, function(file){
31800             
31801             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31802                 var f = this.renderPreview(file);
31803                 files.push(f);
31804                 return;
31805             }
31806             
31807             if(file.type.indexOf('image') != -1){
31808                 this.delegates.push(
31809                     (function(){
31810                         _this.process(file);
31811                     }).createDelegate(this)
31812                 );
31813         
31814                 return;
31815             }
31816             
31817             docs.push(
31818                 (function(){
31819                     _this.process(file);
31820                 }).createDelegate(this)
31821             );
31822             
31823         }, this);
31824         
31825         this.files = files;
31826         
31827         this.delegates = this.delegates.concat(docs);
31828         
31829         if(!this.delegates.length){
31830             this.refresh();
31831             return;
31832         }
31833         
31834         this.progressBar.aria_valuemax = this.delegates.length;
31835         
31836         this.arrange();
31837         
31838         return;
31839     },
31840     
31841     arrange : function()
31842     {
31843         if(!this.delegates.length){
31844             this.progressDialog.hide();
31845             this.refresh();
31846             return;
31847         }
31848         
31849         var delegate = this.delegates.shift();
31850         
31851         this.progressDialog.show();
31852         
31853         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31854         
31855         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31856         
31857         delegate();
31858     },
31859     
31860     refresh : function()
31861     {
31862         this.uploader.show();
31863         
31864         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31865             this.uploader.hide();
31866         }
31867         
31868         Roo.isTouch ? this.closable(false) : this.closable(true);
31869         
31870         this.fireEvent('refresh', this);
31871     },
31872     
31873     onRemove : function(e, el, o)
31874     {
31875         e.preventDefault();
31876         
31877         this.fireEvent('remove', this, o);
31878         
31879     },
31880     
31881     remove : function(o)
31882     {
31883         var files = [];
31884         
31885         Roo.each(this.files, function(file){
31886             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31887                 files.push(file);
31888                 return;
31889             }
31890
31891             o.target.remove();
31892
31893         }, this);
31894         
31895         this.files = files;
31896         
31897         this.refresh();
31898     },
31899     
31900     clear : function()
31901     {
31902         Roo.each(this.files, function(file){
31903             if(!file.target){
31904                 return;
31905             }
31906             
31907             file.target.remove();
31908
31909         }, this);
31910         
31911         this.files = [];
31912         
31913         this.refresh();
31914     },
31915     
31916     onClick : function(e, el, o)
31917     {
31918         e.preventDefault();
31919         
31920         this.fireEvent('click', this, o);
31921         
31922     },
31923     
31924     closable : function(closable)
31925     {
31926         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31927             
31928             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31929             
31930             if(closable){
31931                 el.show();
31932                 return;
31933             }
31934             
31935             el.hide();
31936             
31937         }, this);
31938     },
31939     
31940     xhrOnLoad : function(xhr)
31941     {
31942         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31943             el.remove();
31944         }, this);
31945         
31946         if (xhr.readyState !== 4) {
31947             this.arrange();
31948             this.fireEvent('exception', this, xhr);
31949             return;
31950         }
31951
31952         var response = Roo.decode(xhr.responseText);
31953         
31954         if(!response.success){
31955             this.arrange();
31956             this.fireEvent('exception', this, xhr);
31957             return;
31958         }
31959         
31960         var file = this.renderPreview(response.data);
31961         
31962         this.files.push(file);
31963         
31964         this.arrange();
31965         
31966         this.fireEvent('afterupload', this, xhr);
31967         
31968     },
31969     
31970     xhrOnError : function(xhr)
31971     {
31972         Roo.log('xhr on error');
31973         
31974         var response = Roo.decode(xhr.responseText);
31975           
31976         Roo.log(response);
31977         
31978         this.arrange();
31979     },
31980     
31981     process : function(file)
31982     {
31983         if(this.fireEvent('process', this, file) !== false){
31984             if(this.editable && file.type.indexOf('image') != -1){
31985                 this.fireEvent('edit', this, file);
31986                 return;
31987             }
31988
31989             this.uploadStart(file, false);
31990
31991             return;
31992         }
31993         
31994     },
31995     
31996     uploadStart : function(file, crop)
31997     {
31998         this.xhr = new XMLHttpRequest();
31999         
32000         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32001             this.arrange();
32002             return;
32003         }
32004         
32005         file.xhr = this.xhr;
32006             
32007         this.managerEl.createChild({
32008             tag : 'div',
32009             cls : 'roo-document-manager-loading',
32010             cn : [
32011                 {
32012                     tag : 'div',
32013                     tooltip : file.name,
32014                     cls : 'roo-document-manager-thumb',
32015                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32016                 }
32017             ]
32018
32019         });
32020
32021         this.xhr.open(this.method, this.url, true);
32022         
32023         var headers = {
32024             "Accept": "application/json",
32025             "Cache-Control": "no-cache",
32026             "X-Requested-With": "XMLHttpRequest"
32027         };
32028         
32029         for (var headerName in headers) {
32030             var headerValue = headers[headerName];
32031             if (headerValue) {
32032                 this.xhr.setRequestHeader(headerName, headerValue);
32033             }
32034         }
32035         
32036         var _this = this;
32037         
32038         this.xhr.onload = function()
32039         {
32040             _this.xhrOnLoad(_this.xhr);
32041         }
32042         
32043         this.xhr.onerror = function()
32044         {
32045             _this.xhrOnError(_this.xhr);
32046         }
32047         
32048         var formData = new FormData();
32049
32050         formData.append('returnHTML', 'NO');
32051         
32052         if(crop){
32053             formData.append('crop', crop);
32054         }
32055         
32056         formData.append(this.paramName, file, file.name);
32057         
32058         var options = {
32059             file : file, 
32060             manually : false
32061         };
32062         
32063         if(this.fireEvent('prepare', this, formData, options) != false){
32064             
32065             if(options.manually){
32066                 return;
32067             }
32068             
32069             this.xhr.send(formData);
32070             return;
32071         };
32072         
32073         this.uploadCancel();
32074     },
32075     
32076     uploadCancel : function()
32077     {
32078         if (this.xhr) {
32079             this.xhr.abort();
32080         }
32081         
32082         this.delegates = [];
32083         
32084         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32085             el.remove();
32086         }, this);
32087         
32088         this.arrange();
32089     },
32090     
32091     renderPreview : function(file)
32092     {
32093         if(typeof(file.target) != 'undefined' && file.target){
32094             return file;
32095         }
32096         
32097         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32098         
32099         var previewEl = this.managerEl.createChild({
32100             tag : 'div',
32101             cls : 'roo-document-manager-preview',
32102             cn : [
32103                 {
32104                     tag : 'div',
32105                     tooltip : file[this.toolTipName],
32106                     cls : 'roo-document-manager-thumb',
32107                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32108                 },
32109                 {
32110                     tag : 'button',
32111                     cls : 'close',
32112                     html : '<i class="fa fa-times-circle"></i>'
32113                 }
32114             ]
32115         });
32116
32117         var close = previewEl.select('button.close', true).first();
32118
32119         close.on('click', this.onRemove, this, file);
32120
32121         file.target = previewEl;
32122
32123         var image = previewEl.select('img', true).first();
32124         
32125         var _this = this;
32126         
32127         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32128         
32129         image.on('click', this.onClick, this, file);
32130         
32131         this.fireEvent('previewrendered', this, file);
32132         
32133         return file;
32134         
32135     },
32136     
32137     onPreviewLoad : function(file, image)
32138     {
32139         if(typeof(file.target) == 'undefined' || !file.target){
32140             return;
32141         }
32142         
32143         var width = image.dom.naturalWidth || image.dom.width;
32144         var height = image.dom.naturalHeight || image.dom.height;
32145         
32146         if(!this.previewResize) {
32147             return;
32148         }
32149         
32150         if(width > height){
32151             file.target.addClass('wide');
32152             return;
32153         }
32154         
32155         file.target.addClass('tall');
32156         return;
32157         
32158     },
32159     
32160     uploadFromSource : function(file, crop)
32161     {
32162         this.xhr = new XMLHttpRequest();
32163         
32164         this.managerEl.createChild({
32165             tag : 'div',
32166             cls : 'roo-document-manager-loading',
32167             cn : [
32168                 {
32169                     tag : 'div',
32170                     tooltip : file.name,
32171                     cls : 'roo-document-manager-thumb',
32172                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32173                 }
32174             ]
32175
32176         });
32177
32178         this.xhr.open(this.method, this.url, true);
32179         
32180         var headers = {
32181             "Accept": "application/json",
32182             "Cache-Control": "no-cache",
32183             "X-Requested-With": "XMLHttpRequest"
32184         };
32185         
32186         for (var headerName in headers) {
32187             var headerValue = headers[headerName];
32188             if (headerValue) {
32189                 this.xhr.setRequestHeader(headerName, headerValue);
32190             }
32191         }
32192         
32193         var _this = this;
32194         
32195         this.xhr.onload = function()
32196         {
32197             _this.xhrOnLoad(_this.xhr);
32198         }
32199         
32200         this.xhr.onerror = function()
32201         {
32202             _this.xhrOnError(_this.xhr);
32203         }
32204         
32205         var formData = new FormData();
32206
32207         formData.append('returnHTML', 'NO');
32208         
32209         formData.append('crop', crop);
32210         
32211         if(typeof(file.filename) != 'undefined'){
32212             formData.append('filename', file.filename);
32213         }
32214         
32215         if(typeof(file.mimetype) != 'undefined'){
32216             formData.append('mimetype', file.mimetype);
32217         }
32218         
32219         Roo.log(formData);
32220         
32221         if(this.fireEvent('prepare', this, formData) != false){
32222             this.xhr.send(formData);
32223         };
32224     }
32225 });
32226
32227 /*
32228 * Licence: LGPL
32229 */
32230
32231 /**
32232  * @class Roo.bootstrap.DocumentViewer
32233  * @extends Roo.bootstrap.Component
32234  * Bootstrap DocumentViewer class
32235  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32236  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32237  * 
32238  * @constructor
32239  * Create a new DocumentViewer
32240  * @param {Object} config The config object
32241  */
32242
32243 Roo.bootstrap.DocumentViewer = function(config){
32244     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32245     
32246     this.addEvents({
32247         /**
32248          * @event initial
32249          * Fire after initEvent
32250          * @param {Roo.bootstrap.DocumentViewer} this
32251          */
32252         "initial" : true,
32253         /**
32254          * @event click
32255          * Fire after click
32256          * @param {Roo.bootstrap.DocumentViewer} this
32257          */
32258         "click" : true,
32259         /**
32260          * @event download
32261          * Fire after download button
32262          * @param {Roo.bootstrap.DocumentViewer} this
32263          */
32264         "download" : true,
32265         /**
32266          * @event trash
32267          * Fire after trash button
32268          * @param {Roo.bootstrap.DocumentViewer} this
32269          */
32270         "trash" : true
32271         
32272     });
32273 };
32274
32275 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32276     
32277     showDownload : true,
32278     
32279     showTrash : true,
32280     
32281     getAutoCreate : function()
32282     {
32283         var cfg = {
32284             tag : 'div',
32285             cls : 'roo-document-viewer',
32286             cn : [
32287                 {
32288                     tag : 'div',
32289                     cls : 'roo-document-viewer-body',
32290                     cn : [
32291                         {
32292                             tag : 'div',
32293                             cls : 'roo-document-viewer-thumb',
32294                             cn : [
32295                                 {
32296                                     tag : 'img',
32297                                     cls : 'roo-document-viewer-image'
32298                                 }
32299                             ]
32300                         }
32301                     ]
32302                 },
32303                 {
32304                     tag : 'div',
32305                     cls : 'roo-document-viewer-footer',
32306                     cn : {
32307                         tag : 'div',
32308                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32309                         cn : [
32310                             {
32311                                 tag : 'div',
32312                                 cls : 'btn-group roo-document-viewer-download',
32313                                 cn : [
32314                                     {
32315                                         tag : 'button',
32316                                         cls : 'btn btn-default',
32317                                         html : '<i class="fa fa-download"></i>'
32318                                     }
32319                                 ]
32320                             },
32321                             {
32322                                 tag : 'div',
32323                                 cls : 'btn-group roo-document-viewer-trash',
32324                                 cn : [
32325                                     {
32326                                         tag : 'button',
32327                                         cls : 'btn btn-default',
32328                                         html : '<i class="fa fa-trash"></i>'
32329                                     }
32330                                 ]
32331                             }
32332                         ]
32333                     }
32334                 }
32335             ]
32336         };
32337         
32338         return cfg;
32339     },
32340     
32341     initEvents : function()
32342     {
32343         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32344         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32345         
32346         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32347         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32348         
32349         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32350         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32351         
32352         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32353         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32354         
32355         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32356         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32357         
32358         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32359         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32360         
32361         this.bodyEl.on('click', this.onClick, this);
32362         this.downloadBtn.on('click', this.onDownload, this);
32363         this.trashBtn.on('click', this.onTrash, this);
32364         
32365         this.downloadBtn.hide();
32366         this.trashBtn.hide();
32367         
32368         if(this.showDownload){
32369             this.downloadBtn.show();
32370         }
32371         
32372         if(this.showTrash){
32373             this.trashBtn.show();
32374         }
32375         
32376         if(!this.showDownload && !this.showTrash) {
32377             this.footerEl.hide();
32378         }
32379         
32380     },
32381     
32382     initial : function()
32383     {
32384         this.fireEvent('initial', this);
32385         
32386     },
32387     
32388     onClick : function(e)
32389     {
32390         e.preventDefault();
32391         
32392         this.fireEvent('click', this);
32393     },
32394     
32395     onDownload : function(e)
32396     {
32397         e.preventDefault();
32398         
32399         this.fireEvent('download', this);
32400     },
32401     
32402     onTrash : function(e)
32403     {
32404         e.preventDefault();
32405         
32406         this.fireEvent('trash', this);
32407     }
32408     
32409 });
32410 /*
32411  * - LGPL
32412  *
32413  * nav progress bar
32414  * 
32415  */
32416
32417 /**
32418  * @class Roo.bootstrap.NavProgressBar
32419  * @extends Roo.bootstrap.Component
32420  * Bootstrap NavProgressBar class
32421  * 
32422  * @constructor
32423  * Create a new nav progress bar
32424  * @param {Object} config The config object
32425  */
32426
32427 Roo.bootstrap.NavProgressBar = function(config){
32428     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32429
32430     this.bullets = this.bullets || [];
32431    
32432 //    Roo.bootstrap.NavProgressBar.register(this);
32433      this.addEvents({
32434         /**
32435              * @event changed
32436              * Fires when the active item changes
32437              * @param {Roo.bootstrap.NavProgressBar} this
32438              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32439              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32440          */
32441         'changed': true
32442      });
32443     
32444 };
32445
32446 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32447     
32448     bullets : [],
32449     barItems : [],
32450     
32451     getAutoCreate : function()
32452     {
32453         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32454         
32455         cfg = {
32456             tag : 'div',
32457             cls : 'roo-navigation-bar-group',
32458             cn : [
32459                 {
32460                     tag : 'div',
32461                     cls : 'roo-navigation-top-bar'
32462                 },
32463                 {
32464                     tag : 'div',
32465                     cls : 'roo-navigation-bullets-bar',
32466                     cn : [
32467                         {
32468                             tag : 'ul',
32469                             cls : 'roo-navigation-bar'
32470                         }
32471                     ]
32472                 },
32473                 
32474                 {
32475                     tag : 'div',
32476                     cls : 'roo-navigation-bottom-bar'
32477                 }
32478             ]
32479             
32480         };
32481         
32482         return cfg;
32483         
32484     },
32485     
32486     initEvents: function() 
32487     {
32488         
32489     },
32490     
32491     onRender : function(ct, position) 
32492     {
32493         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32494         
32495         if(this.bullets.length){
32496             Roo.each(this.bullets, function(b){
32497                this.addItem(b);
32498             }, this);
32499         }
32500         
32501         this.format();
32502         
32503     },
32504     
32505     addItem : function(cfg)
32506     {
32507         var item = new Roo.bootstrap.NavProgressItem(cfg);
32508         
32509         item.parentId = this.id;
32510         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32511         
32512         if(cfg.html){
32513             var top = new Roo.bootstrap.Element({
32514                 tag : 'div',
32515                 cls : 'roo-navigation-bar-text'
32516             });
32517             
32518             var bottom = new Roo.bootstrap.Element({
32519                 tag : 'div',
32520                 cls : 'roo-navigation-bar-text'
32521             });
32522             
32523             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32524             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32525             
32526             var topText = new Roo.bootstrap.Element({
32527                 tag : 'span',
32528                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32529             });
32530             
32531             var bottomText = new Roo.bootstrap.Element({
32532                 tag : 'span',
32533                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32534             });
32535             
32536             topText.onRender(top.el, null);
32537             bottomText.onRender(bottom.el, null);
32538             
32539             item.topEl = top;
32540             item.bottomEl = bottom;
32541         }
32542         
32543         this.barItems.push(item);
32544         
32545         return item;
32546     },
32547     
32548     getActive : function()
32549     {
32550         var active = false;
32551         
32552         Roo.each(this.barItems, function(v){
32553             
32554             if (!v.isActive()) {
32555                 return;
32556             }
32557             
32558             active = v;
32559             return false;
32560             
32561         });
32562         
32563         return active;
32564     },
32565     
32566     setActiveItem : function(item)
32567     {
32568         var prev = false;
32569         
32570         Roo.each(this.barItems, function(v){
32571             if (v.rid == item.rid) {
32572                 return ;
32573             }
32574             
32575             if (v.isActive()) {
32576                 v.setActive(false);
32577                 prev = v;
32578             }
32579         });
32580
32581         item.setActive(true);
32582         
32583         this.fireEvent('changed', this, item, prev);
32584     },
32585     
32586     getBarItem: function(rid)
32587     {
32588         var ret = false;
32589         
32590         Roo.each(this.barItems, function(e) {
32591             if (e.rid != rid) {
32592                 return;
32593             }
32594             
32595             ret =  e;
32596             return false;
32597         });
32598         
32599         return ret;
32600     },
32601     
32602     indexOfItem : function(item)
32603     {
32604         var index = false;
32605         
32606         Roo.each(this.barItems, function(v, i){
32607             
32608             if (v.rid != item.rid) {
32609                 return;
32610             }
32611             
32612             index = i;
32613             return false
32614         });
32615         
32616         return index;
32617     },
32618     
32619     setActiveNext : function()
32620     {
32621         var i = this.indexOfItem(this.getActive());
32622         
32623         if (i > this.barItems.length) {
32624             return;
32625         }
32626         
32627         this.setActiveItem(this.barItems[i+1]);
32628     },
32629     
32630     setActivePrev : function()
32631     {
32632         var i = this.indexOfItem(this.getActive());
32633         
32634         if (i  < 1) {
32635             return;
32636         }
32637         
32638         this.setActiveItem(this.barItems[i-1]);
32639     },
32640     
32641     format : function()
32642     {
32643         if(!this.barItems.length){
32644             return;
32645         }
32646      
32647         var width = 100 / this.barItems.length;
32648         
32649         Roo.each(this.barItems, function(i){
32650             i.el.setStyle('width', width + '%');
32651             i.topEl.el.setStyle('width', width + '%');
32652             i.bottomEl.el.setStyle('width', width + '%');
32653         }, this);
32654         
32655     }
32656     
32657 });
32658 /*
32659  * - LGPL
32660  *
32661  * Nav Progress Item
32662  * 
32663  */
32664
32665 /**
32666  * @class Roo.bootstrap.NavProgressItem
32667  * @extends Roo.bootstrap.Component
32668  * Bootstrap NavProgressItem class
32669  * @cfg {String} rid the reference id
32670  * @cfg {Boolean} active (true|false) Is item active default false
32671  * @cfg {Boolean} disabled (true|false) Is item active default false
32672  * @cfg {String} html
32673  * @cfg {String} position (top|bottom) text position default bottom
32674  * @cfg {String} icon show icon instead of number
32675  * 
32676  * @constructor
32677  * Create a new NavProgressItem
32678  * @param {Object} config The config object
32679  */
32680 Roo.bootstrap.NavProgressItem = function(config){
32681     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32682     this.addEvents({
32683         // raw events
32684         /**
32685          * @event click
32686          * The raw click event for the entire grid.
32687          * @param {Roo.bootstrap.NavProgressItem} this
32688          * @param {Roo.EventObject} e
32689          */
32690         "click" : true
32691     });
32692    
32693 };
32694
32695 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32696     
32697     rid : '',
32698     active : false,
32699     disabled : false,
32700     html : '',
32701     position : 'bottom',
32702     icon : false,
32703     
32704     getAutoCreate : function()
32705     {
32706         var iconCls = 'roo-navigation-bar-item-icon';
32707         
32708         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32709         
32710         var cfg = {
32711             tag: 'li',
32712             cls: 'roo-navigation-bar-item',
32713             cn : [
32714                 {
32715                     tag : 'i',
32716                     cls : iconCls
32717                 }
32718             ]
32719         };
32720         
32721         if(this.active){
32722             cfg.cls += ' active';
32723         }
32724         if(this.disabled){
32725             cfg.cls += ' disabled';
32726         }
32727         
32728         return cfg;
32729     },
32730     
32731     disable : function()
32732     {
32733         this.setDisabled(true);
32734     },
32735     
32736     enable : function()
32737     {
32738         this.setDisabled(false);
32739     },
32740     
32741     initEvents: function() 
32742     {
32743         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32744         
32745         this.iconEl.on('click', this.onClick, this);
32746     },
32747     
32748     onClick : function(e)
32749     {
32750         e.preventDefault();
32751         
32752         if(this.disabled){
32753             return;
32754         }
32755         
32756         if(this.fireEvent('click', this, e) === false){
32757             return;
32758         };
32759         
32760         this.parent().setActiveItem(this);
32761     },
32762     
32763     isActive: function () 
32764     {
32765         return this.active;
32766     },
32767     
32768     setActive : function(state)
32769     {
32770         if(this.active == state){
32771             return;
32772         }
32773         
32774         this.active = state;
32775         
32776         if (state) {
32777             this.el.addClass('active');
32778             return;
32779         }
32780         
32781         this.el.removeClass('active');
32782         
32783         return;
32784     },
32785     
32786     setDisabled : function(state)
32787     {
32788         if(this.disabled == state){
32789             return;
32790         }
32791         
32792         this.disabled = state;
32793         
32794         if (state) {
32795             this.el.addClass('disabled');
32796             return;
32797         }
32798         
32799         this.el.removeClass('disabled');
32800     },
32801     
32802     tooltipEl : function()
32803     {
32804         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32805     }
32806 });
32807  
32808
32809  /*
32810  * - LGPL
32811  *
32812  * FieldLabel
32813  * 
32814  */
32815
32816 /**
32817  * @class Roo.bootstrap.FieldLabel
32818  * @extends Roo.bootstrap.Component
32819  * Bootstrap FieldLabel class
32820  * @cfg {String} html contents of the element
32821  * @cfg {String} tag tag of the element default label
32822  * @cfg {String} cls class of the element
32823  * @cfg {String} target label target 
32824  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32825  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32826  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32827  * @cfg {String} iconTooltip default "This field is required"
32828  * @cfg {String} indicatorpos (left|right) default left
32829  * 
32830  * @constructor
32831  * Create a new FieldLabel
32832  * @param {Object} config The config object
32833  */
32834
32835 Roo.bootstrap.FieldLabel = function(config){
32836     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32837     
32838     this.addEvents({
32839             /**
32840              * @event invalid
32841              * Fires after the field has been marked as invalid.
32842              * @param {Roo.form.FieldLabel} this
32843              * @param {String} msg The validation message
32844              */
32845             invalid : true,
32846             /**
32847              * @event valid
32848              * Fires after the field has been validated with no errors.
32849              * @param {Roo.form.FieldLabel} this
32850              */
32851             valid : true
32852         });
32853 };
32854
32855 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32856     
32857     tag: 'label',
32858     cls: '',
32859     html: '',
32860     target: '',
32861     allowBlank : true,
32862     invalidClass : 'has-warning',
32863     validClass : 'has-success',
32864     iconTooltip : 'This field is required',
32865     indicatorpos : 'left',
32866     
32867     getAutoCreate : function(){
32868         
32869         var cls = "";
32870         if (!this.allowBlank) {
32871             cls  = "visible";
32872         }
32873         
32874         var cfg = {
32875             tag : this.tag,
32876             cls : 'roo-bootstrap-field-label ' + this.cls,
32877             for : this.target,
32878             cn : [
32879                 {
32880                     tag : 'i',
32881                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32882                     tooltip : this.iconTooltip
32883                 },
32884                 {
32885                     tag : 'span',
32886                     html : this.html
32887                 }
32888             ] 
32889         };
32890         
32891         if(this.indicatorpos == 'right'){
32892             var cfg = {
32893                 tag : this.tag,
32894                 cls : 'roo-bootstrap-field-label ' + this.cls,
32895                 for : this.target,
32896                 cn : [
32897                     {
32898                         tag : 'span',
32899                         html : this.html
32900                     },
32901                     {
32902                         tag : 'i',
32903                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32904                         tooltip : this.iconTooltip
32905                     }
32906                 ] 
32907             };
32908         }
32909         
32910         return cfg;
32911     },
32912     
32913     initEvents: function() 
32914     {
32915         Roo.bootstrap.Element.superclass.initEvents.call(this);
32916         
32917         this.indicator = this.indicatorEl();
32918         
32919         if(this.indicator){
32920             this.indicator.removeClass('visible');
32921             this.indicator.addClass('invisible');
32922         }
32923         
32924         Roo.bootstrap.FieldLabel.register(this);
32925     },
32926     
32927     indicatorEl : function()
32928     {
32929         var indicator = this.el.select('i.roo-required-indicator',true).first();
32930         
32931         if(!indicator){
32932             return false;
32933         }
32934         
32935         return indicator;
32936         
32937     },
32938     
32939     /**
32940      * Mark this field as valid
32941      */
32942     markValid : function()
32943     {
32944         if(this.indicator){
32945             this.indicator.removeClass('visible');
32946             this.indicator.addClass('invisible');
32947         }
32948         if (Roo.bootstrap.version == 3) {
32949             this.el.removeClass(this.invalidClass);
32950             this.el.addClass(this.validClass);
32951         } else {
32952             this.el.removeClass('is-invalid');
32953             this.el.addClass('is-valid');
32954         }
32955         
32956         
32957         this.fireEvent('valid', this);
32958     },
32959     
32960     /**
32961      * Mark this field as invalid
32962      * @param {String} msg The validation message
32963      */
32964     markInvalid : function(msg)
32965     {
32966         if(this.indicator){
32967             this.indicator.removeClass('invisible');
32968             this.indicator.addClass('visible');
32969         }
32970           if (Roo.bootstrap.version == 3) {
32971             this.el.removeClass(this.validClass);
32972             this.el.addClass(this.invalidClass);
32973         } else {
32974             this.el.removeClass('is-valid');
32975             this.el.addClass('is-invalid');
32976         }
32977         
32978         
32979         this.fireEvent('invalid', this, msg);
32980     }
32981     
32982    
32983 });
32984
32985 Roo.apply(Roo.bootstrap.FieldLabel, {
32986     
32987     groups: {},
32988     
32989      /**
32990     * register a FieldLabel Group
32991     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32992     */
32993     register : function(label)
32994     {
32995         if(this.groups.hasOwnProperty(label.target)){
32996             return;
32997         }
32998      
32999         this.groups[label.target] = label;
33000         
33001     },
33002     /**
33003     * fetch a FieldLabel Group based on the target
33004     * @param {string} target
33005     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33006     */
33007     get: function(target) {
33008         if (typeof(this.groups[target]) == 'undefined') {
33009             return false;
33010         }
33011         
33012         return this.groups[target] ;
33013     }
33014 });
33015
33016  
33017
33018  /*
33019  * - LGPL
33020  *
33021  * page DateSplitField.
33022  * 
33023  */
33024
33025
33026 /**
33027  * @class Roo.bootstrap.DateSplitField
33028  * @extends Roo.bootstrap.Component
33029  * Bootstrap DateSplitField class
33030  * @cfg {string} fieldLabel - the label associated
33031  * @cfg {Number} labelWidth set the width of label (0-12)
33032  * @cfg {String} labelAlign (top|left)
33033  * @cfg {Boolean} dayAllowBlank (true|false) default false
33034  * @cfg {Boolean} monthAllowBlank (true|false) default false
33035  * @cfg {Boolean} yearAllowBlank (true|false) default false
33036  * @cfg {string} dayPlaceholder 
33037  * @cfg {string} monthPlaceholder
33038  * @cfg {string} yearPlaceholder
33039  * @cfg {string} dayFormat default 'd'
33040  * @cfg {string} monthFormat default 'm'
33041  * @cfg {string} yearFormat default 'Y'
33042  * @cfg {Number} labellg set the width of label (1-12)
33043  * @cfg {Number} labelmd set the width of label (1-12)
33044  * @cfg {Number} labelsm set the width of label (1-12)
33045  * @cfg {Number} labelxs set the width of label (1-12)
33046
33047  *     
33048  * @constructor
33049  * Create a new DateSplitField
33050  * @param {Object} config The config object
33051  */
33052
33053 Roo.bootstrap.DateSplitField = function(config){
33054     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33055     
33056     this.addEvents({
33057         // raw events
33058          /**
33059          * @event years
33060          * getting the data of years
33061          * @param {Roo.bootstrap.DateSplitField} this
33062          * @param {Object} years
33063          */
33064         "years" : true,
33065         /**
33066          * @event days
33067          * getting the data of days
33068          * @param {Roo.bootstrap.DateSplitField} this
33069          * @param {Object} days
33070          */
33071         "days" : true,
33072         /**
33073          * @event invalid
33074          * Fires after the field has been marked as invalid.
33075          * @param {Roo.form.Field} this
33076          * @param {String} msg The validation message
33077          */
33078         invalid : true,
33079        /**
33080          * @event valid
33081          * Fires after the field has been validated with no errors.
33082          * @param {Roo.form.Field} this
33083          */
33084         valid : true
33085     });
33086 };
33087
33088 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33089     
33090     fieldLabel : '',
33091     labelAlign : 'top',
33092     labelWidth : 3,
33093     dayAllowBlank : false,
33094     monthAllowBlank : false,
33095     yearAllowBlank : false,
33096     dayPlaceholder : '',
33097     monthPlaceholder : '',
33098     yearPlaceholder : '',
33099     dayFormat : 'd',
33100     monthFormat : 'm',
33101     yearFormat : 'Y',
33102     isFormField : true,
33103     labellg : 0,
33104     labelmd : 0,
33105     labelsm : 0,
33106     labelxs : 0,
33107     
33108     getAutoCreate : function()
33109     {
33110         var cfg = {
33111             tag : 'div',
33112             cls : 'row roo-date-split-field-group',
33113             cn : [
33114                 {
33115                     tag : 'input',
33116                     type : 'hidden',
33117                     cls : 'form-hidden-field roo-date-split-field-group-value',
33118                     name : this.name
33119                 }
33120             ]
33121         };
33122         
33123         var labelCls = 'col-md-12';
33124         var contentCls = 'col-md-4';
33125         
33126         if(this.fieldLabel){
33127             
33128             var label = {
33129                 tag : 'div',
33130                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33131                 cn : [
33132                     {
33133                         tag : 'label',
33134                         html : this.fieldLabel
33135                     }
33136                 ]
33137             };
33138             
33139             if(this.labelAlign == 'left'){
33140             
33141                 if(this.labelWidth > 12){
33142                     label.style = "width: " + this.labelWidth + 'px';
33143                 }
33144
33145                 if(this.labelWidth < 13 && this.labelmd == 0){
33146                     this.labelmd = this.labelWidth;
33147                 }
33148
33149                 if(this.labellg > 0){
33150                     labelCls = ' col-lg-' + this.labellg;
33151                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33152                 }
33153
33154                 if(this.labelmd > 0){
33155                     labelCls = ' col-md-' + this.labelmd;
33156                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33157                 }
33158
33159                 if(this.labelsm > 0){
33160                     labelCls = ' col-sm-' + this.labelsm;
33161                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33162                 }
33163
33164                 if(this.labelxs > 0){
33165                     labelCls = ' col-xs-' + this.labelxs;
33166                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33167                 }
33168             }
33169             
33170             label.cls += ' ' + labelCls;
33171             
33172             cfg.cn.push(label);
33173         }
33174         
33175         Roo.each(['day', 'month', 'year'], function(t){
33176             cfg.cn.push({
33177                 tag : 'div',
33178                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33179             });
33180         }, this);
33181         
33182         return cfg;
33183     },
33184     
33185     inputEl: function ()
33186     {
33187         return this.el.select('.roo-date-split-field-group-value', true).first();
33188     },
33189     
33190     onRender : function(ct, position) 
33191     {
33192         var _this = this;
33193         
33194         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33195         
33196         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33197         
33198         this.dayField = new Roo.bootstrap.ComboBox({
33199             allowBlank : this.dayAllowBlank,
33200             alwaysQuery : true,
33201             displayField : 'value',
33202             editable : false,
33203             fieldLabel : '',
33204             forceSelection : true,
33205             mode : 'local',
33206             placeholder : this.dayPlaceholder,
33207             selectOnFocus : true,
33208             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33209             triggerAction : 'all',
33210             typeAhead : true,
33211             valueField : 'value',
33212             store : new Roo.data.SimpleStore({
33213                 data : (function() {    
33214                     var days = [];
33215                     _this.fireEvent('days', _this, days);
33216                     return days;
33217                 })(),
33218                 fields : [ 'value' ]
33219             }),
33220             listeners : {
33221                 select : function (_self, record, index)
33222                 {
33223                     _this.setValue(_this.getValue());
33224                 }
33225             }
33226         });
33227
33228         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33229         
33230         this.monthField = new Roo.bootstrap.MonthField({
33231             after : '<i class=\"fa fa-calendar\"></i>',
33232             allowBlank : this.monthAllowBlank,
33233             placeholder : this.monthPlaceholder,
33234             readOnly : true,
33235             listeners : {
33236                 render : function (_self)
33237                 {
33238                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33239                         e.preventDefault();
33240                         _self.focus();
33241                     });
33242                 },
33243                 select : function (_self, oldvalue, newvalue)
33244                 {
33245                     _this.setValue(_this.getValue());
33246                 }
33247             }
33248         });
33249         
33250         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33251         
33252         this.yearField = new Roo.bootstrap.ComboBox({
33253             allowBlank : this.yearAllowBlank,
33254             alwaysQuery : true,
33255             displayField : 'value',
33256             editable : false,
33257             fieldLabel : '',
33258             forceSelection : true,
33259             mode : 'local',
33260             placeholder : this.yearPlaceholder,
33261             selectOnFocus : true,
33262             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33263             triggerAction : 'all',
33264             typeAhead : true,
33265             valueField : 'value',
33266             store : new Roo.data.SimpleStore({
33267                 data : (function() {
33268                     var years = [];
33269                     _this.fireEvent('years', _this, years);
33270                     return years;
33271                 })(),
33272                 fields : [ 'value' ]
33273             }),
33274             listeners : {
33275                 select : function (_self, record, index)
33276                 {
33277                     _this.setValue(_this.getValue());
33278                 }
33279             }
33280         });
33281
33282         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33283     },
33284     
33285     setValue : function(v, format)
33286     {
33287         this.inputEl.dom.value = v;
33288         
33289         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33290         
33291         var d = Date.parseDate(v, f);
33292         
33293         if(!d){
33294             this.validate();
33295             return;
33296         }
33297         
33298         this.setDay(d.format(this.dayFormat));
33299         this.setMonth(d.format(this.monthFormat));
33300         this.setYear(d.format(this.yearFormat));
33301         
33302         this.validate();
33303         
33304         return;
33305     },
33306     
33307     setDay : function(v)
33308     {
33309         this.dayField.setValue(v);
33310         this.inputEl.dom.value = this.getValue();
33311         this.validate();
33312         return;
33313     },
33314     
33315     setMonth : function(v)
33316     {
33317         this.monthField.setValue(v, true);
33318         this.inputEl.dom.value = this.getValue();
33319         this.validate();
33320         return;
33321     },
33322     
33323     setYear : function(v)
33324     {
33325         this.yearField.setValue(v);
33326         this.inputEl.dom.value = this.getValue();
33327         this.validate();
33328         return;
33329     },
33330     
33331     getDay : function()
33332     {
33333         return this.dayField.getValue();
33334     },
33335     
33336     getMonth : function()
33337     {
33338         return this.monthField.getValue();
33339     },
33340     
33341     getYear : function()
33342     {
33343         return this.yearField.getValue();
33344     },
33345     
33346     getValue : function()
33347     {
33348         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33349         
33350         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33351         
33352         return date;
33353     },
33354     
33355     reset : function()
33356     {
33357         this.setDay('');
33358         this.setMonth('');
33359         this.setYear('');
33360         this.inputEl.dom.value = '';
33361         this.validate();
33362         return;
33363     },
33364     
33365     validate : function()
33366     {
33367         var d = this.dayField.validate();
33368         var m = this.monthField.validate();
33369         var y = this.yearField.validate();
33370         
33371         var valid = true;
33372         
33373         if(
33374                 (!this.dayAllowBlank && !d) ||
33375                 (!this.monthAllowBlank && !m) ||
33376                 (!this.yearAllowBlank && !y)
33377         ){
33378             valid = false;
33379         }
33380         
33381         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33382             return valid;
33383         }
33384         
33385         if(valid){
33386             this.markValid();
33387             return valid;
33388         }
33389         
33390         this.markInvalid();
33391         
33392         return valid;
33393     },
33394     
33395     markValid : function()
33396     {
33397         
33398         var label = this.el.select('label', true).first();
33399         var icon = this.el.select('i.fa-star', true).first();
33400
33401         if(label && icon){
33402             icon.remove();
33403         }
33404         
33405         this.fireEvent('valid', this);
33406     },
33407     
33408      /**
33409      * Mark this field as invalid
33410      * @param {String} msg The validation message
33411      */
33412     markInvalid : function(msg)
33413     {
33414         
33415         var label = this.el.select('label', true).first();
33416         var icon = this.el.select('i.fa-star', true).first();
33417
33418         if(label && !icon){
33419             this.el.select('.roo-date-split-field-label', true).createChild({
33420                 tag : 'i',
33421                 cls : 'text-danger fa fa-lg fa-star',
33422                 tooltip : 'This field is required',
33423                 style : 'margin-right:5px;'
33424             }, label, true);
33425         }
33426         
33427         this.fireEvent('invalid', this, msg);
33428     },
33429     
33430     clearInvalid : function()
33431     {
33432         var label = this.el.select('label', true).first();
33433         var icon = this.el.select('i.fa-star', true).first();
33434
33435         if(label && icon){
33436             icon.remove();
33437         }
33438         
33439         this.fireEvent('valid', this);
33440     },
33441     
33442     getName: function()
33443     {
33444         return this.name;
33445     }
33446     
33447 });
33448
33449  /**
33450  *
33451  * This is based on 
33452  * http://masonry.desandro.com
33453  *
33454  * The idea is to render all the bricks based on vertical width...
33455  *
33456  * The original code extends 'outlayer' - we might need to use that....
33457  * 
33458  */
33459
33460
33461 /**
33462  * @class Roo.bootstrap.LayoutMasonry
33463  * @extends Roo.bootstrap.Component
33464  * Bootstrap Layout Masonry class
33465  * 
33466  * @constructor
33467  * Create a new Element
33468  * @param {Object} config The config object
33469  */
33470
33471 Roo.bootstrap.LayoutMasonry = function(config){
33472     
33473     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33474     
33475     this.bricks = [];
33476     
33477     Roo.bootstrap.LayoutMasonry.register(this);
33478     
33479     this.addEvents({
33480         // raw events
33481         /**
33482          * @event layout
33483          * Fire after layout the items
33484          * @param {Roo.bootstrap.LayoutMasonry} this
33485          * @param {Roo.EventObject} e
33486          */
33487         "layout" : true
33488     });
33489     
33490 };
33491
33492 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33493     
33494     /**
33495      * @cfg {Boolean} isLayoutInstant = no animation?
33496      */   
33497     isLayoutInstant : false, // needed?
33498    
33499     /**
33500      * @cfg {Number} boxWidth  width of the columns
33501      */   
33502     boxWidth : 450,
33503     
33504       /**
33505      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33506      */   
33507     boxHeight : 0,
33508     
33509     /**
33510      * @cfg {Number} padWidth padding below box..
33511      */   
33512     padWidth : 10, 
33513     
33514     /**
33515      * @cfg {Number} gutter gutter width..
33516      */   
33517     gutter : 10,
33518     
33519      /**
33520      * @cfg {Number} maxCols maximum number of columns
33521      */   
33522     
33523     maxCols: 0,
33524     
33525     /**
33526      * @cfg {Boolean} isAutoInitial defalut true
33527      */   
33528     isAutoInitial : true, 
33529     
33530     containerWidth: 0,
33531     
33532     /**
33533      * @cfg {Boolean} isHorizontal defalut false
33534      */   
33535     isHorizontal : false, 
33536
33537     currentSize : null,
33538     
33539     tag: 'div',
33540     
33541     cls: '',
33542     
33543     bricks: null, //CompositeElement
33544     
33545     cols : 1,
33546     
33547     _isLayoutInited : false,
33548     
33549 //    isAlternative : false, // only use for vertical layout...
33550     
33551     /**
33552      * @cfg {Number} alternativePadWidth padding below box..
33553      */   
33554     alternativePadWidth : 50,
33555     
33556     selectedBrick : [],
33557     
33558     getAutoCreate : function(){
33559         
33560         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33561         
33562         var cfg = {
33563             tag: this.tag,
33564             cls: 'blog-masonary-wrapper ' + this.cls,
33565             cn : {
33566                 cls : 'mas-boxes masonary'
33567             }
33568         };
33569         
33570         return cfg;
33571     },
33572     
33573     getChildContainer: function( )
33574     {
33575         if (this.boxesEl) {
33576             return this.boxesEl;
33577         }
33578         
33579         this.boxesEl = this.el.select('.mas-boxes').first();
33580         
33581         return this.boxesEl;
33582     },
33583     
33584     
33585     initEvents : function()
33586     {
33587         var _this = this;
33588         
33589         if(this.isAutoInitial){
33590             Roo.log('hook children rendered');
33591             this.on('childrenrendered', function() {
33592                 Roo.log('children rendered');
33593                 _this.initial();
33594             } ,this);
33595         }
33596     },
33597     
33598     initial : function()
33599     {
33600         this.selectedBrick = [];
33601         
33602         this.currentSize = this.el.getBox(true);
33603         
33604         Roo.EventManager.onWindowResize(this.resize, this); 
33605
33606         if(!this.isAutoInitial){
33607             this.layout();
33608             return;
33609         }
33610         
33611         this.layout();
33612         
33613         return;
33614         //this.layout.defer(500,this);
33615         
33616     },
33617     
33618     resize : function()
33619     {
33620         var cs = this.el.getBox(true);
33621         
33622         if (
33623                 this.currentSize.width == cs.width && 
33624                 this.currentSize.x == cs.x && 
33625                 this.currentSize.height == cs.height && 
33626                 this.currentSize.y == cs.y 
33627         ) {
33628             Roo.log("no change in with or X or Y");
33629             return;
33630         }
33631         
33632         this.currentSize = cs;
33633         
33634         this.layout();
33635         
33636     },
33637     
33638     layout : function()
33639     {   
33640         this._resetLayout();
33641         
33642         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33643         
33644         this.layoutItems( isInstant );
33645       
33646         this._isLayoutInited = true;
33647         
33648         this.fireEvent('layout', this);
33649         
33650     },
33651     
33652     _resetLayout : function()
33653     {
33654         if(this.isHorizontal){
33655             this.horizontalMeasureColumns();
33656             return;
33657         }
33658         
33659         this.verticalMeasureColumns();
33660         
33661     },
33662     
33663     verticalMeasureColumns : function()
33664     {
33665         this.getContainerWidth();
33666         
33667 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33668 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33669 //            return;
33670 //        }
33671         
33672         var boxWidth = this.boxWidth + this.padWidth;
33673         
33674         if(this.containerWidth < this.boxWidth){
33675             boxWidth = this.containerWidth
33676         }
33677         
33678         var containerWidth = this.containerWidth;
33679         
33680         var cols = Math.floor(containerWidth / boxWidth);
33681         
33682         this.cols = Math.max( cols, 1 );
33683         
33684         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33685         
33686         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33687         
33688         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33689         
33690         this.colWidth = boxWidth + avail - this.padWidth;
33691         
33692         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33693         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33694     },
33695     
33696     horizontalMeasureColumns : function()
33697     {
33698         this.getContainerWidth();
33699         
33700         var boxWidth = this.boxWidth;
33701         
33702         if(this.containerWidth < boxWidth){
33703             boxWidth = this.containerWidth;
33704         }
33705         
33706         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33707         
33708         this.el.setHeight(boxWidth);
33709         
33710     },
33711     
33712     getContainerWidth : function()
33713     {
33714         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33715     },
33716     
33717     layoutItems : function( isInstant )
33718     {
33719         Roo.log(this.bricks);
33720         
33721         var items = Roo.apply([], this.bricks);
33722         
33723         if(this.isHorizontal){
33724             this._horizontalLayoutItems( items , isInstant );
33725             return;
33726         }
33727         
33728 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33729 //            this._verticalAlternativeLayoutItems( items , isInstant );
33730 //            return;
33731 //        }
33732         
33733         this._verticalLayoutItems( items , isInstant );
33734         
33735     },
33736     
33737     _verticalLayoutItems : function ( items , isInstant)
33738     {
33739         if ( !items || !items.length ) {
33740             return;
33741         }
33742         
33743         var standard = [
33744             ['xs', 'xs', 'xs', 'tall'],
33745             ['xs', 'xs', 'tall'],
33746             ['xs', 'xs', 'sm'],
33747             ['xs', 'xs', 'xs'],
33748             ['xs', 'tall'],
33749             ['xs', 'sm'],
33750             ['xs', 'xs'],
33751             ['xs'],
33752             
33753             ['sm', 'xs', 'xs'],
33754             ['sm', 'xs'],
33755             ['sm'],
33756             
33757             ['tall', 'xs', 'xs', 'xs'],
33758             ['tall', 'xs', 'xs'],
33759             ['tall', 'xs'],
33760             ['tall']
33761             
33762         ];
33763         
33764         var queue = [];
33765         
33766         var boxes = [];
33767         
33768         var box = [];
33769         
33770         Roo.each(items, function(item, k){
33771             
33772             switch (item.size) {
33773                 // these layouts take up a full box,
33774                 case 'md' :
33775                 case 'md-left' :
33776                 case 'md-right' :
33777                 case 'wide' :
33778                     
33779                     if(box.length){
33780                         boxes.push(box);
33781                         box = [];
33782                     }
33783                     
33784                     boxes.push([item]);
33785                     
33786                     break;
33787                     
33788                 case 'xs' :
33789                 case 'sm' :
33790                 case 'tall' :
33791                     
33792                     box.push(item);
33793                     
33794                     break;
33795                 default :
33796                     break;
33797                     
33798             }
33799             
33800         }, this);
33801         
33802         if(box.length){
33803             boxes.push(box);
33804             box = [];
33805         }
33806         
33807         var filterPattern = function(box, length)
33808         {
33809             if(!box.length){
33810                 return;
33811             }
33812             
33813             var match = false;
33814             
33815             var pattern = box.slice(0, length);
33816             
33817             var format = [];
33818             
33819             Roo.each(pattern, function(i){
33820                 format.push(i.size);
33821             }, this);
33822             
33823             Roo.each(standard, function(s){
33824                 
33825                 if(String(s) != String(format)){
33826                     return;
33827                 }
33828                 
33829                 match = true;
33830                 return false;
33831                 
33832             }, this);
33833             
33834             if(!match && length == 1){
33835                 return;
33836             }
33837             
33838             if(!match){
33839                 filterPattern(box, length - 1);
33840                 return;
33841             }
33842                 
33843             queue.push(pattern);
33844
33845             box = box.slice(length, box.length);
33846
33847             filterPattern(box, 4);
33848
33849             return;
33850             
33851         }
33852         
33853         Roo.each(boxes, function(box, k){
33854             
33855             if(!box.length){
33856                 return;
33857             }
33858             
33859             if(box.length == 1){
33860                 queue.push(box);
33861                 return;
33862             }
33863             
33864             filterPattern(box, 4);
33865             
33866         }, this);
33867         
33868         this._processVerticalLayoutQueue( queue, isInstant );
33869         
33870     },
33871     
33872 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33873 //    {
33874 //        if ( !items || !items.length ) {
33875 //            return;
33876 //        }
33877 //
33878 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33879 //        
33880 //    },
33881     
33882     _horizontalLayoutItems : function ( items , isInstant)
33883     {
33884         if ( !items || !items.length || items.length < 3) {
33885             return;
33886         }
33887         
33888         items.reverse();
33889         
33890         var eItems = items.slice(0, 3);
33891         
33892         items = items.slice(3, items.length);
33893         
33894         var standard = [
33895             ['xs', 'xs', 'xs', 'wide'],
33896             ['xs', 'xs', 'wide'],
33897             ['xs', 'xs', 'sm'],
33898             ['xs', 'xs', 'xs'],
33899             ['xs', 'wide'],
33900             ['xs', 'sm'],
33901             ['xs', 'xs'],
33902             ['xs'],
33903             
33904             ['sm', 'xs', 'xs'],
33905             ['sm', 'xs'],
33906             ['sm'],
33907             
33908             ['wide', 'xs', 'xs', 'xs'],
33909             ['wide', 'xs', 'xs'],
33910             ['wide', 'xs'],
33911             ['wide'],
33912             
33913             ['wide-thin']
33914         ];
33915         
33916         var queue = [];
33917         
33918         var boxes = [];
33919         
33920         var box = [];
33921         
33922         Roo.each(items, function(item, k){
33923             
33924             switch (item.size) {
33925                 case 'md' :
33926                 case 'md-left' :
33927                 case 'md-right' :
33928                 case 'tall' :
33929                     
33930                     if(box.length){
33931                         boxes.push(box);
33932                         box = [];
33933                     }
33934                     
33935                     boxes.push([item]);
33936                     
33937                     break;
33938                     
33939                 case 'xs' :
33940                 case 'sm' :
33941                 case 'wide' :
33942                 case 'wide-thin' :
33943                     
33944                     box.push(item);
33945                     
33946                     break;
33947                 default :
33948                     break;
33949                     
33950             }
33951             
33952         }, this);
33953         
33954         if(box.length){
33955             boxes.push(box);
33956             box = [];
33957         }
33958         
33959         var filterPattern = function(box, length)
33960         {
33961             if(!box.length){
33962                 return;
33963             }
33964             
33965             var match = false;
33966             
33967             var pattern = box.slice(0, length);
33968             
33969             var format = [];
33970             
33971             Roo.each(pattern, function(i){
33972                 format.push(i.size);
33973             }, this);
33974             
33975             Roo.each(standard, function(s){
33976                 
33977                 if(String(s) != String(format)){
33978                     return;
33979                 }
33980                 
33981                 match = true;
33982                 return false;
33983                 
33984             }, this);
33985             
33986             if(!match && length == 1){
33987                 return;
33988             }
33989             
33990             if(!match){
33991                 filterPattern(box, length - 1);
33992                 return;
33993             }
33994                 
33995             queue.push(pattern);
33996
33997             box = box.slice(length, box.length);
33998
33999             filterPattern(box, 4);
34000
34001             return;
34002             
34003         }
34004         
34005         Roo.each(boxes, function(box, k){
34006             
34007             if(!box.length){
34008                 return;
34009             }
34010             
34011             if(box.length == 1){
34012                 queue.push(box);
34013                 return;
34014             }
34015             
34016             filterPattern(box, 4);
34017             
34018         }, this);
34019         
34020         
34021         var prune = [];
34022         
34023         var pos = this.el.getBox(true);
34024         
34025         var minX = pos.x;
34026         
34027         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34028         
34029         var hit_end = false;
34030         
34031         Roo.each(queue, function(box){
34032             
34033             if(hit_end){
34034                 
34035                 Roo.each(box, function(b){
34036                 
34037                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34038                     b.el.hide();
34039
34040                 }, this);
34041
34042                 return;
34043             }
34044             
34045             var mx = 0;
34046             
34047             Roo.each(box, function(b){
34048                 
34049                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34050                 b.el.show();
34051
34052                 mx = Math.max(mx, b.x);
34053                 
34054             }, this);
34055             
34056             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34057             
34058             if(maxX < minX){
34059                 
34060                 Roo.each(box, function(b){
34061                 
34062                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34063                     b.el.hide();
34064                     
34065                 }, this);
34066                 
34067                 hit_end = true;
34068                 
34069                 return;
34070             }
34071             
34072             prune.push(box);
34073             
34074         }, this);
34075         
34076         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34077     },
34078     
34079     /** Sets position of item in DOM
34080     * @param {Element} item
34081     * @param {Number} x - horizontal position
34082     * @param {Number} y - vertical position
34083     * @param {Boolean} isInstant - disables transitions
34084     */
34085     _processVerticalLayoutQueue : function( queue, isInstant )
34086     {
34087         var pos = this.el.getBox(true);
34088         var x = pos.x;
34089         var y = pos.y;
34090         var maxY = [];
34091         
34092         for (var i = 0; i < this.cols; i++){
34093             maxY[i] = pos.y;
34094         }
34095         
34096         Roo.each(queue, function(box, k){
34097             
34098             var col = k % this.cols;
34099             
34100             Roo.each(box, function(b,kk){
34101                 
34102                 b.el.position('absolute');
34103                 
34104                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34105                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34106                 
34107                 if(b.size == 'md-left' || b.size == 'md-right'){
34108                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34109                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34110                 }
34111                 
34112                 b.el.setWidth(width);
34113                 b.el.setHeight(height);
34114                 // iframe?
34115                 b.el.select('iframe',true).setSize(width,height);
34116                 
34117             }, this);
34118             
34119             for (var i = 0; i < this.cols; i++){
34120                 
34121                 if(maxY[i] < maxY[col]){
34122                     col = i;
34123                     continue;
34124                 }
34125                 
34126                 col = Math.min(col, i);
34127                 
34128             }
34129             
34130             x = pos.x + col * (this.colWidth + this.padWidth);
34131             
34132             y = maxY[col];
34133             
34134             var positions = [];
34135             
34136             switch (box.length){
34137                 case 1 :
34138                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34139                     break;
34140                 case 2 :
34141                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34142                     break;
34143                 case 3 :
34144                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34145                     break;
34146                 case 4 :
34147                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34148                     break;
34149                 default :
34150                     break;
34151             }
34152             
34153             Roo.each(box, function(b,kk){
34154                 
34155                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34156                 
34157                 var sz = b.el.getSize();
34158                 
34159                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34160                 
34161             }, this);
34162             
34163         }, this);
34164         
34165         var mY = 0;
34166         
34167         for (var i = 0; i < this.cols; i++){
34168             mY = Math.max(mY, maxY[i]);
34169         }
34170         
34171         this.el.setHeight(mY - pos.y);
34172         
34173     },
34174     
34175 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34176 //    {
34177 //        var pos = this.el.getBox(true);
34178 //        var x = pos.x;
34179 //        var y = pos.y;
34180 //        var maxX = pos.right;
34181 //        
34182 //        var maxHeight = 0;
34183 //        
34184 //        Roo.each(items, function(item, k){
34185 //            
34186 //            var c = k % 2;
34187 //            
34188 //            item.el.position('absolute');
34189 //                
34190 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34191 //
34192 //            item.el.setWidth(width);
34193 //
34194 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34195 //
34196 //            item.el.setHeight(height);
34197 //            
34198 //            if(c == 0){
34199 //                item.el.setXY([x, y], isInstant ? false : true);
34200 //            } else {
34201 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34202 //            }
34203 //            
34204 //            y = y + height + this.alternativePadWidth;
34205 //            
34206 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34207 //            
34208 //        }, this);
34209 //        
34210 //        this.el.setHeight(maxHeight);
34211 //        
34212 //    },
34213     
34214     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34215     {
34216         var pos = this.el.getBox(true);
34217         
34218         var minX = pos.x;
34219         var minY = pos.y;
34220         
34221         var maxX = pos.right;
34222         
34223         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34224         
34225         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34226         
34227         Roo.each(queue, function(box, k){
34228             
34229             Roo.each(box, function(b, kk){
34230                 
34231                 b.el.position('absolute');
34232                 
34233                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34234                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34235                 
34236                 if(b.size == 'md-left' || b.size == 'md-right'){
34237                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34238                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34239                 }
34240                 
34241                 b.el.setWidth(width);
34242                 b.el.setHeight(height);
34243                 
34244             }, this);
34245             
34246             if(!box.length){
34247                 return;
34248             }
34249             
34250             var positions = [];
34251             
34252             switch (box.length){
34253                 case 1 :
34254                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34255                     break;
34256                 case 2 :
34257                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34258                     break;
34259                 case 3 :
34260                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34261                     break;
34262                 case 4 :
34263                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34264                     break;
34265                 default :
34266                     break;
34267             }
34268             
34269             Roo.each(box, function(b,kk){
34270                 
34271                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34272                 
34273                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34274                 
34275             }, this);
34276             
34277         }, this);
34278         
34279     },
34280     
34281     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34282     {
34283         Roo.each(eItems, function(b,k){
34284             
34285             b.size = (k == 0) ? 'sm' : 'xs';
34286             b.x = (k == 0) ? 2 : 1;
34287             b.y = (k == 0) ? 2 : 1;
34288             
34289             b.el.position('absolute');
34290             
34291             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34292                 
34293             b.el.setWidth(width);
34294             
34295             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34296             
34297             b.el.setHeight(height);
34298             
34299         }, this);
34300
34301         var positions = [];
34302         
34303         positions.push({
34304             x : maxX - this.unitWidth * 2 - this.gutter,
34305             y : minY
34306         });
34307         
34308         positions.push({
34309             x : maxX - this.unitWidth,
34310             y : minY + (this.unitWidth + this.gutter) * 2
34311         });
34312         
34313         positions.push({
34314             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34315             y : minY
34316         });
34317         
34318         Roo.each(eItems, function(b,k){
34319             
34320             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34321
34322         }, this);
34323         
34324     },
34325     
34326     getVerticalOneBoxColPositions : function(x, y, box)
34327     {
34328         var pos = [];
34329         
34330         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34331         
34332         if(box[0].size == 'md-left'){
34333             rand = 0;
34334         }
34335         
34336         if(box[0].size == 'md-right'){
34337             rand = 1;
34338         }
34339         
34340         pos.push({
34341             x : x + (this.unitWidth + this.gutter) * rand,
34342             y : y
34343         });
34344         
34345         return pos;
34346     },
34347     
34348     getVerticalTwoBoxColPositions : function(x, y, box)
34349     {
34350         var pos = [];
34351         
34352         if(box[0].size == 'xs'){
34353             
34354             pos.push({
34355                 x : x,
34356                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34357             });
34358
34359             pos.push({
34360                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34361                 y : y
34362             });
34363             
34364             return pos;
34365             
34366         }
34367         
34368         pos.push({
34369             x : x,
34370             y : y
34371         });
34372
34373         pos.push({
34374             x : x + (this.unitWidth + this.gutter) * 2,
34375             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34376         });
34377         
34378         return pos;
34379         
34380     },
34381     
34382     getVerticalThreeBoxColPositions : function(x, y, box)
34383     {
34384         var pos = [];
34385         
34386         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34387             
34388             pos.push({
34389                 x : x,
34390                 y : y
34391             });
34392
34393             pos.push({
34394                 x : x + (this.unitWidth + this.gutter) * 1,
34395                 y : y
34396             });
34397             
34398             pos.push({
34399                 x : x + (this.unitWidth + this.gutter) * 2,
34400                 y : y
34401             });
34402             
34403             return pos;
34404             
34405         }
34406         
34407         if(box[0].size == 'xs' && box[1].size == 'xs'){
34408             
34409             pos.push({
34410                 x : x,
34411                 y : y
34412             });
34413
34414             pos.push({
34415                 x : x,
34416                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34417             });
34418             
34419             pos.push({
34420                 x : x + (this.unitWidth + this.gutter) * 1,
34421                 y : y
34422             });
34423             
34424             return pos;
34425             
34426         }
34427         
34428         pos.push({
34429             x : x,
34430             y : y
34431         });
34432
34433         pos.push({
34434             x : x + (this.unitWidth + this.gutter) * 2,
34435             y : y
34436         });
34437
34438         pos.push({
34439             x : x + (this.unitWidth + this.gutter) * 2,
34440             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34441         });
34442             
34443         return pos;
34444         
34445     },
34446     
34447     getVerticalFourBoxColPositions : function(x, y, box)
34448     {
34449         var pos = [];
34450         
34451         if(box[0].size == 'xs'){
34452             
34453             pos.push({
34454                 x : x,
34455                 y : y
34456             });
34457
34458             pos.push({
34459                 x : x,
34460                 y : y + (this.unitHeight + this.gutter) * 1
34461             });
34462             
34463             pos.push({
34464                 x : x,
34465                 y : y + (this.unitHeight + this.gutter) * 2
34466             });
34467             
34468             pos.push({
34469                 x : x + (this.unitWidth + this.gutter) * 1,
34470                 y : y
34471             });
34472             
34473             return pos;
34474             
34475         }
34476         
34477         pos.push({
34478             x : x,
34479             y : y
34480         });
34481
34482         pos.push({
34483             x : x + (this.unitWidth + this.gutter) * 2,
34484             y : y
34485         });
34486
34487         pos.push({
34488             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34489             y : y + (this.unitHeight + this.gutter) * 1
34490         });
34491
34492         pos.push({
34493             x : x + (this.unitWidth + this.gutter) * 2,
34494             y : y + (this.unitWidth + this.gutter) * 2
34495         });
34496
34497         return pos;
34498         
34499     },
34500     
34501     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34502     {
34503         var pos = [];
34504         
34505         if(box[0].size == 'md-left'){
34506             pos.push({
34507                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34508                 y : minY
34509             });
34510             
34511             return pos;
34512         }
34513         
34514         if(box[0].size == 'md-right'){
34515             pos.push({
34516                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34517                 y : minY + (this.unitWidth + this.gutter) * 1
34518             });
34519             
34520             return pos;
34521         }
34522         
34523         var rand = Math.floor(Math.random() * (4 - box[0].y));
34524         
34525         pos.push({
34526             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34527             y : minY + (this.unitWidth + this.gutter) * rand
34528         });
34529         
34530         return pos;
34531         
34532     },
34533     
34534     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34535     {
34536         var pos = [];
34537         
34538         if(box[0].size == 'xs'){
34539             
34540             pos.push({
34541                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34542                 y : minY
34543             });
34544
34545             pos.push({
34546                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34547                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34548             });
34549             
34550             return pos;
34551             
34552         }
34553         
34554         pos.push({
34555             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34556             y : minY
34557         });
34558
34559         pos.push({
34560             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34561             y : minY + (this.unitWidth + this.gutter) * 2
34562         });
34563         
34564         return pos;
34565         
34566     },
34567     
34568     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34569     {
34570         var pos = [];
34571         
34572         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34573             
34574             pos.push({
34575                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34576                 y : minY
34577             });
34578
34579             pos.push({
34580                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34581                 y : minY + (this.unitWidth + this.gutter) * 1
34582             });
34583             
34584             pos.push({
34585                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34586                 y : minY + (this.unitWidth + this.gutter) * 2
34587             });
34588             
34589             return pos;
34590             
34591         }
34592         
34593         if(box[0].size == 'xs' && box[1].size == 'xs'){
34594             
34595             pos.push({
34596                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34597                 y : minY
34598             });
34599
34600             pos.push({
34601                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34602                 y : minY
34603             });
34604             
34605             pos.push({
34606                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34607                 y : minY + (this.unitWidth + this.gutter) * 1
34608             });
34609             
34610             return pos;
34611             
34612         }
34613         
34614         pos.push({
34615             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34616             y : minY
34617         });
34618
34619         pos.push({
34620             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34621             y : minY + (this.unitWidth + this.gutter) * 2
34622         });
34623
34624         pos.push({
34625             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34626             y : minY + (this.unitWidth + this.gutter) * 2
34627         });
34628             
34629         return pos;
34630         
34631     },
34632     
34633     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34634     {
34635         var pos = [];
34636         
34637         if(box[0].size == 'xs'){
34638             
34639             pos.push({
34640                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34641                 y : minY
34642             });
34643
34644             pos.push({
34645                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34646                 y : minY
34647             });
34648             
34649             pos.push({
34650                 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),
34651                 y : minY
34652             });
34653             
34654             pos.push({
34655                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34656                 y : minY + (this.unitWidth + this.gutter) * 1
34657             });
34658             
34659             return pos;
34660             
34661         }
34662         
34663         pos.push({
34664             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34665             y : minY
34666         });
34667         
34668         pos.push({
34669             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34670             y : minY + (this.unitWidth + this.gutter) * 2
34671         });
34672         
34673         pos.push({
34674             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34675             y : minY + (this.unitWidth + this.gutter) * 2
34676         });
34677         
34678         pos.push({
34679             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),
34680             y : minY + (this.unitWidth + this.gutter) * 2
34681         });
34682
34683         return pos;
34684         
34685     },
34686     
34687     /**
34688     * remove a Masonry Brick
34689     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34690     */
34691     removeBrick : function(brick_id)
34692     {
34693         if (!brick_id) {
34694             return;
34695         }
34696         
34697         for (var i = 0; i<this.bricks.length; i++) {
34698             if (this.bricks[i].id == brick_id) {
34699                 this.bricks.splice(i,1);
34700                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34701                 this.initial();
34702             }
34703         }
34704     },
34705     
34706     /**
34707     * adds a Masonry Brick
34708     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34709     */
34710     addBrick : function(cfg)
34711     {
34712         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34713         //this.register(cn);
34714         cn.parentId = this.id;
34715         cn.render(this.el);
34716         return cn;
34717     },
34718     
34719     /**
34720     * register a Masonry Brick
34721     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34722     */
34723     
34724     register : function(brick)
34725     {
34726         this.bricks.push(brick);
34727         brick.masonryId = this.id;
34728     },
34729     
34730     /**
34731     * clear all the Masonry Brick
34732     */
34733     clearAll : function()
34734     {
34735         this.bricks = [];
34736         //this.getChildContainer().dom.innerHTML = "";
34737         this.el.dom.innerHTML = '';
34738     },
34739     
34740     getSelected : function()
34741     {
34742         if (!this.selectedBrick) {
34743             return false;
34744         }
34745         
34746         return this.selectedBrick;
34747     }
34748 });
34749
34750 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34751     
34752     groups: {},
34753      /**
34754     * register a Masonry Layout
34755     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34756     */
34757     
34758     register : function(layout)
34759     {
34760         this.groups[layout.id] = layout;
34761     },
34762     /**
34763     * fetch a  Masonry Layout based on the masonry layout ID
34764     * @param {string} the masonry layout to add
34765     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34766     */
34767     
34768     get: function(layout_id) {
34769         if (typeof(this.groups[layout_id]) == 'undefined') {
34770             return false;
34771         }
34772         return this.groups[layout_id] ;
34773     }
34774     
34775     
34776     
34777 });
34778
34779  
34780
34781  /**
34782  *
34783  * This is based on 
34784  * http://masonry.desandro.com
34785  *
34786  * The idea is to render all the bricks based on vertical width...
34787  *
34788  * The original code extends 'outlayer' - we might need to use that....
34789  * 
34790  */
34791
34792
34793 /**
34794  * @class Roo.bootstrap.LayoutMasonryAuto
34795  * @extends Roo.bootstrap.Component
34796  * Bootstrap Layout Masonry class
34797  * 
34798  * @constructor
34799  * Create a new Element
34800  * @param {Object} config The config object
34801  */
34802
34803 Roo.bootstrap.LayoutMasonryAuto = function(config){
34804     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34805 };
34806
34807 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34808     
34809       /**
34810      * @cfg {Boolean} isFitWidth  - resize the width..
34811      */   
34812     isFitWidth : false,  // options..
34813     /**
34814      * @cfg {Boolean} isOriginLeft = left align?
34815      */   
34816     isOriginLeft : true,
34817     /**
34818      * @cfg {Boolean} isOriginTop = top align?
34819      */   
34820     isOriginTop : false,
34821     /**
34822      * @cfg {Boolean} isLayoutInstant = no animation?
34823      */   
34824     isLayoutInstant : false, // needed?
34825     /**
34826      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34827      */   
34828     isResizingContainer : true,
34829     /**
34830      * @cfg {Number} columnWidth  width of the columns 
34831      */   
34832     
34833     columnWidth : 0,
34834     
34835     /**
34836      * @cfg {Number} maxCols maximum number of columns
34837      */   
34838     
34839     maxCols: 0,
34840     /**
34841      * @cfg {Number} padHeight padding below box..
34842      */   
34843     
34844     padHeight : 10, 
34845     
34846     /**
34847      * @cfg {Boolean} isAutoInitial defalut true
34848      */   
34849     
34850     isAutoInitial : true, 
34851     
34852     // private?
34853     gutter : 0,
34854     
34855     containerWidth: 0,
34856     initialColumnWidth : 0,
34857     currentSize : null,
34858     
34859     colYs : null, // array.
34860     maxY : 0,
34861     padWidth: 10,
34862     
34863     
34864     tag: 'div',
34865     cls: '',
34866     bricks: null, //CompositeElement
34867     cols : 0, // array?
34868     // element : null, // wrapped now this.el
34869     _isLayoutInited : null, 
34870     
34871     
34872     getAutoCreate : function(){
34873         
34874         var cfg = {
34875             tag: this.tag,
34876             cls: 'blog-masonary-wrapper ' + this.cls,
34877             cn : {
34878                 cls : 'mas-boxes masonary'
34879             }
34880         };
34881         
34882         return cfg;
34883     },
34884     
34885     getChildContainer: function( )
34886     {
34887         if (this.boxesEl) {
34888             return this.boxesEl;
34889         }
34890         
34891         this.boxesEl = this.el.select('.mas-boxes').first();
34892         
34893         return this.boxesEl;
34894     },
34895     
34896     
34897     initEvents : function()
34898     {
34899         var _this = this;
34900         
34901         if(this.isAutoInitial){
34902             Roo.log('hook children rendered');
34903             this.on('childrenrendered', function() {
34904                 Roo.log('children rendered');
34905                 _this.initial();
34906             } ,this);
34907         }
34908         
34909     },
34910     
34911     initial : function()
34912     {
34913         this.reloadItems();
34914
34915         this.currentSize = this.el.getBox(true);
34916
34917         /// was window resize... - let's see if this works..
34918         Roo.EventManager.onWindowResize(this.resize, this); 
34919
34920         if(!this.isAutoInitial){
34921             this.layout();
34922             return;
34923         }
34924         
34925         this.layout.defer(500,this);
34926     },
34927     
34928     reloadItems: function()
34929     {
34930         this.bricks = this.el.select('.masonry-brick', true);
34931         
34932         this.bricks.each(function(b) {
34933             //Roo.log(b.getSize());
34934             if (!b.attr('originalwidth')) {
34935                 b.attr('originalwidth',  b.getSize().width);
34936             }
34937             
34938         });
34939         
34940         Roo.log(this.bricks.elements.length);
34941     },
34942     
34943     resize : function()
34944     {
34945         Roo.log('resize');
34946         var cs = this.el.getBox(true);
34947         
34948         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34949             Roo.log("no change in with or X");
34950             return;
34951         }
34952         this.currentSize = cs;
34953         this.layout();
34954     },
34955     
34956     layout : function()
34957     {
34958          Roo.log('layout');
34959         this._resetLayout();
34960         //this._manageStamps();
34961       
34962         // don't animate first layout
34963         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34964         this.layoutItems( isInstant );
34965       
34966         // flag for initalized
34967         this._isLayoutInited = true;
34968     },
34969     
34970     layoutItems : function( isInstant )
34971     {
34972         //var items = this._getItemsForLayout( this.items );
34973         // original code supports filtering layout items.. we just ignore it..
34974         
34975         this._layoutItems( this.bricks , isInstant );
34976       
34977         this._postLayout();
34978     },
34979     _layoutItems : function ( items , isInstant)
34980     {
34981        //this.fireEvent( 'layout', this, items );
34982     
34983
34984         if ( !items || !items.elements.length ) {
34985           // no items, emit event with empty array
34986             return;
34987         }
34988
34989         var queue = [];
34990         items.each(function(item) {
34991             Roo.log("layout item");
34992             Roo.log(item);
34993             // get x/y object from method
34994             var position = this._getItemLayoutPosition( item );
34995             // enqueue
34996             position.item = item;
34997             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34998             queue.push( position );
34999         }, this);
35000       
35001         this._processLayoutQueue( queue );
35002     },
35003     /** Sets position of item in DOM
35004     * @param {Element} item
35005     * @param {Number} x - horizontal position
35006     * @param {Number} y - vertical position
35007     * @param {Boolean} isInstant - disables transitions
35008     */
35009     _processLayoutQueue : function( queue )
35010     {
35011         for ( var i=0, len = queue.length; i < len; i++ ) {
35012             var obj = queue[i];
35013             obj.item.position('absolute');
35014             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35015         }
35016     },
35017       
35018     
35019     /**
35020     * Any logic you want to do after each layout,
35021     * i.e. size the container
35022     */
35023     _postLayout : function()
35024     {
35025         this.resizeContainer();
35026     },
35027     
35028     resizeContainer : function()
35029     {
35030         if ( !this.isResizingContainer ) {
35031             return;
35032         }
35033         var size = this._getContainerSize();
35034         if ( size ) {
35035             this.el.setSize(size.width,size.height);
35036             this.boxesEl.setSize(size.width,size.height);
35037         }
35038     },
35039     
35040     
35041     
35042     _resetLayout : function()
35043     {
35044         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35045         this.colWidth = this.el.getWidth();
35046         //this.gutter = this.el.getWidth(); 
35047         
35048         this.measureColumns();
35049
35050         // reset column Y
35051         var i = this.cols;
35052         this.colYs = [];
35053         while (i--) {
35054             this.colYs.push( 0 );
35055         }
35056     
35057         this.maxY = 0;
35058     },
35059
35060     measureColumns : function()
35061     {
35062         this.getContainerWidth();
35063       // if columnWidth is 0, default to outerWidth of first item
35064         if ( !this.columnWidth ) {
35065             var firstItem = this.bricks.first();
35066             Roo.log(firstItem);
35067             this.columnWidth  = this.containerWidth;
35068             if (firstItem && firstItem.attr('originalwidth') ) {
35069                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35070             }
35071             // columnWidth fall back to item of first element
35072             Roo.log("set column width?");
35073                         this.initialColumnWidth = this.columnWidth  ;
35074
35075             // if first elem has no width, default to size of container
35076             
35077         }
35078         
35079         
35080         if (this.initialColumnWidth) {
35081             this.columnWidth = this.initialColumnWidth;
35082         }
35083         
35084         
35085             
35086         // column width is fixed at the top - however if container width get's smaller we should
35087         // reduce it...
35088         
35089         // this bit calcs how man columns..
35090             
35091         var columnWidth = this.columnWidth += this.gutter;
35092       
35093         // calculate columns
35094         var containerWidth = this.containerWidth + this.gutter;
35095         
35096         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35097         // fix rounding errors, typically with gutters
35098         var excess = columnWidth - containerWidth % columnWidth;
35099         
35100         
35101         // if overshoot is less than a pixel, round up, otherwise floor it
35102         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35103         cols = Math[ mathMethod ]( cols );
35104         this.cols = Math.max( cols, 1 );
35105         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35106         
35107          // padding positioning..
35108         var totalColWidth = this.cols * this.columnWidth;
35109         var padavail = this.containerWidth - totalColWidth;
35110         // so for 2 columns - we need 3 'pads'
35111         
35112         var padNeeded = (1+this.cols) * this.padWidth;
35113         
35114         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35115         
35116         this.columnWidth += padExtra
35117         //this.padWidth = Math.floor(padavail /  ( this.cols));
35118         
35119         // adjust colum width so that padding is fixed??
35120         
35121         // we have 3 columns ... total = width * 3
35122         // we have X left over... that should be used by 
35123         
35124         //if (this.expandC) {
35125             
35126         //}
35127         
35128         
35129         
35130     },
35131     
35132     getContainerWidth : function()
35133     {
35134        /* // container is parent if fit width
35135         var container = this.isFitWidth ? this.element.parentNode : this.element;
35136         // check that this.size and size are there
35137         // IE8 triggers resize on body size change, so they might not be
35138         
35139         var size = getSize( container );  //FIXME
35140         this.containerWidth = size && size.innerWidth; //FIXME
35141         */
35142          
35143         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35144         
35145     },
35146     
35147     _getItemLayoutPosition : function( item )  // what is item?
35148     {
35149         // we resize the item to our columnWidth..
35150       
35151         item.setWidth(this.columnWidth);
35152         item.autoBoxAdjust  = false;
35153         
35154         var sz = item.getSize();
35155  
35156         // how many columns does this brick span
35157         var remainder = this.containerWidth % this.columnWidth;
35158         
35159         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35160         // round if off by 1 pixel, otherwise use ceil
35161         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35162         colSpan = Math.min( colSpan, this.cols );
35163         
35164         // normally this should be '1' as we dont' currently allow multi width columns..
35165         
35166         var colGroup = this._getColGroup( colSpan );
35167         // get the minimum Y value from the columns
35168         var minimumY = Math.min.apply( Math, colGroup );
35169         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35170         
35171         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35172          
35173         // position the brick
35174         var position = {
35175             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35176             y: this.currentSize.y + minimumY + this.padHeight
35177         };
35178         
35179         Roo.log(position);
35180         // apply setHeight to necessary columns
35181         var setHeight = minimumY + sz.height + this.padHeight;
35182         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35183         
35184         var setSpan = this.cols + 1 - colGroup.length;
35185         for ( var i = 0; i < setSpan; i++ ) {
35186           this.colYs[ shortColIndex + i ] = setHeight ;
35187         }
35188       
35189         return position;
35190     },
35191     
35192     /**
35193      * @param {Number} colSpan - number of columns the element spans
35194      * @returns {Array} colGroup
35195      */
35196     _getColGroup : function( colSpan )
35197     {
35198         if ( colSpan < 2 ) {
35199           // if brick spans only one column, use all the column Ys
35200           return this.colYs;
35201         }
35202       
35203         var colGroup = [];
35204         // how many different places could this brick fit horizontally
35205         var groupCount = this.cols + 1 - colSpan;
35206         // for each group potential horizontal position
35207         for ( var i = 0; i < groupCount; i++ ) {
35208           // make an array of colY values for that one group
35209           var groupColYs = this.colYs.slice( i, i + colSpan );
35210           // and get the max value of the array
35211           colGroup[i] = Math.max.apply( Math, groupColYs );
35212         }
35213         return colGroup;
35214     },
35215     /*
35216     _manageStamp : function( stamp )
35217     {
35218         var stampSize =  stamp.getSize();
35219         var offset = stamp.getBox();
35220         // get the columns that this stamp affects
35221         var firstX = this.isOriginLeft ? offset.x : offset.right;
35222         var lastX = firstX + stampSize.width;
35223         var firstCol = Math.floor( firstX / this.columnWidth );
35224         firstCol = Math.max( 0, firstCol );
35225         
35226         var lastCol = Math.floor( lastX / this.columnWidth );
35227         // lastCol should not go over if multiple of columnWidth #425
35228         lastCol -= lastX % this.columnWidth ? 0 : 1;
35229         lastCol = Math.min( this.cols - 1, lastCol );
35230         
35231         // set colYs to bottom of the stamp
35232         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35233             stampSize.height;
35234             
35235         for ( var i = firstCol; i <= lastCol; i++ ) {
35236           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35237         }
35238     },
35239     */
35240     
35241     _getContainerSize : function()
35242     {
35243         this.maxY = Math.max.apply( Math, this.colYs );
35244         var size = {
35245             height: this.maxY
35246         };
35247       
35248         if ( this.isFitWidth ) {
35249             size.width = this._getContainerFitWidth();
35250         }
35251       
35252         return size;
35253     },
35254     
35255     _getContainerFitWidth : function()
35256     {
35257         var unusedCols = 0;
35258         // count unused columns
35259         var i = this.cols;
35260         while ( --i ) {
35261           if ( this.colYs[i] !== 0 ) {
35262             break;
35263           }
35264           unusedCols++;
35265         }
35266         // fit container to columns that have been used
35267         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35268     },
35269     
35270     needsResizeLayout : function()
35271     {
35272         var previousWidth = this.containerWidth;
35273         this.getContainerWidth();
35274         return previousWidth !== this.containerWidth;
35275     }
35276  
35277 });
35278
35279  
35280
35281  /*
35282  * - LGPL
35283  *
35284  * element
35285  * 
35286  */
35287
35288 /**
35289  * @class Roo.bootstrap.MasonryBrick
35290  * @extends Roo.bootstrap.Component
35291  * Bootstrap MasonryBrick class
35292  * 
35293  * @constructor
35294  * Create a new MasonryBrick
35295  * @param {Object} config The config object
35296  */
35297
35298 Roo.bootstrap.MasonryBrick = function(config){
35299     
35300     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35301     
35302     Roo.bootstrap.MasonryBrick.register(this);
35303     
35304     this.addEvents({
35305         // raw events
35306         /**
35307          * @event click
35308          * When a MasonryBrick is clcik
35309          * @param {Roo.bootstrap.MasonryBrick} this
35310          * @param {Roo.EventObject} e
35311          */
35312         "click" : true
35313     });
35314 };
35315
35316 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35317     
35318     /**
35319      * @cfg {String} title
35320      */   
35321     title : '',
35322     /**
35323      * @cfg {String} html
35324      */   
35325     html : '',
35326     /**
35327      * @cfg {String} bgimage
35328      */   
35329     bgimage : '',
35330     /**
35331      * @cfg {String} videourl
35332      */   
35333     videourl : '',
35334     /**
35335      * @cfg {String} cls
35336      */   
35337     cls : '',
35338     /**
35339      * @cfg {String} href
35340      */   
35341     href : '',
35342     /**
35343      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35344      */   
35345     size : 'xs',
35346     
35347     /**
35348      * @cfg {String} placetitle (center|bottom)
35349      */   
35350     placetitle : '',
35351     
35352     /**
35353      * @cfg {Boolean} isFitContainer defalut true
35354      */   
35355     isFitContainer : true, 
35356     
35357     /**
35358      * @cfg {Boolean} preventDefault defalut false
35359      */   
35360     preventDefault : false, 
35361     
35362     /**
35363      * @cfg {Boolean} inverse defalut false
35364      */   
35365     maskInverse : false, 
35366     
35367     getAutoCreate : function()
35368     {
35369         if(!this.isFitContainer){
35370             return this.getSplitAutoCreate();
35371         }
35372         
35373         var cls = 'masonry-brick masonry-brick-full';
35374         
35375         if(this.href.length){
35376             cls += ' masonry-brick-link';
35377         }
35378         
35379         if(this.bgimage.length){
35380             cls += ' masonry-brick-image';
35381         }
35382         
35383         if(this.maskInverse){
35384             cls += ' mask-inverse';
35385         }
35386         
35387         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35388             cls += ' enable-mask';
35389         }
35390         
35391         if(this.size){
35392             cls += ' masonry-' + this.size + '-brick';
35393         }
35394         
35395         if(this.placetitle.length){
35396             
35397             switch (this.placetitle) {
35398                 case 'center' :
35399                     cls += ' masonry-center-title';
35400                     break;
35401                 case 'bottom' :
35402                     cls += ' masonry-bottom-title';
35403                     break;
35404                 default:
35405                     break;
35406             }
35407             
35408         } else {
35409             if(!this.html.length && !this.bgimage.length){
35410                 cls += ' masonry-center-title';
35411             }
35412
35413             if(!this.html.length && this.bgimage.length){
35414                 cls += ' masonry-bottom-title';
35415             }
35416         }
35417         
35418         if(this.cls){
35419             cls += ' ' + this.cls;
35420         }
35421         
35422         var cfg = {
35423             tag: (this.href.length) ? 'a' : 'div',
35424             cls: cls,
35425             cn: [
35426                 {
35427                     tag: 'div',
35428                     cls: 'masonry-brick-mask'
35429                 },
35430                 {
35431                     tag: 'div',
35432                     cls: 'masonry-brick-paragraph',
35433                     cn: []
35434                 }
35435             ]
35436         };
35437         
35438         if(this.href.length){
35439             cfg.href = this.href;
35440         }
35441         
35442         var cn = cfg.cn[1].cn;
35443         
35444         if(this.title.length){
35445             cn.push({
35446                 tag: 'h4',
35447                 cls: 'masonry-brick-title',
35448                 html: this.title
35449             });
35450         }
35451         
35452         if(this.html.length){
35453             cn.push({
35454                 tag: 'p',
35455                 cls: 'masonry-brick-text',
35456                 html: this.html
35457             });
35458         }
35459         
35460         if (!this.title.length && !this.html.length) {
35461             cfg.cn[1].cls += ' hide';
35462         }
35463         
35464         if(this.bgimage.length){
35465             cfg.cn.push({
35466                 tag: 'img',
35467                 cls: 'masonry-brick-image-view',
35468                 src: this.bgimage
35469             });
35470         }
35471         
35472         if(this.videourl.length){
35473             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35474             // youtube support only?
35475             cfg.cn.push({
35476                 tag: 'iframe',
35477                 cls: 'masonry-brick-image-view',
35478                 src: vurl,
35479                 frameborder : 0,
35480                 allowfullscreen : true
35481             });
35482         }
35483         
35484         return cfg;
35485         
35486     },
35487     
35488     getSplitAutoCreate : function()
35489     {
35490         var cls = 'masonry-brick masonry-brick-split';
35491         
35492         if(this.href.length){
35493             cls += ' masonry-brick-link';
35494         }
35495         
35496         if(this.bgimage.length){
35497             cls += ' masonry-brick-image';
35498         }
35499         
35500         if(this.size){
35501             cls += ' masonry-' + this.size + '-brick';
35502         }
35503         
35504         switch (this.placetitle) {
35505             case 'center' :
35506                 cls += ' masonry-center-title';
35507                 break;
35508             case 'bottom' :
35509                 cls += ' masonry-bottom-title';
35510                 break;
35511             default:
35512                 if(!this.bgimage.length){
35513                     cls += ' masonry-center-title';
35514                 }
35515
35516                 if(this.bgimage.length){
35517                     cls += ' masonry-bottom-title';
35518                 }
35519                 break;
35520         }
35521         
35522         if(this.cls){
35523             cls += ' ' + this.cls;
35524         }
35525         
35526         var cfg = {
35527             tag: (this.href.length) ? 'a' : 'div',
35528             cls: cls,
35529             cn: [
35530                 {
35531                     tag: 'div',
35532                     cls: 'masonry-brick-split-head',
35533                     cn: [
35534                         {
35535                             tag: 'div',
35536                             cls: 'masonry-brick-paragraph',
35537                             cn: []
35538                         }
35539                     ]
35540                 },
35541                 {
35542                     tag: 'div',
35543                     cls: 'masonry-brick-split-body',
35544                     cn: []
35545                 }
35546             ]
35547         };
35548         
35549         if(this.href.length){
35550             cfg.href = this.href;
35551         }
35552         
35553         if(this.title.length){
35554             cfg.cn[0].cn[0].cn.push({
35555                 tag: 'h4',
35556                 cls: 'masonry-brick-title',
35557                 html: this.title
35558             });
35559         }
35560         
35561         if(this.html.length){
35562             cfg.cn[1].cn.push({
35563                 tag: 'p',
35564                 cls: 'masonry-brick-text',
35565                 html: this.html
35566             });
35567         }
35568
35569         if(this.bgimage.length){
35570             cfg.cn[0].cn.push({
35571                 tag: 'img',
35572                 cls: 'masonry-brick-image-view',
35573                 src: this.bgimage
35574             });
35575         }
35576         
35577         if(this.videourl.length){
35578             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35579             // youtube support only?
35580             cfg.cn[0].cn.cn.push({
35581                 tag: 'iframe',
35582                 cls: 'masonry-brick-image-view',
35583                 src: vurl,
35584                 frameborder : 0,
35585                 allowfullscreen : true
35586             });
35587         }
35588         
35589         return cfg;
35590     },
35591     
35592     initEvents: function() 
35593     {
35594         switch (this.size) {
35595             case 'xs' :
35596                 this.x = 1;
35597                 this.y = 1;
35598                 break;
35599             case 'sm' :
35600                 this.x = 2;
35601                 this.y = 2;
35602                 break;
35603             case 'md' :
35604             case 'md-left' :
35605             case 'md-right' :
35606                 this.x = 3;
35607                 this.y = 3;
35608                 break;
35609             case 'tall' :
35610                 this.x = 2;
35611                 this.y = 3;
35612                 break;
35613             case 'wide' :
35614                 this.x = 3;
35615                 this.y = 2;
35616                 break;
35617             case 'wide-thin' :
35618                 this.x = 3;
35619                 this.y = 1;
35620                 break;
35621                         
35622             default :
35623                 break;
35624         }
35625         
35626         if(Roo.isTouch){
35627             this.el.on('touchstart', this.onTouchStart, this);
35628             this.el.on('touchmove', this.onTouchMove, this);
35629             this.el.on('touchend', this.onTouchEnd, this);
35630             this.el.on('contextmenu', this.onContextMenu, this);
35631         } else {
35632             this.el.on('mouseenter'  ,this.enter, this);
35633             this.el.on('mouseleave', this.leave, this);
35634             this.el.on('click', this.onClick, this);
35635         }
35636         
35637         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35638             this.parent().bricks.push(this);   
35639         }
35640         
35641     },
35642     
35643     onClick: function(e, el)
35644     {
35645         var time = this.endTimer - this.startTimer;
35646         // Roo.log(e.preventDefault());
35647         if(Roo.isTouch){
35648             if(time > 1000){
35649                 e.preventDefault();
35650                 return;
35651             }
35652         }
35653         
35654         if(!this.preventDefault){
35655             return;
35656         }
35657         
35658         e.preventDefault();
35659         
35660         if (this.activeClass != '') {
35661             this.selectBrick();
35662         }
35663         
35664         this.fireEvent('click', this, e);
35665     },
35666     
35667     enter: function(e, el)
35668     {
35669         e.preventDefault();
35670         
35671         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35672             return;
35673         }
35674         
35675         if(this.bgimage.length && this.html.length){
35676             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35677         }
35678     },
35679     
35680     leave: function(e, el)
35681     {
35682         e.preventDefault();
35683         
35684         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35685             return;
35686         }
35687         
35688         if(this.bgimage.length && this.html.length){
35689             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35690         }
35691     },
35692     
35693     onTouchStart: function(e, el)
35694     {
35695 //        e.preventDefault();
35696         
35697         this.touchmoved = false;
35698         
35699         if(!this.isFitContainer){
35700             return;
35701         }
35702         
35703         if(!this.bgimage.length || !this.html.length){
35704             return;
35705         }
35706         
35707         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35708         
35709         this.timer = new Date().getTime();
35710         
35711     },
35712     
35713     onTouchMove: function(e, el)
35714     {
35715         this.touchmoved = true;
35716     },
35717     
35718     onContextMenu : function(e,el)
35719     {
35720         e.preventDefault();
35721         e.stopPropagation();
35722         return false;
35723     },
35724     
35725     onTouchEnd: function(e, el)
35726     {
35727 //        e.preventDefault();
35728         
35729         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35730         
35731             this.leave(e,el);
35732             
35733             return;
35734         }
35735         
35736         if(!this.bgimage.length || !this.html.length){
35737             
35738             if(this.href.length){
35739                 window.location.href = this.href;
35740             }
35741             
35742             return;
35743         }
35744         
35745         if(!this.isFitContainer){
35746             return;
35747         }
35748         
35749         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35750         
35751         window.location.href = this.href;
35752     },
35753     
35754     //selection on single brick only
35755     selectBrick : function() {
35756         
35757         if (!this.parentId) {
35758             return;
35759         }
35760         
35761         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35762         var index = m.selectedBrick.indexOf(this.id);
35763         
35764         if ( index > -1) {
35765             m.selectedBrick.splice(index,1);
35766             this.el.removeClass(this.activeClass);
35767             return;
35768         }
35769         
35770         for(var i = 0; i < m.selectedBrick.length; i++) {
35771             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35772             b.el.removeClass(b.activeClass);
35773         }
35774         
35775         m.selectedBrick = [];
35776         
35777         m.selectedBrick.push(this.id);
35778         this.el.addClass(this.activeClass);
35779         return;
35780     },
35781     
35782     isSelected : function(){
35783         return this.el.hasClass(this.activeClass);
35784         
35785     }
35786 });
35787
35788 Roo.apply(Roo.bootstrap.MasonryBrick, {
35789     
35790     //groups: {},
35791     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35792      /**
35793     * register a Masonry Brick
35794     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35795     */
35796     
35797     register : function(brick)
35798     {
35799         //this.groups[brick.id] = brick;
35800         this.groups.add(brick.id, brick);
35801     },
35802     /**
35803     * fetch a  masonry brick based on the masonry brick ID
35804     * @param {string} the masonry brick to add
35805     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35806     */
35807     
35808     get: function(brick_id) 
35809     {
35810         // if (typeof(this.groups[brick_id]) == 'undefined') {
35811         //     return false;
35812         // }
35813         // return this.groups[brick_id] ;
35814         
35815         if(this.groups.key(brick_id)) {
35816             return this.groups.key(brick_id);
35817         }
35818         
35819         return false;
35820     }
35821     
35822     
35823     
35824 });
35825
35826  /*
35827  * - LGPL
35828  *
35829  * element
35830  * 
35831  */
35832
35833 /**
35834  * @class Roo.bootstrap.Brick
35835  * @extends Roo.bootstrap.Component
35836  * Bootstrap Brick class
35837  * 
35838  * @constructor
35839  * Create a new Brick
35840  * @param {Object} config The config object
35841  */
35842
35843 Roo.bootstrap.Brick = function(config){
35844     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35845     
35846     this.addEvents({
35847         // raw events
35848         /**
35849          * @event click
35850          * When a Brick is click
35851          * @param {Roo.bootstrap.Brick} this
35852          * @param {Roo.EventObject} e
35853          */
35854         "click" : true
35855     });
35856 };
35857
35858 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35859     
35860     /**
35861      * @cfg {String} title
35862      */   
35863     title : '',
35864     /**
35865      * @cfg {String} html
35866      */   
35867     html : '',
35868     /**
35869      * @cfg {String} bgimage
35870      */   
35871     bgimage : '',
35872     /**
35873      * @cfg {String} cls
35874      */   
35875     cls : '',
35876     /**
35877      * @cfg {String} href
35878      */   
35879     href : '',
35880     /**
35881      * @cfg {String} video
35882      */   
35883     video : '',
35884     /**
35885      * @cfg {Boolean} square
35886      */   
35887     square : true,
35888     
35889     getAutoCreate : function()
35890     {
35891         var cls = 'roo-brick';
35892         
35893         if(this.href.length){
35894             cls += ' roo-brick-link';
35895         }
35896         
35897         if(this.bgimage.length){
35898             cls += ' roo-brick-image';
35899         }
35900         
35901         if(!this.html.length && !this.bgimage.length){
35902             cls += ' roo-brick-center-title';
35903         }
35904         
35905         if(!this.html.length && this.bgimage.length){
35906             cls += ' roo-brick-bottom-title';
35907         }
35908         
35909         if(this.cls){
35910             cls += ' ' + this.cls;
35911         }
35912         
35913         var cfg = {
35914             tag: (this.href.length) ? 'a' : 'div',
35915             cls: cls,
35916             cn: [
35917                 {
35918                     tag: 'div',
35919                     cls: 'roo-brick-paragraph',
35920                     cn: []
35921                 }
35922             ]
35923         };
35924         
35925         if(this.href.length){
35926             cfg.href = this.href;
35927         }
35928         
35929         var cn = cfg.cn[0].cn;
35930         
35931         if(this.title.length){
35932             cn.push({
35933                 tag: 'h4',
35934                 cls: 'roo-brick-title',
35935                 html: this.title
35936             });
35937         }
35938         
35939         if(this.html.length){
35940             cn.push({
35941                 tag: 'p',
35942                 cls: 'roo-brick-text',
35943                 html: this.html
35944             });
35945         } else {
35946             cn.cls += ' hide';
35947         }
35948         
35949         if(this.bgimage.length){
35950             cfg.cn.push({
35951                 tag: 'img',
35952                 cls: 'roo-brick-image-view',
35953                 src: this.bgimage
35954             });
35955         }
35956         
35957         return cfg;
35958     },
35959     
35960     initEvents: function() 
35961     {
35962         if(this.title.length || this.html.length){
35963             this.el.on('mouseenter'  ,this.enter, this);
35964             this.el.on('mouseleave', this.leave, this);
35965         }
35966         
35967         Roo.EventManager.onWindowResize(this.resize, this); 
35968         
35969         if(this.bgimage.length){
35970             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35971             this.imageEl.on('load', this.onImageLoad, this);
35972             return;
35973         }
35974         
35975         this.resize();
35976     },
35977     
35978     onImageLoad : function()
35979     {
35980         this.resize();
35981     },
35982     
35983     resize : function()
35984     {
35985         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35986         
35987         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35988         
35989         if(this.bgimage.length){
35990             var image = this.el.select('.roo-brick-image-view', true).first();
35991             
35992             image.setWidth(paragraph.getWidth());
35993             
35994             if(this.square){
35995                 image.setHeight(paragraph.getWidth());
35996             }
35997             
35998             this.el.setHeight(image.getHeight());
35999             paragraph.setHeight(image.getHeight());
36000             
36001         }
36002         
36003     },
36004     
36005     enter: function(e, el)
36006     {
36007         e.preventDefault();
36008         
36009         if(this.bgimage.length){
36010             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36011             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36012         }
36013     },
36014     
36015     leave: function(e, el)
36016     {
36017         e.preventDefault();
36018         
36019         if(this.bgimage.length){
36020             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36021             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36022         }
36023     }
36024     
36025 });
36026
36027  
36028
36029  /*
36030  * - LGPL
36031  *
36032  * Number field 
36033  */
36034
36035 /**
36036  * @class Roo.bootstrap.NumberField
36037  * @extends Roo.bootstrap.Input
36038  * Bootstrap NumberField class
36039  * 
36040  * 
36041  * 
36042  * 
36043  * @constructor
36044  * Create a new NumberField
36045  * @param {Object} config The config object
36046  */
36047
36048 Roo.bootstrap.NumberField = function(config){
36049     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36050 };
36051
36052 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36053     
36054     /**
36055      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36056      */
36057     allowDecimals : true,
36058     /**
36059      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36060      */
36061     decimalSeparator : ".",
36062     /**
36063      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36064      */
36065     decimalPrecision : 2,
36066     /**
36067      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36068      */
36069     allowNegative : true,
36070     
36071     /**
36072      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36073      */
36074     allowZero: true,
36075     /**
36076      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36077      */
36078     minValue : Number.NEGATIVE_INFINITY,
36079     /**
36080      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36081      */
36082     maxValue : Number.MAX_VALUE,
36083     /**
36084      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36085      */
36086     minText : "The minimum value for this field is {0}",
36087     /**
36088      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36089      */
36090     maxText : "The maximum value for this field is {0}",
36091     /**
36092      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36093      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36094      */
36095     nanText : "{0} is not a valid number",
36096     /**
36097      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36098      */
36099     thousandsDelimiter : false,
36100     /**
36101      * @cfg {String} valueAlign alignment of value
36102      */
36103     valueAlign : "left",
36104
36105     getAutoCreate : function()
36106     {
36107         var hiddenInput = {
36108             tag: 'input',
36109             type: 'hidden',
36110             id: Roo.id(),
36111             cls: 'hidden-number-input'
36112         };
36113         
36114         if (this.name) {
36115             hiddenInput.name = this.name;
36116         }
36117         
36118         this.name = '';
36119         
36120         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36121         
36122         this.name = hiddenInput.name;
36123         
36124         if(cfg.cn.length > 0) {
36125             cfg.cn.push(hiddenInput);
36126         }
36127         
36128         return cfg;
36129     },
36130
36131     // private
36132     initEvents : function()
36133     {   
36134         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36135         
36136         var allowed = "0123456789";
36137         
36138         if(this.allowDecimals){
36139             allowed += this.decimalSeparator;
36140         }
36141         
36142         if(this.allowNegative){
36143             allowed += "-";
36144         }
36145         
36146         if(this.thousandsDelimiter) {
36147             allowed += ",";
36148         }
36149         
36150         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36151         
36152         var keyPress = function(e){
36153             
36154             var k = e.getKey();
36155             
36156             var c = e.getCharCode();
36157             
36158             if(
36159                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36160                     allowed.indexOf(String.fromCharCode(c)) === -1
36161             ){
36162                 e.stopEvent();
36163                 return;
36164             }
36165             
36166             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36167                 return;
36168             }
36169             
36170             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36171                 e.stopEvent();
36172             }
36173         };
36174         
36175         this.el.on("keypress", keyPress, this);
36176     },
36177     
36178     validateValue : function(value)
36179     {
36180         
36181         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36182             return false;
36183         }
36184         
36185         var num = this.parseValue(value);
36186         
36187         if(isNaN(num)){
36188             this.markInvalid(String.format(this.nanText, value));
36189             return false;
36190         }
36191         
36192         if(num < this.minValue){
36193             this.markInvalid(String.format(this.minText, this.minValue));
36194             return false;
36195         }
36196         
36197         if(num > this.maxValue){
36198             this.markInvalid(String.format(this.maxText, this.maxValue));
36199             return false;
36200         }
36201         
36202         return true;
36203     },
36204
36205     getValue : function()
36206     {
36207         var v = this.hiddenEl().getValue();
36208         
36209         return this.fixPrecision(this.parseValue(v));
36210     },
36211
36212     parseValue : function(value)
36213     {
36214         if(this.thousandsDelimiter) {
36215             value += "";
36216             r = new RegExp(",", "g");
36217             value = value.replace(r, "");
36218         }
36219         
36220         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36221         return isNaN(value) ? '' : value;
36222     },
36223
36224     fixPrecision : function(value)
36225     {
36226         if(this.thousandsDelimiter) {
36227             value += "";
36228             r = new RegExp(",", "g");
36229             value = value.replace(r, "");
36230         }
36231         
36232         var nan = isNaN(value);
36233         
36234         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36235             return nan ? '' : value;
36236         }
36237         return parseFloat(value).toFixed(this.decimalPrecision);
36238     },
36239
36240     setValue : function(v)
36241     {
36242         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36243         
36244         this.value = v;
36245         
36246         if(this.rendered){
36247             
36248             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36249             
36250             this.inputEl().dom.value = (v == '') ? '' :
36251                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36252             
36253             if(!this.allowZero && v === '0') {
36254                 this.hiddenEl().dom.value = '';
36255                 this.inputEl().dom.value = '';
36256             }
36257             
36258             this.validate();
36259         }
36260     },
36261
36262     decimalPrecisionFcn : function(v)
36263     {
36264         return Math.floor(v);
36265     },
36266
36267     beforeBlur : function()
36268     {
36269         var v = this.parseValue(this.getRawValue());
36270         
36271         if(v || v === 0 || v === ''){
36272             this.setValue(v);
36273         }
36274     },
36275     
36276     hiddenEl : function()
36277     {
36278         return this.el.select('input.hidden-number-input',true).first();
36279     }
36280     
36281 });
36282
36283  
36284
36285 /*
36286 * Licence: LGPL
36287 */
36288
36289 /**
36290  * @class Roo.bootstrap.DocumentSlider
36291  * @extends Roo.bootstrap.Component
36292  * Bootstrap DocumentSlider class
36293  * 
36294  * @constructor
36295  * Create a new DocumentViewer
36296  * @param {Object} config The config object
36297  */
36298
36299 Roo.bootstrap.DocumentSlider = function(config){
36300     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36301     
36302     this.files = [];
36303     
36304     this.addEvents({
36305         /**
36306          * @event initial
36307          * Fire after initEvent
36308          * @param {Roo.bootstrap.DocumentSlider} this
36309          */
36310         "initial" : true,
36311         /**
36312          * @event update
36313          * Fire after update
36314          * @param {Roo.bootstrap.DocumentSlider} this
36315          */
36316         "update" : true,
36317         /**
36318          * @event click
36319          * Fire after click
36320          * @param {Roo.bootstrap.DocumentSlider} this
36321          */
36322         "click" : true
36323     });
36324 };
36325
36326 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36327     
36328     files : false,
36329     
36330     indicator : 0,
36331     
36332     getAutoCreate : function()
36333     {
36334         var cfg = {
36335             tag : 'div',
36336             cls : 'roo-document-slider',
36337             cn : [
36338                 {
36339                     tag : 'div',
36340                     cls : 'roo-document-slider-header',
36341                     cn : [
36342                         {
36343                             tag : 'div',
36344                             cls : 'roo-document-slider-header-title'
36345                         }
36346                     ]
36347                 },
36348                 {
36349                     tag : 'div',
36350                     cls : 'roo-document-slider-body',
36351                     cn : [
36352                         {
36353                             tag : 'div',
36354                             cls : 'roo-document-slider-prev',
36355                             cn : [
36356                                 {
36357                                     tag : 'i',
36358                                     cls : 'fa fa-chevron-left'
36359                                 }
36360                             ]
36361                         },
36362                         {
36363                             tag : 'div',
36364                             cls : 'roo-document-slider-thumb',
36365                             cn : [
36366                                 {
36367                                     tag : 'img',
36368                                     cls : 'roo-document-slider-image'
36369                                 }
36370                             ]
36371                         },
36372                         {
36373                             tag : 'div',
36374                             cls : 'roo-document-slider-next',
36375                             cn : [
36376                                 {
36377                                     tag : 'i',
36378                                     cls : 'fa fa-chevron-right'
36379                                 }
36380                             ]
36381                         }
36382                     ]
36383                 }
36384             ]
36385         };
36386         
36387         return cfg;
36388     },
36389     
36390     initEvents : function()
36391     {
36392         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36393         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36394         
36395         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36396         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36397         
36398         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36399         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36400         
36401         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36402         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36403         
36404         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36405         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36406         
36407         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36408         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36409         
36410         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36411         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36412         
36413         this.thumbEl.on('click', this.onClick, this);
36414         
36415         this.prevIndicator.on('click', this.prev, this);
36416         
36417         this.nextIndicator.on('click', this.next, this);
36418         
36419     },
36420     
36421     initial : function()
36422     {
36423         if(this.files.length){
36424             this.indicator = 1;
36425             this.update()
36426         }
36427         
36428         this.fireEvent('initial', this);
36429     },
36430     
36431     update : function()
36432     {
36433         this.imageEl.attr('src', this.files[this.indicator - 1]);
36434         
36435         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36436         
36437         this.prevIndicator.show();
36438         
36439         if(this.indicator == 1){
36440             this.prevIndicator.hide();
36441         }
36442         
36443         this.nextIndicator.show();
36444         
36445         if(this.indicator == this.files.length){
36446             this.nextIndicator.hide();
36447         }
36448         
36449         this.thumbEl.scrollTo('top');
36450         
36451         this.fireEvent('update', this);
36452     },
36453     
36454     onClick : function(e)
36455     {
36456         e.preventDefault();
36457         
36458         this.fireEvent('click', this);
36459     },
36460     
36461     prev : function(e)
36462     {
36463         e.preventDefault();
36464         
36465         this.indicator = Math.max(1, this.indicator - 1);
36466         
36467         this.update();
36468     },
36469     
36470     next : function(e)
36471     {
36472         e.preventDefault();
36473         
36474         this.indicator = Math.min(this.files.length, this.indicator + 1);
36475         
36476         this.update();
36477     }
36478 });
36479 /*
36480  * - LGPL
36481  *
36482  * RadioSet
36483  *
36484  *
36485  */
36486
36487 /**
36488  * @class Roo.bootstrap.RadioSet
36489  * @extends Roo.bootstrap.Input
36490  * Bootstrap RadioSet class
36491  * @cfg {String} indicatorpos (left|right) default left
36492  * @cfg {Boolean} inline (true|false) inline the element (default true)
36493  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36494  * @constructor
36495  * Create a new RadioSet
36496  * @param {Object} config The config object
36497  */
36498
36499 Roo.bootstrap.RadioSet = function(config){
36500     
36501     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36502     
36503     this.radioes = [];
36504     
36505     Roo.bootstrap.RadioSet.register(this);
36506     
36507     this.addEvents({
36508         /**
36509         * @event check
36510         * Fires when the element is checked or unchecked.
36511         * @param {Roo.bootstrap.RadioSet} this This radio
36512         * @param {Roo.bootstrap.Radio} item The checked item
36513         */
36514        check : true,
36515        /**
36516         * @event click
36517         * Fires when the element is click.
36518         * @param {Roo.bootstrap.RadioSet} this This radio set
36519         * @param {Roo.bootstrap.Radio} item The checked item
36520         * @param {Roo.EventObject} e The event object
36521         */
36522        click : true
36523     });
36524     
36525 };
36526
36527 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36528
36529     radioes : false,
36530     
36531     inline : true,
36532     
36533     weight : '',
36534     
36535     indicatorpos : 'left',
36536     
36537     getAutoCreate : function()
36538     {
36539         var label = {
36540             tag : 'label',
36541             cls : 'roo-radio-set-label',
36542             cn : [
36543                 {
36544                     tag : 'span',
36545                     html : this.fieldLabel
36546                 }
36547             ]
36548         };
36549         if (Roo.bootstrap.version == 3) {
36550             
36551             
36552             if(this.indicatorpos == 'left'){
36553                 label.cn.unshift({
36554                     tag : 'i',
36555                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36556                     tooltip : 'This field is required'
36557                 });
36558             } else {
36559                 label.cn.push({
36560                     tag : 'i',
36561                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36562                     tooltip : 'This field is required'
36563                 });
36564             }
36565         }
36566         var items = {
36567             tag : 'div',
36568             cls : 'roo-radio-set-items'
36569         };
36570         
36571         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36572         
36573         if (align === 'left' && this.fieldLabel.length) {
36574             
36575             items = {
36576                 cls : "roo-radio-set-right", 
36577                 cn: [
36578                     items
36579                 ]
36580             };
36581             
36582             if(this.labelWidth > 12){
36583                 label.style = "width: " + this.labelWidth + 'px';
36584             }
36585             
36586             if(this.labelWidth < 13 && this.labelmd == 0){
36587                 this.labelmd = this.labelWidth;
36588             }
36589             
36590             if(this.labellg > 0){
36591                 label.cls += ' col-lg-' + this.labellg;
36592                 items.cls += ' col-lg-' + (12 - this.labellg);
36593             }
36594             
36595             if(this.labelmd > 0){
36596                 label.cls += ' col-md-' + this.labelmd;
36597                 items.cls += ' col-md-' + (12 - this.labelmd);
36598             }
36599             
36600             if(this.labelsm > 0){
36601                 label.cls += ' col-sm-' + this.labelsm;
36602                 items.cls += ' col-sm-' + (12 - this.labelsm);
36603             }
36604             
36605             if(this.labelxs > 0){
36606                 label.cls += ' col-xs-' + this.labelxs;
36607                 items.cls += ' col-xs-' + (12 - this.labelxs);
36608             }
36609         }
36610         
36611         var cfg = {
36612             tag : 'div',
36613             cls : 'roo-radio-set',
36614             cn : [
36615                 {
36616                     tag : 'input',
36617                     cls : 'roo-radio-set-input',
36618                     type : 'hidden',
36619                     name : this.name,
36620                     value : this.value ? this.value :  ''
36621                 },
36622                 label,
36623                 items
36624             ]
36625         };
36626         
36627         if(this.weight.length){
36628             cfg.cls += ' roo-radio-' + this.weight;
36629         }
36630         
36631         if(this.inline) {
36632             cfg.cls += ' roo-radio-set-inline';
36633         }
36634         
36635         var settings=this;
36636         ['xs','sm','md','lg'].map(function(size){
36637             if (settings[size]) {
36638                 cfg.cls += ' col-' + size + '-' + settings[size];
36639             }
36640         });
36641         
36642         return cfg;
36643         
36644     },
36645
36646     initEvents : function()
36647     {
36648         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36649         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36650         
36651         if(!this.fieldLabel.length){
36652             this.labelEl.hide();
36653         }
36654         
36655         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36656         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36657         
36658         this.indicator = this.indicatorEl();
36659         
36660         if(this.indicator){
36661             this.indicator.addClass('invisible');
36662         }
36663         
36664         this.originalValue = this.getValue();
36665         
36666     },
36667     
36668     inputEl: function ()
36669     {
36670         return this.el.select('.roo-radio-set-input', true).first();
36671     },
36672     
36673     getChildContainer : function()
36674     {
36675         return this.itemsEl;
36676     },
36677     
36678     register : function(item)
36679     {
36680         this.radioes.push(item);
36681         
36682     },
36683     
36684     validate : function()
36685     {   
36686         if(this.getVisibilityEl().hasClass('hidden')){
36687             return true;
36688         }
36689         
36690         var valid = false;
36691         
36692         Roo.each(this.radioes, function(i){
36693             if(!i.checked){
36694                 return;
36695             }
36696             
36697             valid = true;
36698             return false;
36699         });
36700         
36701         if(this.allowBlank) {
36702             return true;
36703         }
36704         
36705         if(this.disabled || valid){
36706             this.markValid();
36707             return true;
36708         }
36709         
36710         this.markInvalid();
36711         return false;
36712         
36713     },
36714     
36715     markValid : function()
36716     {
36717         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36718             this.indicatorEl().removeClass('visible');
36719             this.indicatorEl().addClass('invisible');
36720         }
36721         
36722         
36723         if (Roo.bootstrap.version == 3) {
36724             this.el.removeClass([this.invalidClass, this.validClass]);
36725             this.el.addClass(this.validClass);
36726         } else {
36727             this.el.removeClass(['is-invalid','is-valid']);
36728             this.el.addClass(['is-valid']);
36729         }
36730         this.fireEvent('valid', this);
36731     },
36732     
36733     markInvalid : function(msg)
36734     {
36735         if(this.allowBlank || this.disabled){
36736             return;
36737         }
36738         
36739         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36740             this.indicatorEl().removeClass('invisible');
36741             this.indicatorEl().addClass('visible');
36742         }
36743         if (Roo.bootstrap.version == 3) {
36744             this.el.removeClass([this.invalidClass, this.validClass]);
36745             this.el.addClass(this.invalidClass);
36746         } else {
36747             this.el.removeClass(['is-invalid','is-valid']);
36748             this.el.addClass(['is-invalid']);
36749         }
36750         
36751         this.fireEvent('invalid', this, msg);
36752         
36753     },
36754     
36755     setValue : function(v, suppressEvent)
36756     {   
36757         if(this.value === v){
36758             return;
36759         }
36760         
36761         this.value = v;
36762         
36763         if(this.rendered){
36764             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36765         }
36766         
36767         Roo.each(this.radioes, function(i){
36768             i.checked = false;
36769             i.el.removeClass('checked');
36770         });
36771         
36772         Roo.each(this.radioes, function(i){
36773             
36774             if(i.value === v || i.value.toString() === v.toString()){
36775                 i.checked = true;
36776                 i.el.addClass('checked');
36777                 
36778                 if(suppressEvent !== true){
36779                     this.fireEvent('check', this, i);
36780                 }
36781                 
36782                 return false;
36783             }
36784             
36785         }, this);
36786         
36787         this.validate();
36788     },
36789     
36790     clearInvalid : function(){
36791         
36792         if(!this.el || this.preventMark){
36793             return;
36794         }
36795         
36796         this.el.removeClass([this.invalidClass]);
36797         
36798         this.fireEvent('valid', this);
36799     }
36800     
36801 });
36802
36803 Roo.apply(Roo.bootstrap.RadioSet, {
36804     
36805     groups: {},
36806     
36807     register : function(set)
36808     {
36809         this.groups[set.name] = set;
36810     },
36811     
36812     get: function(name) 
36813     {
36814         if (typeof(this.groups[name]) == 'undefined') {
36815             return false;
36816         }
36817         
36818         return this.groups[name] ;
36819     }
36820     
36821 });
36822 /*
36823  * Based on:
36824  * Ext JS Library 1.1.1
36825  * Copyright(c) 2006-2007, Ext JS, LLC.
36826  *
36827  * Originally Released Under LGPL - original licence link has changed is not relivant.
36828  *
36829  * Fork - LGPL
36830  * <script type="text/javascript">
36831  */
36832
36833
36834 /**
36835  * @class Roo.bootstrap.SplitBar
36836  * @extends Roo.util.Observable
36837  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36838  * <br><br>
36839  * Usage:
36840  * <pre><code>
36841 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36842                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36843 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36844 split.minSize = 100;
36845 split.maxSize = 600;
36846 split.animate = true;
36847 split.on('moved', splitterMoved);
36848 </code></pre>
36849  * @constructor
36850  * Create a new SplitBar
36851  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36852  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36853  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36854  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36855                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36856                         position of the SplitBar).
36857  */
36858 Roo.bootstrap.SplitBar = function(cfg){
36859     
36860     /** @private */
36861     
36862     //{
36863     //  dragElement : elm
36864     //  resizingElement: el,
36865         // optional..
36866     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36867     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36868         // existingProxy ???
36869     //}
36870     
36871     this.el = Roo.get(cfg.dragElement, true);
36872     this.el.dom.unselectable = "on";
36873     /** @private */
36874     this.resizingEl = Roo.get(cfg.resizingElement, true);
36875
36876     /**
36877      * @private
36878      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36879      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36880      * @type Number
36881      */
36882     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36883     
36884     /**
36885      * The minimum size of the resizing element. (Defaults to 0)
36886      * @type Number
36887      */
36888     this.minSize = 0;
36889     
36890     /**
36891      * The maximum size of the resizing element. (Defaults to 2000)
36892      * @type Number
36893      */
36894     this.maxSize = 2000;
36895     
36896     /**
36897      * Whether to animate the transition to the new size
36898      * @type Boolean
36899      */
36900     this.animate = false;
36901     
36902     /**
36903      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36904      * @type Boolean
36905      */
36906     this.useShim = false;
36907     
36908     /** @private */
36909     this.shim = null;
36910     
36911     if(!cfg.existingProxy){
36912         /** @private */
36913         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36914     }else{
36915         this.proxy = Roo.get(cfg.existingProxy).dom;
36916     }
36917     /** @private */
36918     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36919     
36920     /** @private */
36921     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36922     
36923     /** @private */
36924     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36925     
36926     /** @private */
36927     this.dragSpecs = {};
36928     
36929     /**
36930      * @private The adapter to use to positon and resize elements
36931      */
36932     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36933     this.adapter.init(this);
36934     
36935     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36936         /** @private */
36937         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36938         this.el.addClass("roo-splitbar-h");
36939     }else{
36940         /** @private */
36941         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36942         this.el.addClass("roo-splitbar-v");
36943     }
36944     
36945     this.addEvents({
36946         /**
36947          * @event resize
36948          * Fires when the splitter is moved (alias for {@link #event-moved})
36949          * @param {Roo.bootstrap.SplitBar} this
36950          * @param {Number} newSize the new width or height
36951          */
36952         "resize" : true,
36953         /**
36954          * @event moved
36955          * Fires when the splitter is moved
36956          * @param {Roo.bootstrap.SplitBar} this
36957          * @param {Number} newSize the new width or height
36958          */
36959         "moved" : true,
36960         /**
36961          * @event beforeresize
36962          * Fires before the splitter is dragged
36963          * @param {Roo.bootstrap.SplitBar} this
36964          */
36965         "beforeresize" : true,
36966
36967         "beforeapply" : true
36968     });
36969
36970     Roo.util.Observable.call(this);
36971 };
36972
36973 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36974     onStartProxyDrag : function(x, y){
36975         this.fireEvent("beforeresize", this);
36976         if(!this.overlay){
36977             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36978             o.unselectable();
36979             o.enableDisplayMode("block");
36980             // all splitbars share the same overlay
36981             Roo.bootstrap.SplitBar.prototype.overlay = o;
36982         }
36983         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36984         this.overlay.show();
36985         Roo.get(this.proxy).setDisplayed("block");
36986         var size = this.adapter.getElementSize(this);
36987         this.activeMinSize = this.getMinimumSize();;
36988         this.activeMaxSize = this.getMaximumSize();;
36989         var c1 = size - this.activeMinSize;
36990         var c2 = Math.max(this.activeMaxSize - size, 0);
36991         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36992             this.dd.resetConstraints();
36993             this.dd.setXConstraint(
36994                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36995                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36996             );
36997             this.dd.setYConstraint(0, 0);
36998         }else{
36999             this.dd.resetConstraints();
37000             this.dd.setXConstraint(0, 0);
37001             this.dd.setYConstraint(
37002                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37003                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37004             );
37005          }
37006         this.dragSpecs.startSize = size;
37007         this.dragSpecs.startPoint = [x, y];
37008         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37009     },
37010     
37011     /** 
37012      * @private Called after the drag operation by the DDProxy
37013      */
37014     onEndProxyDrag : function(e){
37015         Roo.get(this.proxy).setDisplayed(false);
37016         var endPoint = Roo.lib.Event.getXY(e);
37017         if(this.overlay){
37018             this.overlay.hide();
37019         }
37020         var newSize;
37021         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37022             newSize = this.dragSpecs.startSize + 
37023                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37024                     endPoint[0] - this.dragSpecs.startPoint[0] :
37025                     this.dragSpecs.startPoint[0] - endPoint[0]
37026                 );
37027         }else{
37028             newSize = this.dragSpecs.startSize + 
37029                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37030                     endPoint[1] - this.dragSpecs.startPoint[1] :
37031                     this.dragSpecs.startPoint[1] - endPoint[1]
37032                 );
37033         }
37034         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37035         if(newSize != this.dragSpecs.startSize){
37036             if(this.fireEvent('beforeapply', this, newSize) !== false){
37037                 this.adapter.setElementSize(this, newSize);
37038                 this.fireEvent("moved", this, newSize);
37039                 this.fireEvent("resize", this, newSize);
37040             }
37041         }
37042     },
37043     
37044     /**
37045      * Get the adapter this SplitBar uses
37046      * @return The adapter object
37047      */
37048     getAdapter : function(){
37049         return this.adapter;
37050     },
37051     
37052     /**
37053      * Set the adapter this SplitBar uses
37054      * @param {Object} adapter A SplitBar adapter object
37055      */
37056     setAdapter : function(adapter){
37057         this.adapter = adapter;
37058         this.adapter.init(this);
37059     },
37060     
37061     /**
37062      * Gets the minimum size for the resizing element
37063      * @return {Number} The minimum size
37064      */
37065     getMinimumSize : function(){
37066         return this.minSize;
37067     },
37068     
37069     /**
37070      * Sets the minimum size for the resizing element
37071      * @param {Number} minSize The minimum size
37072      */
37073     setMinimumSize : function(minSize){
37074         this.minSize = minSize;
37075     },
37076     
37077     /**
37078      * Gets the maximum size for the resizing element
37079      * @return {Number} The maximum size
37080      */
37081     getMaximumSize : function(){
37082         return this.maxSize;
37083     },
37084     
37085     /**
37086      * Sets the maximum size for the resizing element
37087      * @param {Number} maxSize The maximum size
37088      */
37089     setMaximumSize : function(maxSize){
37090         this.maxSize = maxSize;
37091     },
37092     
37093     /**
37094      * Sets the initialize size for the resizing element
37095      * @param {Number} size The initial size
37096      */
37097     setCurrentSize : function(size){
37098         var oldAnimate = this.animate;
37099         this.animate = false;
37100         this.adapter.setElementSize(this, size);
37101         this.animate = oldAnimate;
37102     },
37103     
37104     /**
37105      * Destroy this splitbar. 
37106      * @param {Boolean} removeEl True to remove the element
37107      */
37108     destroy : function(removeEl){
37109         if(this.shim){
37110             this.shim.remove();
37111         }
37112         this.dd.unreg();
37113         this.proxy.parentNode.removeChild(this.proxy);
37114         if(removeEl){
37115             this.el.remove();
37116         }
37117     }
37118 });
37119
37120 /**
37121  * @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.
37122  */
37123 Roo.bootstrap.SplitBar.createProxy = function(dir){
37124     var proxy = new Roo.Element(document.createElement("div"));
37125     proxy.unselectable();
37126     var cls = 'roo-splitbar-proxy';
37127     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37128     document.body.appendChild(proxy.dom);
37129     return proxy.dom;
37130 };
37131
37132 /** 
37133  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37134  * Default Adapter. It assumes the splitter and resizing element are not positioned
37135  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37136  */
37137 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37138 };
37139
37140 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37141     // do nothing for now
37142     init : function(s){
37143     
37144     },
37145     /**
37146      * Called before drag operations to get the current size of the resizing element. 
37147      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37148      */
37149      getElementSize : function(s){
37150         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37151             return s.resizingEl.getWidth();
37152         }else{
37153             return s.resizingEl.getHeight();
37154         }
37155     },
37156     
37157     /**
37158      * Called after drag operations to set the size of the resizing element.
37159      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37160      * @param {Number} newSize The new size to set
37161      * @param {Function} onComplete A function to be invoked when resizing is complete
37162      */
37163     setElementSize : function(s, newSize, onComplete){
37164         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37165             if(!s.animate){
37166                 s.resizingEl.setWidth(newSize);
37167                 if(onComplete){
37168                     onComplete(s, newSize);
37169                 }
37170             }else{
37171                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37172             }
37173         }else{
37174             
37175             if(!s.animate){
37176                 s.resizingEl.setHeight(newSize);
37177                 if(onComplete){
37178                     onComplete(s, newSize);
37179                 }
37180             }else{
37181                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37182             }
37183         }
37184     }
37185 };
37186
37187 /** 
37188  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37189  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37190  * Adapter that  moves the splitter element to align with the resized sizing element. 
37191  * Used with an absolute positioned SplitBar.
37192  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37193  * document.body, make sure you assign an id to the body element.
37194  */
37195 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37196     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37197     this.container = Roo.get(container);
37198 };
37199
37200 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37201     init : function(s){
37202         this.basic.init(s);
37203     },
37204     
37205     getElementSize : function(s){
37206         return this.basic.getElementSize(s);
37207     },
37208     
37209     setElementSize : function(s, newSize, onComplete){
37210         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37211     },
37212     
37213     moveSplitter : function(s){
37214         var yes = Roo.bootstrap.SplitBar;
37215         switch(s.placement){
37216             case yes.LEFT:
37217                 s.el.setX(s.resizingEl.getRight());
37218                 break;
37219             case yes.RIGHT:
37220                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37221                 break;
37222             case yes.TOP:
37223                 s.el.setY(s.resizingEl.getBottom());
37224                 break;
37225             case yes.BOTTOM:
37226                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37227                 break;
37228         }
37229     }
37230 };
37231
37232 /**
37233  * Orientation constant - Create a vertical SplitBar
37234  * @static
37235  * @type Number
37236  */
37237 Roo.bootstrap.SplitBar.VERTICAL = 1;
37238
37239 /**
37240  * Orientation constant - Create a horizontal SplitBar
37241  * @static
37242  * @type Number
37243  */
37244 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37245
37246 /**
37247  * Placement constant - The resizing element is to the left of the splitter element
37248  * @static
37249  * @type Number
37250  */
37251 Roo.bootstrap.SplitBar.LEFT = 1;
37252
37253 /**
37254  * Placement constant - The resizing element is to the right of the splitter element
37255  * @static
37256  * @type Number
37257  */
37258 Roo.bootstrap.SplitBar.RIGHT = 2;
37259
37260 /**
37261  * Placement constant - The resizing element is positioned above the splitter element
37262  * @static
37263  * @type Number
37264  */
37265 Roo.bootstrap.SplitBar.TOP = 3;
37266
37267 /**
37268  * Placement constant - The resizing element is positioned under splitter element
37269  * @static
37270  * @type Number
37271  */
37272 Roo.bootstrap.SplitBar.BOTTOM = 4;
37273 Roo.namespace("Roo.bootstrap.layout");/*
37274  * Based on:
37275  * Ext JS Library 1.1.1
37276  * Copyright(c) 2006-2007, Ext JS, LLC.
37277  *
37278  * Originally Released Under LGPL - original licence link has changed is not relivant.
37279  *
37280  * Fork - LGPL
37281  * <script type="text/javascript">
37282  */
37283
37284 /**
37285  * @class Roo.bootstrap.layout.Manager
37286  * @extends Roo.bootstrap.Component
37287  * Base class for layout managers.
37288  */
37289 Roo.bootstrap.layout.Manager = function(config)
37290 {
37291     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37292
37293
37294
37295
37296
37297     /** false to disable window resize monitoring @type Boolean */
37298     this.monitorWindowResize = true;
37299     this.regions = {};
37300     this.addEvents({
37301         /**
37302          * @event layout
37303          * Fires when a layout is performed.
37304          * @param {Roo.LayoutManager} this
37305          */
37306         "layout" : true,
37307         /**
37308          * @event regionresized
37309          * Fires when the user resizes a region.
37310          * @param {Roo.LayoutRegion} region The resized region
37311          * @param {Number} newSize The new size (width for east/west, height for north/south)
37312          */
37313         "regionresized" : true,
37314         /**
37315          * @event regioncollapsed
37316          * Fires when a region is collapsed.
37317          * @param {Roo.LayoutRegion} region The collapsed region
37318          */
37319         "regioncollapsed" : true,
37320         /**
37321          * @event regionexpanded
37322          * Fires when a region is expanded.
37323          * @param {Roo.LayoutRegion} region The expanded region
37324          */
37325         "regionexpanded" : true
37326     });
37327     this.updating = false;
37328
37329     if (config.el) {
37330         this.el = Roo.get(config.el);
37331         this.initEvents();
37332     }
37333
37334 };
37335
37336 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37337
37338
37339     regions : null,
37340
37341     monitorWindowResize : true,
37342
37343
37344     updating : false,
37345
37346
37347     onRender : function(ct, position)
37348     {
37349         if(!this.el){
37350             this.el = Roo.get(ct);
37351             this.initEvents();
37352         }
37353         //this.fireEvent('render',this);
37354     },
37355
37356
37357     initEvents: function()
37358     {
37359
37360
37361         // ie scrollbar fix
37362         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37363             document.body.scroll = "no";
37364         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37365             this.el.position('relative');
37366         }
37367         this.id = this.el.id;
37368         this.el.addClass("roo-layout-container");
37369         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37370         if(this.el.dom != document.body ) {
37371             this.el.on('resize', this.layout,this);
37372             this.el.on('show', this.layout,this);
37373         }
37374
37375     },
37376
37377     /**
37378      * Returns true if this layout is currently being updated
37379      * @return {Boolean}
37380      */
37381     isUpdating : function(){
37382         return this.updating;
37383     },
37384
37385     /**
37386      * Suspend the LayoutManager from doing auto-layouts while
37387      * making multiple add or remove calls
37388      */
37389     beginUpdate : function(){
37390         this.updating = true;
37391     },
37392
37393     /**
37394      * Restore auto-layouts and optionally disable the manager from performing a layout
37395      * @param {Boolean} noLayout true to disable a layout update
37396      */
37397     endUpdate : function(noLayout){
37398         this.updating = false;
37399         if(!noLayout){
37400             this.layout();
37401         }
37402     },
37403
37404     layout: function(){
37405         // abstract...
37406     },
37407
37408     onRegionResized : function(region, newSize){
37409         this.fireEvent("regionresized", region, newSize);
37410         this.layout();
37411     },
37412
37413     onRegionCollapsed : function(region){
37414         this.fireEvent("regioncollapsed", region);
37415     },
37416
37417     onRegionExpanded : function(region){
37418         this.fireEvent("regionexpanded", region);
37419     },
37420
37421     /**
37422      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37423      * performs box-model adjustments.
37424      * @return {Object} The size as an object {width: (the width), height: (the height)}
37425      */
37426     getViewSize : function()
37427     {
37428         var size;
37429         if(this.el.dom != document.body){
37430             size = this.el.getSize();
37431         }else{
37432             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37433         }
37434         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37435         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37436         return size;
37437     },
37438
37439     /**
37440      * Returns the Element this layout is bound to.
37441      * @return {Roo.Element}
37442      */
37443     getEl : function(){
37444         return this.el;
37445     },
37446
37447     /**
37448      * Returns the specified region.
37449      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37450      * @return {Roo.LayoutRegion}
37451      */
37452     getRegion : function(target){
37453         return this.regions[target.toLowerCase()];
37454     },
37455
37456     onWindowResize : function(){
37457         if(this.monitorWindowResize){
37458             this.layout();
37459         }
37460     }
37461 });
37462 /*
37463  * Based on:
37464  * Ext JS Library 1.1.1
37465  * Copyright(c) 2006-2007, Ext JS, LLC.
37466  *
37467  * Originally Released Under LGPL - original licence link has changed is not relivant.
37468  *
37469  * Fork - LGPL
37470  * <script type="text/javascript">
37471  */
37472 /**
37473  * @class Roo.bootstrap.layout.Border
37474  * @extends Roo.bootstrap.layout.Manager
37475  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37476  * please see: examples/bootstrap/nested.html<br><br>
37477  
37478 <b>The container the layout is rendered into can be either the body element or any other element.
37479 If it is not the body element, the container needs to either be an absolute positioned element,
37480 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37481 the container size if it is not the body element.</b>
37482
37483 * @constructor
37484 * Create a new Border
37485 * @param {Object} config Configuration options
37486  */
37487 Roo.bootstrap.layout.Border = function(config){
37488     config = config || {};
37489     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37490     
37491     
37492     
37493     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37494         if(config[region]){
37495             config[region].region = region;
37496             this.addRegion(config[region]);
37497         }
37498     },this);
37499     
37500 };
37501
37502 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37503
37504 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37505     
37506     parent : false, // this might point to a 'nest' or a ???
37507     
37508     /**
37509      * Creates and adds a new region if it doesn't already exist.
37510      * @param {String} target The target region key (north, south, east, west or center).
37511      * @param {Object} config The regions config object
37512      * @return {BorderLayoutRegion} The new region
37513      */
37514     addRegion : function(config)
37515     {
37516         if(!this.regions[config.region]){
37517             var r = this.factory(config);
37518             this.bindRegion(r);
37519         }
37520         return this.regions[config.region];
37521     },
37522
37523     // private (kinda)
37524     bindRegion : function(r){
37525         this.regions[r.config.region] = r;
37526         
37527         r.on("visibilitychange",    this.layout, this);
37528         r.on("paneladded",          this.layout, this);
37529         r.on("panelremoved",        this.layout, this);
37530         r.on("invalidated",         this.layout, this);
37531         r.on("resized",             this.onRegionResized, this);
37532         r.on("collapsed",           this.onRegionCollapsed, this);
37533         r.on("expanded",            this.onRegionExpanded, this);
37534     },
37535
37536     /**
37537      * Performs a layout update.
37538      */
37539     layout : function()
37540     {
37541         if(this.updating) {
37542             return;
37543         }
37544         
37545         // render all the rebions if they have not been done alreayd?
37546         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37547             if(this.regions[region] && !this.regions[region].bodyEl){
37548                 this.regions[region].onRender(this.el)
37549             }
37550         },this);
37551         
37552         var size = this.getViewSize();
37553         var w = size.width;
37554         var h = size.height;
37555         var centerW = w;
37556         var centerH = h;
37557         var centerY = 0;
37558         var centerX = 0;
37559         //var x = 0, y = 0;
37560
37561         var rs = this.regions;
37562         var north = rs["north"];
37563         var south = rs["south"]; 
37564         var west = rs["west"];
37565         var east = rs["east"];
37566         var center = rs["center"];
37567         //if(this.hideOnLayout){ // not supported anymore
37568             //c.el.setStyle("display", "none");
37569         //}
37570         if(north && north.isVisible()){
37571             var b = north.getBox();
37572             var m = north.getMargins();
37573             b.width = w - (m.left+m.right);
37574             b.x = m.left;
37575             b.y = m.top;
37576             centerY = b.height + b.y + m.bottom;
37577             centerH -= centerY;
37578             north.updateBox(this.safeBox(b));
37579         }
37580         if(south && south.isVisible()){
37581             var b = south.getBox();
37582             var m = south.getMargins();
37583             b.width = w - (m.left+m.right);
37584             b.x = m.left;
37585             var totalHeight = (b.height + m.top + m.bottom);
37586             b.y = h - totalHeight + m.top;
37587             centerH -= totalHeight;
37588             south.updateBox(this.safeBox(b));
37589         }
37590         if(west && west.isVisible()){
37591             var b = west.getBox();
37592             var m = west.getMargins();
37593             b.height = centerH - (m.top+m.bottom);
37594             b.x = m.left;
37595             b.y = centerY + m.top;
37596             var totalWidth = (b.width + m.left + m.right);
37597             centerX += totalWidth;
37598             centerW -= totalWidth;
37599             west.updateBox(this.safeBox(b));
37600         }
37601         if(east && east.isVisible()){
37602             var b = east.getBox();
37603             var m = east.getMargins();
37604             b.height = centerH - (m.top+m.bottom);
37605             var totalWidth = (b.width + m.left + m.right);
37606             b.x = w - totalWidth + m.left;
37607             b.y = centerY + m.top;
37608             centerW -= totalWidth;
37609             east.updateBox(this.safeBox(b));
37610         }
37611         if(center){
37612             var m = center.getMargins();
37613             var centerBox = {
37614                 x: centerX + m.left,
37615                 y: centerY + m.top,
37616                 width: centerW - (m.left+m.right),
37617                 height: centerH - (m.top+m.bottom)
37618             };
37619             //if(this.hideOnLayout){
37620                 //center.el.setStyle("display", "block");
37621             //}
37622             center.updateBox(this.safeBox(centerBox));
37623         }
37624         this.el.repaint();
37625         this.fireEvent("layout", this);
37626     },
37627
37628     // private
37629     safeBox : function(box){
37630         box.width = Math.max(0, box.width);
37631         box.height = Math.max(0, box.height);
37632         return box;
37633     },
37634
37635     /**
37636      * Adds a ContentPanel (or subclass) to this layout.
37637      * @param {String} target The target region key (north, south, east, west or center).
37638      * @param {Roo.ContentPanel} panel The panel to add
37639      * @return {Roo.ContentPanel} The added panel
37640      */
37641     add : function(target, panel){
37642          
37643         target = target.toLowerCase();
37644         return this.regions[target].add(panel);
37645     },
37646
37647     /**
37648      * Remove a ContentPanel (or subclass) to this layout.
37649      * @param {String} target The target region key (north, south, east, west or center).
37650      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37651      * @return {Roo.ContentPanel} The removed panel
37652      */
37653     remove : function(target, panel){
37654         target = target.toLowerCase();
37655         return this.regions[target].remove(panel);
37656     },
37657
37658     /**
37659      * Searches all regions for a panel with the specified id
37660      * @param {String} panelId
37661      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37662      */
37663     findPanel : function(panelId){
37664         var rs = this.regions;
37665         for(var target in rs){
37666             if(typeof rs[target] != "function"){
37667                 var p = rs[target].getPanel(panelId);
37668                 if(p){
37669                     return p;
37670                 }
37671             }
37672         }
37673         return null;
37674     },
37675
37676     /**
37677      * Searches all regions for a panel with the specified id and activates (shows) it.
37678      * @param {String/ContentPanel} panelId The panels id or the panel itself
37679      * @return {Roo.ContentPanel} The shown panel or null
37680      */
37681     showPanel : function(panelId) {
37682       var rs = this.regions;
37683       for(var target in rs){
37684          var r = rs[target];
37685          if(typeof r != "function"){
37686             if(r.hasPanel(panelId)){
37687                return r.showPanel(panelId);
37688             }
37689          }
37690       }
37691       return null;
37692    },
37693
37694    /**
37695      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37696      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37697      */
37698    /*
37699     restoreState : function(provider){
37700         if(!provider){
37701             provider = Roo.state.Manager;
37702         }
37703         var sm = new Roo.LayoutStateManager();
37704         sm.init(this, provider);
37705     },
37706 */
37707  
37708  
37709     /**
37710      * Adds a xtype elements to the layout.
37711      * <pre><code>
37712
37713 layout.addxtype({
37714        xtype : 'ContentPanel',
37715        region: 'west',
37716        items: [ .... ]
37717    }
37718 );
37719
37720 layout.addxtype({
37721         xtype : 'NestedLayoutPanel',
37722         region: 'west',
37723         layout: {
37724            center: { },
37725            west: { }   
37726         },
37727         items : [ ... list of content panels or nested layout panels.. ]
37728    }
37729 );
37730 </code></pre>
37731      * @param {Object} cfg Xtype definition of item to add.
37732      */
37733     addxtype : function(cfg)
37734     {
37735         // basically accepts a pannel...
37736         // can accept a layout region..!?!?
37737         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37738         
37739         
37740         // theory?  children can only be panels??
37741         
37742         //if (!cfg.xtype.match(/Panel$/)) {
37743         //    return false;
37744         //}
37745         var ret = false;
37746         
37747         if (typeof(cfg.region) == 'undefined') {
37748             Roo.log("Failed to add Panel, region was not set");
37749             Roo.log(cfg);
37750             return false;
37751         }
37752         var region = cfg.region;
37753         delete cfg.region;
37754         
37755           
37756         var xitems = [];
37757         if (cfg.items) {
37758             xitems = cfg.items;
37759             delete cfg.items;
37760         }
37761         var nb = false;
37762         
37763         if ( region == 'center') {
37764             Roo.log("Center: " + cfg.title);
37765         }
37766         
37767         
37768         switch(cfg.xtype) 
37769         {
37770             case 'Content':  // ContentPanel (el, cfg)
37771             case 'Scroll':  // ContentPanel (el, cfg)
37772             case 'View': 
37773                 cfg.autoCreate = cfg.autoCreate || true;
37774                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37775                 //} else {
37776                 //    var el = this.el.createChild();
37777                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37778                 //}
37779                 
37780                 this.add(region, ret);
37781                 break;
37782             
37783             /*
37784             case 'TreePanel': // our new panel!
37785                 cfg.el = this.el.createChild();
37786                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37787                 this.add(region, ret);
37788                 break;
37789             */
37790             
37791             case 'Nest': 
37792                 // create a new Layout (which is  a Border Layout...
37793                 
37794                 var clayout = cfg.layout;
37795                 clayout.el  = this.el.createChild();
37796                 clayout.items   = clayout.items  || [];
37797                 
37798                 delete cfg.layout;
37799                 
37800                 // replace this exitems with the clayout ones..
37801                 xitems = clayout.items;
37802                  
37803                 // force background off if it's in center...
37804                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37805                     cfg.background = false;
37806                 }
37807                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37808                 
37809                 
37810                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37811                 //console.log('adding nested layout panel '  + cfg.toSource());
37812                 this.add(region, ret);
37813                 nb = {}; /// find first...
37814                 break;
37815             
37816             case 'Grid':
37817                 
37818                 // needs grid and region
37819                 
37820                 //var el = this.getRegion(region).el.createChild();
37821                 /*
37822                  *var el = this.el.createChild();
37823                 // create the grid first...
37824                 cfg.grid.container = el;
37825                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37826                 */
37827                 
37828                 if (region == 'center' && this.active ) {
37829                     cfg.background = false;
37830                 }
37831                 
37832                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37833                 
37834                 this.add(region, ret);
37835                 /*
37836                 if (cfg.background) {
37837                     // render grid on panel activation (if panel background)
37838                     ret.on('activate', function(gp) {
37839                         if (!gp.grid.rendered) {
37840                     //        gp.grid.render(el);
37841                         }
37842                     });
37843                 } else {
37844                   //  cfg.grid.render(el);
37845                 }
37846                 */
37847                 break;
37848            
37849            
37850             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37851                 // it was the old xcomponent building that caused this before.
37852                 // espeically if border is the top element in the tree.
37853                 ret = this;
37854                 break; 
37855                 
37856                     
37857                 
37858                 
37859                 
37860             default:
37861                 /*
37862                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37863                     
37864                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37865                     this.add(region, ret);
37866                 } else {
37867                 */
37868                     Roo.log(cfg);
37869                     throw "Can not add '" + cfg.xtype + "' to Border";
37870                     return null;
37871              
37872                                 
37873              
37874         }
37875         this.beginUpdate();
37876         // add children..
37877         var region = '';
37878         var abn = {};
37879         Roo.each(xitems, function(i)  {
37880             region = nb && i.region ? i.region : false;
37881             
37882             var add = ret.addxtype(i);
37883            
37884             if (region) {
37885                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37886                 if (!i.background) {
37887                     abn[region] = nb[region] ;
37888                 }
37889             }
37890             
37891         });
37892         this.endUpdate();
37893
37894         // make the last non-background panel active..
37895         //if (nb) { Roo.log(abn); }
37896         if (nb) {
37897             
37898             for(var r in abn) {
37899                 region = this.getRegion(r);
37900                 if (region) {
37901                     // tried using nb[r], but it does not work..
37902                      
37903                     region.showPanel(abn[r]);
37904                    
37905                 }
37906             }
37907         }
37908         return ret;
37909         
37910     },
37911     
37912     
37913 // private
37914     factory : function(cfg)
37915     {
37916         
37917         var validRegions = Roo.bootstrap.layout.Border.regions;
37918
37919         var target = cfg.region;
37920         cfg.mgr = this;
37921         
37922         var r = Roo.bootstrap.layout;
37923         Roo.log(target);
37924         switch(target){
37925             case "north":
37926                 return new r.North(cfg);
37927             case "south":
37928                 return new r.South(cfg);
37929             case "east":
37930                 return new r.East(cfg);
37931             case "west":
37932                 return new r.West(cfg);
37933             case "center":
37934                 return new r.Center(cfg);
37935         }
37936         throw 'Layout region "'+target+'" not supported.';
37937     }
37938     
37939     
37940 });
37941  /*
37942  * Based on:
37943  * Ext JS Library 1.1.1
37944  * Copyright(c) 2006-2007, Ext JS, LLC.
37945  *
37946  * Originally Released Under LGPL - original licence link has changed is not relivant.
37947  *
37948  * Fork - LGPL
37949  * <script type="text/javascript">
37950  */
37951  
37952 /**
37953  * @class Roo.bootstrap.layout.Basic
37954  * @extends Roo.util.Observable
37955  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37956  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37957  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37958  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37959  * @cfg {string}   region  the region that it inhabits..
37960  * @cfg {bool}   skipConfig skip config?
37961  * 
37962
37963  */
37964 Roo.bootstrap.layout.Basic = function(config){
37965     
37966     this.mgr = config.mgr;
37967     
37968     this.position = config.region;
37969     
37970     var skipConfig = config.skipConfig;
37971     
37972     this.events = {
37973         /**
37974          * @scope Roo.BasicLayoutRegion
37975          */
37976         
37977         /**
37978          * @event beforeremove
37979          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37980          * @param {Roo.LayoutRegion} this
37981          * @param {Roo.ContentPanel} panel The panel
37982          * @param {Object} e The cancel event object
37983          */
37984         "beforeremove" : true,
37985         /**
37986          * @event invalidated
37987          * Fires when the layout for this region is changed.
37988          * @param {Roo.LayoutRegion} this
37989          */
37990         "invalidated" : true,
37991         /**
37992          * @event visibilitychange
37993          * Fires when this region is shown or hidden 
37994          * @param {Roo.LayoutRegion} this
37995          * @param {Boolean} visibility true or false
37996          */
37997         "visibilitychange" : true,
37998         /**
37999          * @event paneladded
38000          * Fires when a panel is added. 
38001          * @param {Roo.LayoutRegion} this
38002          * @param {Roo.ContentPanel} panel The panel
38003          */
38004         "paneladded" : true,
38005         /**
38006          * @event panelremoved
38007          * Fires when a panel is removed. 
38008          * @param {Roo.LayoutRegion} this
38009          * @param {Roo.ContentPanel} panel The panel
38010          */
38011         "panelremoved" : true,
38012         /**
38013          * @event beforecollapse
38014          * Fires when this region before collapse.
38015          * @param {Roo.LayoutRegion} this
38016          */
38017         "beforecollapse" : true,
38018         /**
38019          * @event collapsed
38020          * Fires when this region is collapsed.
38021          * @param {Roo.LayoutRegion} this
38022          */
38023         "collapsed" : true,
38024         /**
38025          * @event expanded
38026          * Fires when this region is expanded.
38027          * @param {Roo.LayoutRegion} this
38028          */
38029         "expanded" : true,
38030         /**
38031          * @event slideshow
38032          * Fires when this region is slid into view.
38033          * @param {Roo.LayoutRegion} this
38034          */
38035         "slideshow" : true,
38036         /**
38037          * @event slidehide
38038          * Fires when this region slides out of view. 
38039          * @param {Roo.LayoutRegion} this
38040          */
38041         "slidehide" : true,
38042         /**
38043          * @event panelactivated
38044          * Fires when a panel is activated. 
38045          * @param {Roo.LayoutRegion} this
38046          * @param {Roo.ContentPanel} panel The activated panel
38047          */
38048         "panelactivated" : true,
38049         /**
38050          * @event resized
38051          * Fires when the user resizes this region. 
38052          * @param {Roo.LayoutRegion} this
38053          * @param {Number} newSize The new size (width for east/west, height for north/south)
38054          */
38055         "resized" : true
38056     };
38057     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38058     this.panels = new Roo.util.MixedCollection();
38059     this.panels.getKey = this.getPanelId.createDelegate(this);
38060     this.box = null;
38061     this.activePanel = null;
38062     // ensure listeners are added...
38063     
38064     if (config.listeners || config.events) {
38065         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38066             listeners : config.listeners || {},
38067             events : config.events || {}
38068         });
38069     }
38070     
38071     if(skipConfig !== true){
38072         this.applyConfig(config);
38073     }
38074 };
38075
38076 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38077 {
38078     getPanelId : function(p){
38079         return p.getId();
38080     },
38081     
38082     applyConfig : function(config){
38083         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38084         this.config = config;
38085         
38086     },
38087     
38088     /**
38089      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38090      * the width, for horizontal (north, south) the height.
38091      * @param {Number} newSize The new width or height
38092      */
38093     resizeTo : function(newSize){
38094         var el = this.el ? this.el :
38095                  (this.activePanel ? this.activePanel.getEl() : null);
38096         if(el){
38097             switch(this.position){
38098                 case "east":
38099                 case "west":
38100                     el.setWidth(newSize);
38101                     this.fireEvent("resized", this, newSize);
38102                 break;
38103                 case "north":
38104                 case "south":
38105                     el.setHeight(newSize);
38106                     this.fireEvent("resized", this, newSize);
38107                 break;                
38108             }
38109         }
38110     },
38111     
38112     getBox : function(){
38113         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38114     },
38115     
38116     getMargins : function(){
38117         return this.margins;
38118     },
38119     
38120     updateBox : function(box){
38121         this.box = box;
38122         var el = this.activePanel.getEl();
38123         el.dom.style.left = box.x + "px";
38124         el.dom.style.top = box.y + "px";
38125         this.activePanel.setSize(box.width, box.height);
38126     },
38127     
38128     /**
38129      * Returns the container element for this region.
38130      * @return {Roo.Element}
38131      */
38132     getEl : function(){
38133         return this.activePanel;
38134     },
38135     
38136     /**
38137      * Returns true if this region is currently visible.
38138      * @return {Boolean}
38139      */
38140     isVisible : function(){
38141         return this.activePanel ? true : false;
38142     },
38143     
38144     setActivePanel : function(panel){
38145         panel = this.getPanel(panel);
38146         if(this.activePanel && this.activePanel != panel){
38147             this.activePanel.setActiveState(false);
38148             this.activePanel.getEl().setLeftTop(-10000,-10000);
38149         }
38150         this.activePanel = panel;
38151         panel.setActiveState(true);
38152         if(this.box){
38153             panel.setSize(this.box.width, this.box.height);
38154         }
38155         this.fireEvent("panelactivated", this, panel);
38156         this.fireEvent("invalidated");
38157     },
38158     
38159     /**
38160      * Show the specified panel.
38161      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38162      * @return {Roo.ContentPanel} The shown panel or null
38163      */
38164     showPanel : function(panel){
38165         panel = this.getPanel(panel);
38166         if(panel){
38167             this.setActivePanel(panel);
38168         }
38169         return panel;
38170     },
38171     
38172     /**
38173      * Get the active panel for this region.
38174      * @return {Roo.ContentPanel} The active panel or null
38175      */
38176     getActivePanel : function(){
38177         return this.activePanel;
38178     },
38179     
38180     /**
38181      * Add the passed ContentPanel(s)
38182      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38183      * @return {Roo.ContentPanel} The panel added (if only one was added)
38184      */
38185     add : function(panel){
38186         if(arguments.length > 1){
38187             for(var i = 0, len = arguments.length; i < len; i++) {
38188                 this.add(arguments[i]);
38189             }
38190             return null;
38191         }
38192         if(this.hasPanel(panel)){
38193             this.showPanel(panel);
38194             return panel;
38195         }
38196         var el = panel.getEl();
38197         if(el.dom.parentNode != this.mgr.el.dom){
38198             this.mgr.el.dom.appendChild(el.dom);
38199         }
38200         if(panel.setRegion){
38201             panel.setRegion(this);
38202         }
38203         this.panels.add(panel);
38204         el.setStyle("position", "absolute");
38205         if(!panel.background){
38206             this.setActivePanel(panel);
38207             if(this.config.initialSize && this.panels.getCount()==1){
38208                 this.resizeTo(this.config.initialSize);
38209             }
38210         }
38211         this.fireEvent("paneladded", this, panel);
38212         return panel;
38213     },
38214     
38215     /**
38216      * Returns true if the panel is in this region.
38217      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38218      * @return {Boolean}
38219      */
38220     hasPanel : function(panel){
38221         if(typeof panel == "object"){ // must be panel obj
38222             panel = panel.getId();
38223         }
38224         return this.getPanel(panel) ? true : false;
38225     },
38226     
38227     /**
38228      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38229      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38230      * @param {Boolean} preservePanel Overrides the config preservePanel option
38231      * @return {Roo.ContentPanel} The panel that was removed
38232      */
38233     remove : function(panel, preservePanel){
38234         panel = this.getPanel(panel);
38235         if(!panel){
38236             return null;
38237         }
38238         var e = {};
38239         this.fireEvent("beforeremove", this, panel, e);
38240         if(e.cancel === true){
38241             return null;
38242         }
38243         var panelId = panel.getId();
38244         this.panels.removeKey(panelId);
38245         return panel;
38246     },
38247     
38248     /**
38249      * Returns the panel specified or null if it's not in this region.
38250      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38251      * @return {Roo.ContentPanel}
38252      */
38253     getPanel : function(id){
38254         if(typeof id == "object"){ // must be panel obj
38255             return id;
38256         }
38257         return this.panels.get(id);
38258     },
38259     
38260     /**
38261      * Returns this regions position (north/south/east/west/center).
38262      * @return {String} 
38263      */
38264     getPosition: function(){
38265         return this.position;    
38266     }
38267 });/*
38268  * Based on:
38269  * Ext JS Library 1.1.1
38270  * Copyright(c) 2006-2007, Ext JS, LLC.
38271  *
38272  * Originally Released Under LGPL - original licence link has changed is not relivant.
38273  *
38274  * Fork - LGPL
38275  * <script type="text/javascript">
38276  */
38277  
38278 /**
38279  * @class Roo.bootstrap.layout.Region
38280  * @extends Roo.bootstrap.layout.Basic
38281  * This class represents a region in a layout manager.
38282  
38283  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38284  * @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})
38285  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38286  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38287  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38288  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38289  * @cfg {String}    title           The title for the region (overrides panel titles)
38290  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38291  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38292  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38293  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38294  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38295  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38296  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38297  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38298  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38299  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38300
38301  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38302  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38303  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38304  * @cfg {Number}    width           For East/West panels
38305  * @cfg {Number}    height          For North/South panels
38306  * @cfg {Boolean}   split           To show the splitter
38307  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38308  * 
38309  * @cfg {string}   cls             Extra CSS classes to add to region
38310  * 
38311  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38312  * @cfg {string}   region  the region that it inhabits..
38313  *
38314
38315  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38316  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38317
38318  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38319  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38320  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38321  */
38322 Roo.bootstrap.layout.Region = function(config)
38323 {
38324     this.applyConfig(config);
38325
38326     var mgr = config.mgr;
38327     var pos = config.region;
38328     config.skipConfig = true;
38329     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38330     
38331     if (mgr.el) {
38332         this.onRender(mgr.el);   
38333     }
38334      
38335     this.visible = true;
38336     this.collapsed = false;
38337     this.unrendered_panels = [];
38338 };
38339
38340 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38341
38342     position: '', // set by wrapper (eg. north/south etc..)
38343     unrendered_panels : null,  // unrendered panels.
38344     
38345     tabPosition : false,
38346     
38347     mgr: false, // points to 'Border'
38348     
38349     
38350     createBody : function(){
38351         /** This region's body element 
38352         * @type Roo.Element */
38353         this.bodyEl = this.el.createChild({
38354                 tag: "div",
38355                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38356         });
38357     },
38358
38359     onRender: function(ctr, pos)
38360     {
38361         var dh = Roo.DomHelper;
38362         /** This region's container element 
38363         * @type Roo.Element */
38364         this.el = dh.append(ctr.dom, {
38365                 tag: "div",
38366                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38367             }, true);
38368         /** This region's title element 
38369         * @type Roo.Element */
38370     
38371         this.titleEl = dh.append(this.el.dom,  {
38372                 tag: "div",
38373                 unselectable: "on",
38374                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38375                 children:[
38376                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38377                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38378                 ]
38379             }, true);
38380         
38381         this.titleEl.enableDisplayMode();
38382         /** This region's title text element 
38383         * @type HTMLElement */
38384         this.titleTextEl = this.titleEl.dom.firstChild;
38385         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38386         /*
38387         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38388         this.closeBtn.enableDisplayMode();
38389         this.closeBtn.on("click", this.closeClicked, this);
38390         this.closeBtn.hide();
38391     */
38392         this.createBody(this.config);
38393         if(this.config.hideWhenEmpty){
38394             this.hide();
38395             this.on("paneladded", this.validateVisibility, this);
38396             this.on("panelremoved", this.validateVisibility, this);
38397         }
38398         if(this.autoScroll){
38399             this.bodyEl.setStyle("overflow", "auto");
38400         }else{
38401             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38402         }
38403         //if(c.titlebar !== false){
38404             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38405                 this.titleEl.hide();
38406             }else{
38407                 this.titleEl.show();
38408                 if(this.config.title){
38409                     this.titleTextEl.innerHTML = this.config.title;
38410                 }
38411             }
38412         //}
38413         if(this.config.collapsed){
38414             this.collapse(true);
38415         }
38416         if(this.config.hidden){
38417             this.hide();
38418         }
38419         
38420         if (this.unrendered_panels && this.unrendered_panels.length) {
38421             for (var i =0;i< this.unrendered_panels.length; i++) {
38422                 this.add(this.unrendered_panels[i]);
38423             }
38424             this.unrendered_panels = null;
38425             
38426         }
38427         
38428     },
38429     
38430     applyConfig : function(c)
38431     {
38432         /*
38433          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38434             var dh = Roo.DomHelper;
38435             if(c.titlebar !== false){
38436                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38437                 this.collapseBtn.on("click", this.collapse, this);
38438                 this.collapseBtn.enableDisplayMode();
38439                 /*
38440                 if(c.showPin === true || this.showPin){
38441                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38442                     this.stickBtn.enableDisplayMode();
38443                     this.stickBtn.on("click", this.expand, this);
38444                     this.stickBtn.hide();
38445                 }
38446                 
38447             }
38448             */
38449             /** This region's collapsed element
38450             * @type Roo.Element */
38451             /*
38452              *
38453             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38454                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38455             ]}, true);
38456             
38457             if(c.floatable !== false){
38458                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38459                this.collapsedEl.on("click", this.collapseClick, this);
38460             }
38461
38462             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38463                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38464                    id: "message", unselectable: "on", style:{"float":"left"}});
38465                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38466              }
38467             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38468             this.expandBtn.on("click", this.expand, this);
38469             
38470         }
38471         
38472         if(this.collapseBtn){
38473             this.collapseBtn.setVisible(c.collapsible == true);
38474         }
38475         
38476         this.cmargins = c.cmargins || this.cmargins ||
38477                          (this.position == "west" || this.position == "east" ?
38478                              {top: 0, left: 2, right:2, bottom: 0} :
38479                              {top: 2, left: 0, right:0, bottom: 2});
38480         */
38481         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38482         
38483         
38484         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38485         
38486         this.autoScroll = c.autoScroll || false;
38487         
38488         
38489        
38490         
38491         this.duration = c.duration || .30;
38492         this.slideDuration = c.slideDuration || .45;
38493         this.config = c;
38494        
38495     },
38496     /**
38497      * Returns true if this region is currently visible.
38498      * @return {Boolean}
38499      */
38500     isVisible : function(){
38501         return this.visible;
38502     },
38503
38504     /**
38505      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38506      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38507      */
38508     //setCollapsedTitle : function(title){
38509     //    title = title || "&#160;";
38510      //   if(this.collapsedTitleTextEl){
38511       //      this.collapsedTitleTextEl.innerHTML = title;
38512        // }
38513     //},
38514
38515     getBox : function(){
38516         var b;
38517       //  if(!this.collapsed){
38518             b = this.el.getBox(false, true);
38519        // }else{
38520           //  b = this.collapsedEl.getBox(false, true);
38521         //}
38522         return b;
38523     },
38524
38525     getMargins : function(){
38526         return this.margins;
38527         //return this.collapsed ? this.cmargins : this.margins;
38528     },
38529 /*
38530     highlight : function(){
38531         this.el.addClass("x-layout-panel-dragover");
38532     },
38533
38534     unhighlight : function(){
38535         this.el.removeClass("x-layout-panel-dragover");
38536     },
38537 */
38538     updateBox : function(box)
38539     {
38540         if (!this.bodyEl) {
38541             return; // not rendered yet..
38542         }
38543         
38544         this.box = box;
38545         if(!this.collapsed){
38546             this.el.dom.style.left = box.x + "px";
38547             this.el.dom.style.top = box.y + "px";
38548             this.updateBody(box.width, box.height);
38549         }else{
38550             this.collapsedEl.dom.style.left = box.x + "px";
38551             this.collapsedEl.dom.style.top = box.y + "px";
38552             this.collapsedEl.setSize(box.width, box.height);
38553         }
38554         if(this.tabs){
38555             this.tabs.autoSizeTabs();
38556         }
38557     },
38558
38559     updateBody : function(w, h)
38560     {
38561         if(w !== null){
38562             this.el.setWidth(w);
38563             w -= this.el.getBorderWidth("rl");
38564             if(this.config.adjustments){
38565                 w += this.config.adjustments[0];
38566             }
38567         }
38568         if(h !== null && h > 0){
38569             this.el.setHeight(h);
38570             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38571             h -= this.el.getBorderWidth("tb");
38572             if(this.config.adjustments){
38573                 h += this.config.adjustments[1];
38574             }
38575             this.bodyEl.setHeight(h);
38576             if(this.tabs){
38577                 h = this.tabs.syncHeight(h);
38578             }
38579         }
38580         if(this.panelSize){
38581             w = w !== null ? w : this.panelSize.width;
38582             h = h !== null ? h : this.panelSize.height;
38583         }
38584         if(this.activePanel){
38585             var el = this.activePanel.getEl();
38586             w = w !== null ? w : el.getWidth();
38587             h = h !== null ? h : el.getHeight();
38588             this.panelSize = {width: w, height: h};
38589             this.activePanel.setSize(w, h);
38590         }
38591         if(Roo.isIE && this.tabs){
38592             this.tabs.el.repaint();
38593         }
38594     },
38595
38596     /**
38597      * Returns the container element for this region.
38598      * @return {Roo.Element}
38599      */
38600     getEl : function(){
38601         return this.el;
38602     },
38603
38604     /**
38605      * Hides this region.
38606      */
38607     hide : function(){
38608         //if(!this.collapsed){
38609             this.el.dom.style.left = "-2000px";
38610             this.el.hide();
38611         //}else{
38612          //   this.collapsedEl.dom.style.left = "-2000px";
38613          //   this.collapsedEl.hide();
38614        // }
38615         this.visible = false;
38616         this.fireEvent("visibilitychange", this, false);
38617     },
38618
38619     /**
38620      * Shows this region if it was previously hidden.
38621      */
38622     show : function(){
38623         //if(!this.collapsed){
38624             this.el.show();
38625         //}else{
38626         //    this.collapsedEl.show();
38627        // }
38628         this.visible = true;
38629         this.fireEvent("visibilitychange", this, true);
38630     },
38631 /*
38632     closeClicked : function(){
38633         if(this.activePanel){
38634             this.remove(this.activePanel);
38635         }
38636     },
38637
38638     collapseClick : function(e){
38639         if(this.isSlid){
38640            e.stopPropagation();
38641            this.slideIn();
38642         }else{
38643            e.stopPropagation();
38644            this.slideOut();
38645         }
38646     },
38647 */
38648     /**
38649      * Collapses this region.
38650      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38651      */
38652     /*
38653     collapse : function(skipAnim, skipCheck = false){
38654         if(this.collapsed) {
38655             return;
38656         }
38657         
38658         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38659             
38660             this.collapsed = true;
38661             if(this.split){
38662                 this.split.el.hide();
38663             }
38664             if(this.config.animate && skipAnim !== true){
38665                 this.fireEvent("invalidated", this);
38666                 this.animateCollapse();
38667             }else{
38668                 this.el.setLocation(-20000,-20000);
38669                 this.el.hide();
38670                 this.collapsedEl.show();
38671                 this.fireEvent("collapsed", this);
38672                 this.fireEvent("invalidated", this);
38673             }
38674         }
38675         
38676     },
38677 */
38678     animateCollapse : function(){
38679         // overridden
38680     },
38681
38682     /**
38683      * Expands this region if it was previously collapsed.
38684      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38685      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38686      */
38687     /*
38688     expand : function(e, skipAnim){
38689         if(e) {
38690             e.stopPropagation();
38691         }
38692         if(!this.collapsed || this.el.hasActiveFx()) {
38693             return;
38694         }
38695         if(this.isSlid){
38696             this.afterSlideIn();
38697             skipAnim = true;
38698         }
38699         this.collapsed = false;
38700         if(this.config.animate && skipAnim !== true){
38701             this.animateExpand();
38702         }else{
38703             this.el.show();
38704             if(this.split){
38705                 this.split.el.show();
38706             }
38707             this.collapsedEl.setLocation(-2000,-2000);
38708             this.collapsedEl.hide();
38709             this.fireEvent("invalidated", this);
38710             this.fireEvent("expanded", this);
38711         }
38712     },
38713 */
38714     animateExpand : function(){
38715         // overridden
38716     },
38717
38718     initTabs : function()
38719     {
38720         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38721         
38722         var ts = new Roo.bootstrap.panel.Tabs({
38723             el: this.bodyEl.dom,
38724             region : this,
38725             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38726             disableTooltips: this.config.disableTabTips,
38727             toolbar : this.config.toolbar
38728         });
38729         
38730         if(this.config.hideTabs){
38731             ts.stripWrap.setDisplayed(false);
38732         }
38733         this.tabs = ts;
38734         ts.resizeTabs = this.config.resizeTabs === true;
38735         ts.minTabWidth = this.config.minTabWidth || 40;
38736         ts.maxTabWidth = this.config.maxTabWidth || 250;
38737         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38738         ts.monitorResize = false;
38739         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38740         ts.bodyEl.addClass('roo-layout-tabs-body');
38741         this.panels.each(this.initPanelAsTab, this);
38742     },
38743
38744     initPanelAsTab : function(panel){
38745         var ti = this.tabs.addTab(
38746             panel.getEl().id,
38747             panel.getTitle(),
38748             null,
38749             this.config.closeOnTab && panel.isClosable(),
38750             panel.tpl
38751         );
38752         if(panel.tabTip !== undefined){
38753             ti.setTooltip(panel.tabTip);
38754         }
38755         ti.on("activate", function(){
38756               this.setActivePanel(panel);
38757         }, this);
38758         
38759         if(this.config.closeOnTab){
38760             ti.on("beforeclose", function(t, e){
38761                 e.cancel = true;
38762                 this.remove(panel);
38763             }, this);
38764         }
38765         
38766         panel.tabItem = ti;
38767         
38768         return ti;
38769     },
38770
38771     updatePanelTitle : function(panel, title)
38772     {
38773         if(this.activePanel == panel){
38774             this.updateTitle(title);
38775         }
38776         if(this.tabs){
38777             var ti = this.tabs.getTab(panel.getEl().id);
38778             ti.setText(title);
38779             if(panel.tabTip !== undefined){
38780                 ti.setTooltip(panel.tabTip);
38781             }
38782         }
38783     },
38784
38785     updateTitle : function(title){
38786         if(this.titleTextEl && !this.config.title){
38787             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38788         }
38789     },
38790
38791     setActivePanel : function(panel)
38792     {
38793         panel = this.getPanel(panel);
38794         if(this.activePanel && this.activePanel != panel){
38795             if(this.activePanel.setActiveState(false) === false){
38796                 return;
38797             }
38798         }
38799         this.activePanel = panel;
38800         panel.setActiveState(true);
38801         if(this.panelSize){
38802             panel.setSize(this.panelSize.width, this.panelSize.height);
38803         }
38804         if(this.closeBtn){
38805             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38806         }
38807         this.updateTitle(panel.getTitle());
38808         if(this.tabs){
38809             this.fireEvent("invalidated", this);
38810         }
38811         this.fireEvent("panelactivated", this, panel);
38812     },
38813
38814     /**
38815      * Shows the specified panel.
38816      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38817      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38818      */
38819     showPanel : function(panel)
38820     {
38821         panel = this.getPanel(panel);
38822         if(panel){
38823             if(this.tabs){
38824                 var tab = this.tabs.getTab(panel.getEl().id);
38825                 if(tab.isHidden()){
38826                     this.tabs.unhideTab(tab.id);
38827                 }
38828                 tab.activate();
38829             }else{
38830                 this.setActivePanel(panel);
38831             }
38832         }
38833         return panel;
38834     },
38835
38836     /**
38837      * Get the active panel for this region.
38838      * @return {Roo.ContentPanel} The active panel or null
38839      */
38840     getActivePanel : function(){
38841         return this.activePanel;
38842     },
38843
38844     validateVisibility : function(){
38845         if(this.panels.getCount() < 1){
38846             this.updateTitle("&#160;");
38847             this.closeBtn.hide();
38848             this.hide();
38849         }else{
38850             if(!this.isVisible()){
38851                 this.show();
38852             }
38853         }
38854     },
38855
38856     /**
38857      * Adds the passed ContentPanel(s) to this region.
38858      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38859      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38860      */
38861     add : function(panel)
38862     {
38863         if(arguments.length > 1){
38864             for(var i = 0, len = arguments.length; i < len; i++) {
38865                 this.add(arguments[i]);
38866             }
38867             return null;
38868         }
38869         
38870         // if we have not been rendered yet, then we can not really do much of this..
38871         if (!this.bodyEl) {
38872             this.unrendered_panels.push(panel);
38873             return panel;
38874         }
38875         
38876         
38877         
38878         
38879         if(this.hasPanel(panel)){
38880             this.showPanel(panel);
38881             return panel;
38882         }
38883         panel.setRegion(this);
38884         this.panels.add(panel);
38885        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38886             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38887             // and hide them... ???
38888             this.bodyEl.dom.appendChild(panel.getEl().dom);
38889             if(panel.background !== true){
38890                 this.setActivePanel(panel);
38891             }
38892             this.fireEvent("paneladded", this, panel);
38893             return panel;
38894         }
38895         */
38896         if(!this.tabs){
38897             this.initTabs();
38898         }else{
38899             this.initPanelAsTab(panel);
38900         }
38901         
38902         
38903         if(panel.background !== true){
38904             this.tabs.activate(panel.getEl().id);
38905         }
38906         this.fireEvent("paneladded", this, panel);
38907         return panel;
38908     },
38909
38910     /**
38911      * Hides the tab for the specified panel.
38912      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38913      */
38914     hidePanel : function(panel){
38915         if(this.tabs && (panel = this.getPanel(panel))){
38916             this.tabs.hideTab(panel.getEl().id);
38917         }
38918     },
38919
38920     /**
38921      * Unhides the tab for a previously hidden panel.
38922      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38923      */
38924     unhidePanel : function(panel){
38925         if(this.tabs && (panel = this.getPanel(panel))){
38926             this.tabs.unhideTab(panel.getEl().id);
38927         }
38928     },
38929
38930     clearPanels : function(){
38931         while(this.panels.getCount() > 0){
38932              this.remove(this.panels.first());
38933         }
38934     },
38935
38936     /**
38937      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38938      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38939      * @param {Boolean} preservePanel Overrides the config preservePanel option
38940      * @return {Roo.ContentPanel} The panel that was removed
38941      */
38942     remove : function(panel, preservePanel)
38943     {
38944         panel = this.getPanel(panel);
38945         if(!panel){
38946             return null;
38947         }
38948         var e = {};
38949         this.fireEvent("beforeremove", this, panel, e);
38950         if(e.cancel === true){
38951             return null;
38952         }
38953         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38954         var panelId = panel.getId();
38955         this.panels.removeKey(panelId);
38956         if(preservePanel){
38957             document.body.appendChild(panel.getEl().dom);
38958         }
38959         if(this.tabs){
38960             this.tabs.removeTab(panel.getEl().id);
38961         }else if (!preservePanel){
38962             this.bodyEl.dom.removeChild(panel.getEl().dom);
38963         }
38964         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38965             var p = this.panels.first();
38966             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38967             tempEl.appendChild(p.getEl().dom);
38968             this.bodyEl.update("");
38969             this.bodyEl.dom.appendChild(p.getEl().dom);
38970             tempEl = null;
38971             this.updateTitle(p.getTitle());
38972             this.tabs = null;
38973             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38974             this.setActivePanel(p);
38975         }
38976         panel.setRegion(null);
38977         if(this.activePanel == panel){
38978             this.activePanel = null;
38979         }
38980         if(this.config.autoDestroy !== false && preservePanel !== true){
38981             try{panel.destroy();}catch(e){}
38982         }
38983         this.fireEvent("panelremoved", this, panel);
38984         return panel;
38985     },
38986
38987     /**
38988      * Returns the TabPanel component used by this region
38989      * @return {Roo.TabPanel}
38990      */
38991     getTabs : function(){
38992         return this.tabs;
38993     },
38994
38995     createTool : function(parentEl, className){
38996         var btn = Roo.DomHelper.append(parentEl, {
38997             tag: "div",
38998             cls: "x-layout-tools-button",
38999             children: [ {
39000                 tag: "div",
39001                 cls: "roo-layout-tools-button-inner " + className,
39002                 html: "&#160;"
39003             }]
39004         }, true);
39005         btn.addClassOnOver("roo-layout-tools-button-over");
39006         return btn;
39007     }
39008 });/*
39009  * Based on:
39010  * Ext JS Library 1.1.1
39011  * Copyright(c) 2006-2007, Ext JS, LLC.
39012  *
39013  * Originally Released Under LGPL - original licence link has changed is not relivant.
39014  *
39015  * Fork - LGPL
39016  * <script type="text/javascript">
39017  */
39018  
39019
39020
39021 /**
39022  * @class Roo.SplitLayoutRegion
39023  * @extends Roo.LayoutRegion
39024  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39025  */
39026 Roo.bootstrap.layout.Split = function(config){
39027     this.cursor = config.cursor;
39028     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39029 };
39030
39031 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39032 {
39033     splitTip : "Drag to resize.",
39034     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39035     useSplitTips : false,
39036
39037     applyConfig : function(config){
39038         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39039     },
39040     
39041     onRender : function(ctr,pos) {
39042         
39043         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39044         if(!this.config.split){
39045             return;
39046         }
39047         if(!this.split){
39048             
39049             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39050                             tag: "div",
39051                             id: this.el.id + "-split",
39052                             cls: "roo-layout-split roo-layout-split-"+this.position,
39053                             html: "&#160;"
39054             });
39055             /** The SplitBar for this region 
39056             * @type Roo.SplitBar */
39057             // does not exist yet...
39058             Roo.log([this.position, this.orientation]);
39059             
39060             this.split = new Roo.bootstrap.SplitBar({
39061                 dragElement : splitEl,
39062                 resizingElement: this.el,
39063                 orientation : this.orientation
39064             });
39065             
39066             this.split.on("moved", this.onSplitMove, this);
39067             this.split.useShim = this.config.useShim === true;
39068             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39069             if(this.useSplitTips){
39070                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39071             }
39072             //if(config.collapsible){
39073             //    this.split.el.on("dblclick", this.collapse,  this);
39074             //}
39075         }
39076         if(typeof this.config.minSize != "undefined"){
39077             this.split.minSize = this.config.minSize;
39078         }
39079         if(typeof this.config.maxSize != "undefined"){
39080             this.split.maxSize = this.config.maxSize;
39081         }
39082         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39083             this.hideSplitter();
39084         }
39085         
39086     },
39087
39088     getHMaxSize : function(){
39089          var cmax = this.config.maxSize || 10000;
39090          var center = this.mgr.getRegion("center");
39091          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39092     },
39093
39094     getVMaxSize : function(){
39095          var cmax = this.config.maxSize || 10000;
39096          var center = this.mgr.getRegion("center");
39097          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39098     },
39099
39100     onSplitMove : function(split, newSize){
39101         this.fireEvent("resized", this, newSize);
39102     },
39103     
39104     /** 
39105      * Returns the {@link Roo.SplitBar} for this region.
39106      * @return {Roo.SplitBar}
39107      */
39108     getSplitBar : function(){
39109         return this.split;
39110     },
39111     
39112     hide : function(){
39113         this.hideSplitter();
39114         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39115     },
39116
39117     hideSplitter : function(){
39118         if(this.split){
39119             this.split.el.setLocation(-2000,-2000);
39120             this.split.el.hide();
39121         }
39122     },
39123
39124     show : function(){
39125         if(this.split){
39126             this.split.el.show();
39127         }
39128         Roo.bootstrap.layout.Split.superclass.show.call(this);
39129     },
39130     
39131     beforeSlide: function(){
39132         if(Roo.isGecko){// firefox overflow auto bug workaround
39133             this.bodyEl.clip();
39134             if(this.tabs) {
39135                 this.tabs.bodyEl.clip();
39136             }
39137             if(this.activePanel){
39138                 this.activePanel.getEl().clip();
39139                 
39140                 if(this.activePanel.beforeSlide){
39141                     this.activePanel.beforeSlide();
39142                 }
39143             }
39144         }
39145     },
39146     
39147     afterSlide : function(){
39148         if(Roo.isGecko){// firefox overflow auto bug workaround
39149             this.bodyEl.unclip();
39150             if(this.tabs) {
39151                 this.tabs.bodyEl.unclip();
39152             }
39153             if(this.activePanel){
39154                 this.activePanel.getEl().unclip();
39155                 if(this.activePanel.afterSlide){
39156                     this.activePanel.afterSlide();
39157                 }
39158             }
39159         }
39160     },
39161
39162     initAutoHide : function(){
39163         if(this.autoHide !== false){
39164             if(!this.autoHideHd){
39165                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39166                 this.autoHideHd = {
39167                     "mouseout": function(e){
39168                         if(!e.within(this.el, true)){
39169                             st.delay(500);
39170                         }
39171                     },
39172                     "mouseover" : function(e){
39173                         st.cancel();
39174                     },
39175                     scope : this
39176                 };
39177             }
39178             this.el.on(this.autoHideHd);
39179         }
39180     },
39181
39182     clearAutoHide : function(){
39183         if(this.autoHide !== false){
39184             this.el.un("mouseout", this.autoHideHd.mouseout);
39185             this.el.un("mouseover", this.autoHideHd.mouseover);
39186         }
39187     },
39188
39189     clearMonitor : function(){
39190         Roo.get(document).un("click", this.slideInIf, this);
39191     },
39192
39193     // these names are backwards but not changed for compat
39194     slideOut : function(){
39195         if(this.isSlid || this.el.hasActiveFx()){
39196             return;
39197         }
39198         this.isSlid = true;
39199         if(this.collapseBtn){
39200             this.collapseBtn.hide();
39201         }
39202         this.closeBtnState = this.closeBtn.getStyle('display');
39203         this.closeBtn.hide();
39204         if(this.stickBtn){
39205             this.stickBtn.show();
39206         }
39207         this.el.show();
39208         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39209         this.beforeSlide();
39210         this.el.setStyle("z-index", 10001);
39211         this.el.slideIn(this.getSlideAnchor(), {
39212             callback: function(){
39213                 this.afterSlide();
39214                 this.initAutoHide();
39215                 Roo.get(document).on("click", this.slideInIf, this);
39216                 this.fireEvent("slideshow", this);
39217             },
39218             scope: this,
39219             block: true
39220         });
39221     },
39222
39223     afterSlideIn : function(){
39224         this.clearAutoHide();
39225         this.isSlid = false;
39226         this.clearMonitor();
39227         this.el.setStyle("z-index", "");
39228         if(this.collapseBtn){
39229             this.collapseBtn.show();
39230         }
39231         this.closeBtn.setStyle('display', this.closeBtnState);
39232         if(this.stickBtn){
39233             this.stickBtn.hide();
39234         }
39235         this.fireEvent("slidehide", this);
39236     },
39237
39238     slideIn : function(cb){
39239         if(!this.isSlid || this.el.hasActiveFx()){
39240             Roo.callback(cb);
39241             return;
39242         }
39243         this.isSlid = false;
39244         this.beforeSlide();
39245         this.el.slideOut(this.getSlideAnchor(), {
39246             callback: function(){
39247                 this.el.setLeftTop(-10000, -10000);
39248                 this.afterSlide();
39249                 this.afterSlideIn();
39250                 Roo.callback(cb);
39251             },
39252             scope: this,
39253             block: true
39254         });
39255     },
39256     
39257     slideInIf : function(e){
39258         if(!e.within(this.el)){
39259             this.slideIn();
39260         }
39261     },
39262
39263     animateCollapse : function(){
39264         this.beforeSlide();
39265         this.el.setStyle("z-index", 20000);
39266         var anchor = this.getSlideAnchor();
39267         this.el.slideOut(anchor, {
39268             callback : function(){
39269                 this.el.setStyle("z-index", "");
39270                 this.collapsedEl.slideIn(anchor, {duration:.3});
39271                 this.afterSlide();
39272                 this.el.setLocation(-10000,-10000);
39273                 this.el.hide();
39274                 this.fireEvent("collapsed", this);
39275             },
39276             scope: this,
39277             block: true
39278         });
39279     },
39280
39281     animateExpand : function(){
39282         this.beforeSlide();
39283         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39284         this.el.setStyle("z-index", 20000);
39285         this.collapsedEl.hide({
39286             duration:.1
39287         });
39288         this.el.slideIn(this.getSlideAnchor(), {
39289             callback : function(){
39290                 this.el.setStyle("z-index", "");
39291                 this.afterSlide();
39292                 if(this.split){
39293                     this.split.el.show();
39294                 }
39295                 this.fireEvent("invalidated", this);
39296                 this.fireEvent("expanded", this);
39297             },
39298             scope: this,
39299             block: true
39300         });
39301     },
39302
39303     anchors : {
39304         "west" : "left",
39305         "east" : "right",
39306         "north" : "top",
39307         "south" : "bottom"
39308     },
39309
39310     sanchors : {
39311         "west" : "l",
39312         "east" : "r",
39313         "north" : "t",
39314         "south" : "b"
39315     },
39316
39317     canchors : {
39318         "west" : "tl-tr",
39319         "east" : "tr-tl",
39320         "north" : "tl-bl",
39321         "south" : "bl-tl"
39322     },
39323
39324     getAnchor : function(){
39325         return this.anchors[this.position];
39326     },
39327
39328     getCollapseAnchor : function(){
39329         return this.canchors[this.position];
39330     },
39331
39332     getSlideAnchor : function(){
39333         return this.sanchors[this.position];
39334     },
39335
39336     getAlignAdj : function(){
39337         var cm = this.cmargins;
39338         switch(this.position){
39339             case "west":
39340                 return [0, 0];
39341             break;
39342             case "east":
39343                 return [0, 0];
39344             break;
39345             case "north":
39346                 return [0, 0];
39347             break;
39348             case "south":
39349                 return [0, 0];
39350             break;
39351         }
39352     },
39353
39354     getExpandAdj : function(){
39355         var c = this.collapsedEl, cm = this.cmargins;
39356         switch(this.position){
39357             case "west":
39358                 return [-(cm.right+c.getWidth()+cm.left), 0];
39359             break;
39360             case "east":
39361                 return [cm.right+c.getWidth()+cm.left, 0];
39362             break;
39363             case "north":
39364                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39365             break;
39366             case "south":
39367                 return [0, cm.top+cm.bottom+c.getHeight()];
39368             break;
39369         }
39370     }
39371 });/*
39372  * Based on:
39373  * Ext JS Library 1.1.1
39374  * Copyright(c) 2006-2007, Ext JS, LLC.
39375  *
39376  * Originally Released Under LGPL - original licence link has changed is not relivant.
39377  *
39378  * Fork - LGPL
39379  * <script type="text/javascript">
39380  */
39381 /*
39382  * These classes are private internal classes
39383  */
39384 Roo.bootstrap.layout.Center = function(config){
39385     config.region = "center";
39386     Roo.bootstrap.layout.Region.call(this, config);
39387     this.visible = true;
39388     this.minWidth = config.minWidth || 20;
39389     this.minHeight = config.minHeight || 20;
39390 };
39391
39392 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39393     hide : function(){
39394         // center panel can't be hidden
39395     },
39396     
39397     show : function(){
39398         // center panel can't be hidden
39399     },
39400     
39401     getMinWidth: function(){
39402         return this.minWidth;
39403     },
39404     
39405     getMinHeight: function(){
39406         return this.minHeight;
39407     }
39408 });
39409
39410
39411
39412
39413  
39414
39415
39416
39417
39418
39419
39420 Roo.bootstrap.layout.North = function(config)
39421 {
39422     config.region = 'north';
39423     config.cursor = 'n-resize';
39424     
39425     Roo.bootstrap.layout.Split.call(this, config);
39426     
39427     
39428     if(this.split){
39429         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39430         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39431         this.split.el.addClass("roo-layout-split-v");
39432     }
39433     //var size = config.initialSize || config.height;
39434     //if(this.el && typeof size != "undefined"){
39435     //    this.el.setHeight(size);
39436     //}
39437 };
39438 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39439 {
39440     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39441      
39442      
39443     onRender : function(ctr, pos)
39444     {
39445         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39446         var size = this.config.initialSize || this.config.height;
39447         if(this.el && typeof size != "undefined"){
39448             this.el.setHeight(size);
39449         }
39450     
39451     },
39452     
39453     getBox : function(){
39454         if(this.collapsed){
39455             return this.collapsedEl.getBox();
39456         }
39457         var box = this.el.getBox();
39458         if(this.split){
39459             box.height += this.split.el.getHeight();
39460         }
39461         return box;
39462     },
39463     
39464     updateBox : function(box){
39465         if(this.split && !this.collapsed){
39466             box.height -= this.split.el.getHeight();
39467             this.split.el.setLeft(box.x);
39468             this.split.el.setTop(box.y+box.height);
39469             this.split.el.setWidth(box.width);
39470         }
39471         if(this.collapsed){
39472             this.updateBody(box.width, null);
39473         }
39474         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39475     }
39476 });
39477
39478
39479
39480
39481
39482 Roo.bootstrap.layout.South = function(config){
39483     config.region = 'south';
39484     config.cursor = 's-resize';
39485     Roo.bootstrap.layout.Split.call(this, config);
39486     if(this.split){
39487         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39488         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39489         this.split.el.addClass("roo-layout-split-v");
39490     }
39491     
39492 };
39493
39494 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39495     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39496     
39497     onRender : function(ctr, pos)
39498     {
39499         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39500         var size = this.config.initialSize || this.config.height;
39501         if(this.el && typeof size != "undefined"){
39502             this.el.setHeight(size);
39503         }
39504     
39505     },
39506     
39507     getBox : function(){
39508         if(this.collapsed){
39509             return this.collapsedEl.getBox();
39510         }
39511         var box = this.el.getBox();
39512         if(this.split){
39513             var sh = this.split.el.getHeight();
39514             box.height += sh;
39515             box.y -= sh;
39516         }
39517         return box;
39518     },
39519     
39520     updateBox : function(box){
39521         if(this.split && !this.collapsed){
39522             var sh = this.split.el.getHeight();
39523             box.height -= sh;
39524             box.y += sh;
39525             this.split.el.setLeft(box.x);
39526             this.split.el.setTop(box.y-sh);
39527             this.split.el.setWidth(box.width);
39528         }
39529         if(this.collapsed){
39530             this.updateBody(box.width, null);
39531         }
39532         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39533     }
39534 });
39535
39536 Roo.bootstrap.layout.East = function(config){
39537     config.region = "east";
39538     config.cursor = "e-resize";
39539     Roo.bootstrap.layout.Split.call(this, config);
39540     if(this.split){
39541         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39542         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39543         this.split.el.addClass("roo-layout-split-h");
39544     }
39545     
39546 };
39547 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39548     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39549     
39550     onRender : function(ctr, pos)
39551     {
39552         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39553         var size = this.config.initialSize || this.config.width;
39554         if(this.el && typeof size != "undefined"){
39555             this.el.setWidth(size);
39556         }
39557     
39558     },
39559     
39560     getBox : function(){
39561         if(this.collapsed){
39562             return this.collapsedEl.getBox();
39563         }
39564         var box = this.el.getBox();
39565         if(this.split){
39566             var sw = this.split.el.getWidth();
39567             box.width += sw;
39568             box.x -= sw;
39569         }
39570         return box;
39571     },
39572
39573     updateBox : function(box){
39574         if(this.split && !this.collapsed){
39575             var sw = this.split.el.getWidth();
39576             box.width -= sw;
39577             this.split.el.setLeft(box.x);
39578             this.split.el.setTop(box.y);
39579             this.split.el.setHeight(box.height);
39580             box.x += sw;
39581         }
39582         if(this.collapsed){
39583             this.updateBody(null, box.height);
39584         }
39585         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39586     }
39587 });
39588
39589 Roo.bootstrap.layout.West = function(config){
39590     config.region = "west";
39591     config.cursor = "w-resize";
39592     
39593     Roo.bootstrap.layout.Split.call(this, config);
39594     if(this.split){
39595         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39596         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39597         this.split.el.addClass("roo-layout-split-h");
39598     }
39599     
39600 };
39601 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39602     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39603     
39604     onRender: function(ctr, pos)
39605     {
39606         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39607         var size = this.config.initialSize || this.config.width;
39608         if(typeof size != "undefined"){
39609             this.el.setWidth(size);
39610         }
39611     },
39612     
39613     getBox : function(){
39614         if(this.collapsed){
39615             return this.collapsedEl.getBox();
39616         }
39617         var box = this.el.getBox();
39618         if (box.width == 0) {
39619             box.width = this.config.width; // kludge?
39620         }
39621         if(this.split){
39622             box.width += this.split.el.getWidth();
39623         }
39624         return box;
39625     },
39626     
39627     updateBox : function(box){
39628         if(this.split && !this.collapsed){
39629             var sw = this.split.el.getWidth();
39630             box.width -= sw;
39631             this.split.el.setLeft(box.x+box.width);
39632             this.split.el.setTop(box.y);
39633             this.split.el.setHeight(box.height);
39634         }
39635         if(this.collapsed){
39636             this.updateBody(null, box.height);
39637         }
39638         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39639     }
39640 });Roo.namespace("Roo.bootstrap.panel");/*
39641  * Based on:
39642  * Ext JS Library 1.1.1
39643  * Copyright(c) 2006-2007, Ext JS, LLC.
39644  *
39645  * Originally Released Under LGPL - original licence link has changed is not relivant.
39646  *
39647  * Fork - LGPL
39648  * <script type="text/javascript">
39649  */
39650 /**
39651  * @class Roo.ContentPanel
39652  * @extends Roo.util.Observable
39653  * A basic ContentPanel element.
39654  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39655  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39656  * @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
39657  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39658  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39659  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39660  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39661  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39662  * @cfg {String} title          The title for this panel
39663  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39664  * @cfg {String} url            Calls {@link #setUrl} with this value
39665  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39666  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39667  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39668  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39669  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39670  * @cfg {Boolean} badges render the badges
39671  * @cfg {String} cls  extra classes to use  
39672  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39673
39674  * @constructor
39675  * Create a new ContentPanel.
39676  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39677  * @param {String/Object} config A string to set only the title or a config object
39678  * @param {String} content (optional) Set the HTML content for this panel
39679  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39680  */
39681 Roo.bootstrap.panel.Content = function( config){
39682     
39683     this.tpl = config.tpl || false;
39684     
39685     var el = config.el;
39686     var content = config.content;
39687
39688     if(config.autoCreate){ // xtype is available if this is called from factory
39689         el = Roo.id();
39690     }
39691     this.el = Roo.get(el);
39692     if(!this.el && config && config.autoCreate){
39693         if(typeof config.autoCreate == "object"){
39694             if(!config.autoCreate.id){
39695                 config.autoCreate.id = config.id||el;
39696             }
39697             this.el = Roo.DomHelper.append(document.body,
39698                         config.autoCreate, true);
39699         }else{
39700             var elcfg =  {
39701                 tag: "div",
39702                 cls: (config.cls || '') +
39703                     (config.background ? ' bg-' + config.background : '') +
39704                     " roo-layout-inactive-content",
39705                 id: config.id||el
39706             };
39707             if (config.iframe) {
39708                 elcfg.cn = [
39709                     {
39710                         tag : 'iframe',
39711                         style : 'border: 0px',
39712                         src : 'about:blank'
39713                     }
39714                 ];
39715             }
39716               
39717             if (config.html) {
39718                 elcfg.html = config.html;
39719                 
39720             }
39721                         
39722             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39723             if (config.iframe) {
39724                 this.iframeEl = this.el.select('iframe',true).first();
39725             }
39726             
39727         }
39728     } 
39729     this.closable = false;
39730     this.loaded = false;
39731     this.active = false;
39732    
39733       
39734     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39735         
39736         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39737         
39738         this.wrapEl = this.el; //this.el.wrap();
39739         var ti = [];
39740         if (config.toolbar.items) {
39741             ti = config.toolbar.items ;
39742             delete config.toolbar.items ;
39743         }
39744         
39745         var nitems = [];
39746         this.toolbar.render(this.wrapEl, 'before');
39747         for(var i =0;i < ti.length;i++) {
39748           //  Roo.log(['add child', items[i]]);
39749             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39750         }
39751         this.toolbar.items = nitems;
39752         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39753         delete config.toolbar;
39754         
39755     }
39756     /*
39757     // xtype created footer. - not sure if will work as we normally have to render first..
39758     if (this.footer && !this.footer.el && this.footer.xtype) {
39759         if (!this.wrapEl) {
39760             this.wrapEl = this.el.wrap();
39761         }
39762     
39763         this.footer.container = this.wrapEl.createChild();
39764          
39765         this.footer = Roo.factory(this.footer, Roo);
39766         
39767     }
39768     */
39769     
39770      if(typeof config == "string"){
39771         this.title = config;
39772     }else{
39773         Roo.apply(this, config);
39774     }
39775     
39776     if(this.resizeEl){
39777         this.resizeEl = Roo.get(this.resizeEl, true);
39778     }else{
39779         this.resizeEl = this.el;
39780     }
39781     // handle view.xtype
39782     
39783  
39784     
39785     
39786     this.addEvents({
39787         /**
39788          * @event activate
39789          * Fires when this panel is activated. 
39790          * @param {Roo.ContentPanel} this
39791          */
39792         "activate" : true,
39793         /**
39794          * @event deactivate
39795          * Fires when this panel is activated. 
39796          * @param {Roo.ContentPanel} this
39797          */
39798         "deactivate" : true,
39799
39800         /**
39801          * @event resize
39802          * Fires when this panel is resized if fitToFrame is true.
39803          * @param {Roo.ContentPanel} this
39804          * @param {Number} width The width after any component adjustments
39805          * @param {Number} height The height after any component adjustments
39806          */
39807         "resize" : true,
39808         
39809          /**
39810          * @event render
39811          * Fires when this tab is created
39812          * @param {Roo.ContentPanel} this
39813          */
39814         "render" : true
39815         
39816         
39817         
39818     });
39819     
39820
39821     
39822     
39823     if(this.autoScroll && !this.iframe){
39824         this.resizeEl.setStyle("overflow", "auto");
39825     } else {
39826         // fix randome scrolling
39827         //this.el.on('scroll', function() {
39828         //    Roo.log('fix random scolling');
39829         //    this.scrollTo('top',0); 
39830         //});
39831     }
39832     content = content || this.content;
39833     if(content){
39834         this.setContent(content);
39835     }
39836     if(config && config.url){
39837         this.setUrl(this.url, this.params, this.loadOnce);
39838     }
39839     
39840     
39841     
39842     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39843     
39844     if (this.view && typeof(this.view.xtype) != 'undefined') {
39845         this.view.el = this.el.appendChild(document.createElement("div"));
39846         this.view = Roo.factory(this.view); 
39847         this.view.render  &&  this.view.render(false, '');  
39848     }
39849     
39850     
39851     this.fireEvent('render', this);
39852 };
39853
39854 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39855     
39856     cls : '',
39857     background : '',
39858     
39859     tabTip : '',
39860     
39861     iframe : false,
39862     iframeEl : false,
39863     
39864     setRegion : function(region){
39865         this.region = region;
39866         this.setActiveClass(region && !this.background);
39867     },
39868     
39869     
39870     setActiveClass: function(state)
39871     {
39872         if(state){
39873            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39874            this.el.setStyle('position','relative');
39875         }else{
39876            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39877            this.el.setStyle('position', 'absolute');
39878         } 
39879     },
39880     
39881     /**
39882      * Returns the toolbar for this Panel if one was configured. 
39883      * @return {Roo.Toolbar} 
39884      */
39885     getToolbar : function(){
39886         return this.toolbar;
39887     },
39888     
39889     setActiveState : function(active)
39890     {
39891         this.active = active;
39892         this.setActiveClass(active);
39893         if(!active){
39894             if(this.fireEvent("deactivate", this) === false){
39895                 return false;
39896             }
39897             return true;
39898         }
39899         this.fireEvent("activate", this);
39900         return true;
39901     },
39902     /**
39903      * Updates this panel's element (not for iframe)
39904      * @param {String} content The new content
39905      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39906     */
39907     setContent : function(content, loadScripts){
39908         if (this.iframe) {
39909             return;
39910         }
39911         
39912         this.el.update(content, loadScripts);
39913     },
39914
39915     ignoreResize : function(w, h){
39916         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39917             return true;
39918         }else{
39919             this.lastSize = {width: w, height: h};
39920             return false;
39921         }
39922     },
39923     /**
39924      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39925      * @return {Roo.UpdateManager} The UpdateManager
39926      */
39927     getUpdateManager : function(){
39928         if (this.iframe) {
39929             return false;
39930         }
39931         return this.el.getUpdateManager();
39932     },
39933      /**
39934      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39935      * Does not work with IFRAME contents
39936      * @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:
39937 <pre><code>
39938 panel.load({
39939     url: "your-url.php",
39940     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39941     callback: yourFunction,
39942     scope: yourObject, //(optional scope)
39943     discardUrl: false,
39944     nocache: false,
39945     text: "Loading...",
39946     timeout: 30,
39947     scripts: false
39948 });
39949 </code></pre>
39950      
39951      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39952      * 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.
39953      * @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}
39954      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39955      * @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.
39956      * @return {Roo.ContentPanel} this
39957      */
39958     load : function(){
39959         
39960         if (this.iframe) {
39961             return this;
39962         }
39963         
39964         var um = this.el.getUpdateManager();
39965         um.update.apply(um, arguments);
39966         return this;
39967     },
39968
39969
39970     /**
39971      * 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.
39972      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39973      * @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)
39974      * @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)
39975      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39976      */
39977     setUrl : function(url, params, loadOnce){
39978         if (this.iframe) {
39979             this.iframeEl.dom.src = url;
39980             return false;
39981         }
39982         
39983         if(this.refreshDelegate){
39984             this.removeListener("activate", this.refreshDelegate);
39985         }
39986         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39987         this.on("activate", this.refreshDelegate);
39988         return this.el.getUpdateManager();
39989     },
39990     
39991     _handleRefresh : function(url, params, loadOnce){
39992         if(!loadOnce || !this.loaded){
39993             var updater = this.el.getUpdateManager();
39994             updater.update(url, params, this._setLoaded.createDelegate(this));
39995         }
39996     },
39997     
39998     _setLoaded : function(){
39999         this.loaded = true;
40000     }, 
40001     
40002     /**
40003      * Returns this panel's id
40004      * @return {String} 
40005      */
40006     getId : function(){
40007         return this.el.id;
40008     },
40009     
40010     /** 
40011      * Returns this panel's element - used by regiosn to add.
40012      * @return {Roo.Element} 
40013      */
40014     getEl : function(){
40015         return this.wrapEl || this.el;
40016     },
40017     
40018    
40019     
40020     adjustForComponents : function(width, height)
40021     {
40022         //Roo.log('adjustForComponents ');
40023         if(this.resizeEl != this.el){
40024             width -= this.el.getFrameWidth('lr');
40025             height -= this.el.getFrameWidth('tb');
40026         }
40027         if(this.toolbar){
40028             var te = this.toolbar.getEl();
40029             te.setWidth(width);
40030             height -= te.getHeight();
40031         }
40032         if(this.footer){
40033             var te = this.footer.getEl();
40034             te.setWidth(width);
40035             height -= te.getHeight();
40036         }
40037         
40038         
40039         if(this.adjustments){
40040             width += this.adjustments[0];
40041             height += this.adjustments[1];
40042         }
40043         return {"width": width, "height": height};
40044     },
40045     
40046     setSize : function(width, height){
40047         if(this.fitToFrame && !this.ignoreResize(width, height)){
40048             if(this.fitContainer && this.resizeEl != this.el){
40049                 this.el.setSize(width, height);
40050             }
40051             var size = this.adjustForComponents(width, height);
40052             if (this.iframe) {
40053                 this.iframeEl.setSize(width,height);
40054             }
40055             
40056             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40057             this.fireEvent('resize', this, size.width, size.height);
40058             
40059             
40060         }
40061     },
40062     
40063     /**
40064      * Returns this panel's title
40065      * @return {String} 
40066      */
40067     getTitle : function(){
40068         
40069         if (typeof(this.title) != 'object') {
40070             return this.title;
40071         }
40072         
40073         var t = '';
40074         for (var k in this.title) {
40075             if (!this.title.hasOwnProperty(k)) {
40076                 continue;
40077             }
40078             
40079             if (k.indexOf('-') >= 0) {
40080                 var s = k.split('-');
40081                 for (var i = 0; i<s.length; i++) {
40082                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40083                 }
40084             } else {
40085                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40086             }
40087         }
40088         return t;
40089     },
40090     
40091     /**
40092      * Set this panel's title
40093      * @param {String} title
40094      */
40095     setTitle : function(title){
40096         this.title = title;
40097         if(this.region){
40098             this.region.updatePanelTitle(this, title);
40099         }
40100     },
40101     
40102     /**
40103      * Returns true is this panel was configured to be closable
40104      * @return {Boolean} 
40105      */
40106     isClosable : function(){
40107         return this.closable;
40108     },
40109     
40110     beforeSlide : function(){
40111         this.el.clip();
40112         this.resizeEl.clip();
40113     },
40114     
40115     afterSlide : function(){
40116         this.el.unclip();
40117         this.resizeEl.unclip();
40118     },
40119     
40120     /**
40121      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40122      *   Will fail silently if the {@link #setUrl} method has not been called.
40123      *   This does not activate the panel, just updates its content.
40124      */
40125     refresh : function(){
40126         if(this.refreshDelegate){
40127            this.loaded = false;
40128            this.refreshDelegate();
40129         }
40130     },
40131     
40132     /**
40133      * Destroys this panel
40134      */
40135     destroy : function(){
40136         this.el.removeAllListeners();
40137         var tempEl = document.createElement("span");
40138         tempEl.appendChild(this.el.dom);
40139         tempEl.innerHTML = "";
40140         this.el.remove();
40141         this.el = null;
40142     },
40143     
40144     /**
40145      * form - if the content panel contains a form - this is a reference to it.
40146      * @type {Roo.form.Form}
40147      */
40148     form : false,
40149     /**
40150      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40151      *    This contains a reference to it.
40152      * @type {Roo.View}
40153      */
40154     view : false,
40155     
40156       /**
40157      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40158      * <pre><code>
40159
40160 layout.addxtype({
40161        xtype : 'Form',
40162        items: [ .... ]
40163    }
40164 );
40165
40166 </code></pre>
40167      * @param {Object} cfg Xtype definition of item to add.
40168      */
40169     
40170     
40171     getChildContainer: function () {
40172         return this.getEl();
40173     }
40174     
40175     
40176     /*
40177         var  ret = new Roo.factory(cfg);
40178         return ret;
40179         
40180         
40181         // add form..
40182         if (cfg.xtype.match(/^Form$/)) {
40183             
40184             var el;
40185             //if (this.footer) {
40186             //    el = this.footer.container.insertSibling(false, 'before');
40187             //} else {
40188                 el = this.el.createChild();
40189             //}
40190
40191             this.form = new  Roo.form.Form(cfg);
40192             
40193             
40194             if ( this.form.allItems.length) {
40195                 this.form.render(el.dom);
40196             }
40197             return this.form;
40198         }
40199         // should only have one of theses..
40200         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40201             // views.. should not be just added - used named prop 'view''
40202             
40203             cfg.el = this.el.appendChild(document.createElement("div"));
40204             // factory?
40205             
40206             var ret = new Roo.factory(cfg);
40207              
40208              ret.render && ret.render(false, ''); // render blank..
40209             this.view = ret;
40210             return ret;
40211         }
40212         return false;
40213     }
40214     \*/
40215 });
40216  
40217 /**
40218  * @class Roo.bootstrap.panel.Grid
40219  * @extends Roo.bootstrap.panel.Content
40220  * @constructor
40221  * Create a new GridPanel.
40222  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40223  * @param {Object} config A the config object
40224   
40225  */
40226
40227
40228
40229 Roo.bootstrap.panel.Grid = function(config)
40230 {
40231     
40232       
40233     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40234         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40235
40236     config.el = this.wrapper;
40237     //this.el = this.wrapper;
40238     
40239       if (config.container) {
40240         // ctor'ed from a Border/panel.grid
40241         
40242         
40243         this.wrapper.setStyle("overflow", "hidden");
40244         this.wrapper.addClass('roo-grid-container');
40245
40246     }
40247     
40248     
40249     if(config.toolbar){
40250         var tool_el = this.wrapper.createChild();    
40251         this.toolbar = Roo.factory(config.toolbar);
40252         var ti = [];
40253         if (config.toolbar.items) {
40254             ti = config.toolbar.items ;
40255             delete config.toolbar.items ;
40256         }
40257         
40258         var nitems = [];
40259         this.toolbar.render(tool_el);
40260         for(var i =0;i < ti.length;i++) {
40261           //  Roo.log(['add child', items[i]]);
40262             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40263         }
40264         this.toolbar.items = nitems;
40265         
40266         delete config.toolbar;
40267     }
40268     
40269     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40270     config.grid.scrollBody = true;;
40271     config.grid.monitorWindowResize = false; // turn off autosizing
40272     config.grid.autoHeight = false;
40273     config.grid.autoWidth = false;
40274     
40275     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40276     
40277     if (config.background) {
40278         // render grid on panel activation (if panel background)
40279         this.on('activate', function(gp) {
40280             if (!gp.grid.rendered) {
40281                 gp.grid.render(this.wrapper);
40282                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40283             }
40284         });
40285             
40286     } else {
40287         this.grid.render(this.wrapper);
40288         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40289
40290     }
40291     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40292     // ??? needed ??? config.el = this.wrapper;
40293     
40294     
40295     
40296   
40297     // xtype created footer. - not sure if will work as we normally have to render first..
40298     if (this.footer && !this.footer.el && this.footer.xtype) {
40299         
40300         var ctr = this.grid.getView().getFooterPanel(true);
40301         this.footer.dataSource = this.grid.dataSource;
40302         this.footer = Roo.factory(this.footer, Roo);
40303         this.footer.render(ctr);
40304         
40305     }
40306     
40307     
40308     
40309     
40310      
40311 };
40312
40313 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40314     getId : function(){
40315         return this.grid.id;
40316     },
40317     
40318     /**
40319      * Returns the grid for this panel
40320      * @return {Roo.bootstrap.Table} 
40321      */
40322     getGrid : function(){
40323         return this.grid;    
40324     },
40325     
40326     setSize : function(width, height){
40327         if(!this.ignoreResize(width, height)){
40328             var grid = this.grid;
40329             var size = this.adjustForComponents(width, height);
40330             // tfoot is not a footer?
40331           
40332             
40333             var gridel = grid.getGridEl();
40334             gridel.setSize(size.width, size.height);
40335             
40336             var tbd = grid.getGridEl().select('tbody', true).first();
40337             var thd = grid.getGridEl().select('thead',true).first();
40338             var tbf= grid.getGridEl().select('tfoot', true).first();
40339
40340             if (tbf) {
40341                 size.height -= tbf.getHeight();
40342             }
40343             if (thd) {
40344                 size.height -= thd.getHeight();
40345             }
40346             
40347             tbd.setSize(size.width, size.height );
40348             // this is for the account management tab -seems to work there.
40349             var thd = grid.getGridEl().select('thead',true).first();
40350             //if (tbd) {
40351             //    tbd.setSize(size.width, size.height - thd.getHeight());
40352             //}
40353              
40354             grid.autoSize();
40355         }
40356     },
40357      
40358     
40359     
40360     beforeSlide : function(){
40361         this.grid.getView().scroller.clip();
40362     },
40363     
40364     afterSlide : function(){
40365         this.grid.getView().scroller.unclip();
40366     },
40367     
40368     destroy : function(){
40369         this.grid.destroy();
40370         delete this.grid;
40371         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40372     }
40373 });
40374
40375 /**
40376  * @class Roo.bootstrap.panel.Nest
40377  * @extends Roo.bootstrap.panel.Content
40378  * @constructor
40379  * Create a new Panel, that can contain a layout.Border.
40380  * 
40381  * 
40382  * @param {Roo.BorderLayout} layout The layout for this panel
40383  * @param {String/Object} config A string to set only the title or a config object
40384  */
40385 Roo.bootstrap.panel.Nest = function(config)
40386 {
40387     // construct with only one argument..
40388     /* FIXME - implement nicer consturctors
40389     if (layout.layout) {
40390         config = layout;
40391         layout = config.layout;
40392         delete config.layout;
40393     }
40394     if (layout.xtype && !layout.getEl) {
40395         // then layout needs constructing..
40396         layout = Roo.factory(layout, Roo);
40397     }
40398     */
40399     
40400     config.el =  config.layout.getEl();
40401     
40402     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40403     
40404     config.layout.monitorWindowResize = false; // turn off autosizing
40405     this.layout = config.layout;
40406     this.layout.getEl().addClass("roo-layout-nested-layout");
40407     this.layout.parent = this;
40408     
40409     
40410     
40411     
40412 };
40413
40414 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40415
40416     setSize : function(width, height){
40417         if(!this.ignoreResize(width, height)){
40418             var size = this.adjustForComponents(width, height);
40419             var el = this.layout.getEl();
40420             if (size.height < 1) {
40421                 el.setWidth(size.width);   
40422             } else {
40423                 el.setSize(size.width, size.height);
40424             }
40425             var touch = el.dom.offsetWidth;
40426             this.layout.layout();
40427             // ie requires a double layout on the first pass
40428             if(Roo.isIE && !this.initialized){
40429                 this.initialized = true;
40430                 this.layout.layout();
40431             }
40432         }
40433     },
40434     
40435     // activate all subpanels if not currently active..
40436     
40437     setActiveState : function(active){
40438         this.active = active;
40439         this.setActiveClass(active);
40440         
40441         if(!active){
40442             this.fireEvent("deactivate", this);
40443             return;
40444         }
40445         
40446         this.fireEvent("activate", this);
40447         // not sure if this should happen before or after..
40448         if (!this.layout) {
40449             return; // should not happen..
40450         }
40451         var reg = false;
40452         for (var r in this.layout.regions) {
40453             reg = this.layout.getRegion(r);
40454             if (reg.getActivePanel()) {
40455                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40456                 reg.setActivePanel(reg.getActivePanel());
40457                 continue;
40458             }
40459             if (!reg.panels.length) {
40460                 continue;
40461             }
40462             reg.showPanel(reg.getPanel(0));
40463         }
40464         
40465         
40466         
40467         
40468     },
40469     
40470     /**
40471      * Returns the nested BorderLayout for this panel
40472      * @return {Roo.BorderLayout} 
40473      */
40474     getLayout : function(){
40475         return this.layout;
40476     },
40477     
40478      /**
40479      * Adds a xtype elements to the layout of the nested panel
40480      * <pre><code>
40481
40482 panel.addxtype({
40483        xtype : 'ContentPanel',
40484        region: 'west',
40485        items: [ .... ]
40486    }
40487 );
40488
40489 panel.addxtype({
40490         xtype : 'NestedLayoutPanel',
40491         region: 'west',
40492         layout: {
40493            center: { },
40494            west: { }   
40495         },
40496         items : [ ... list of content panels or nested layout panels.. ]
40497    }
40498 );
40499 </code></pre>
40500      * @param {Object} cfg Xtype definition of item to add.
40501      */
40502     addxtype : function(cfg) {
40503         return this.layout.addxtype(cfg);
40504     
40505     }
40506 });/*
40507  * Based on:
40508  * Ext JS Library 1.1.1
40509  * Copyright(c) 2006-2007, Ext JS, LLC.
40510  *
40511  * Originally Released Under LGPL - original licence link has changed is not relivant.
40512  *
40513  * Fork - LGPL
40514  * <script type="text/javascript">
40515  */
40516 /**
40517  * @class Roo.TabPanel
40518  * @extends Roo.util.Observable
40519  * A lightweight tab container.
40520  * <br><br>
40521  * Usage:
40522  * <pre><code>
40523 // basic tabs 1, built from existing content
40524 var tabs = new Roo.TabPanel("tabs1");
40525 tabs.addTab("script", "View Script");
40526 tabs.addTab("markup", "View Markup");
40527 tabs.activate("script");
40528
40529 // more advanced tabs, built from javascript
40530 var jtabs = new Roo.TabPanel("jtabs");
40531 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40532
40533 // set up the UpdateManager
40534 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40535 var updater = tab2.getUpdateManager();
40536 updater.setDefaultUrl("ajax1.htm");
40537 tab2.on('activate', updater.refresh, updater, true);
40538
40539 // Use setUrl for Ajax loading
40540 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40541 tab3.setUrl("ajax2.htm", null, true);
40542
40543 // Disabled tab
40544 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40545 tab4.disable();
40546
40547 jtabs.activate("jtabs-1");
40548  * </code></pre>
40549  * @constructor
40550  * Create a new TabPanel.
40551  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40552  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40553  */
40554 Roo.bootstrap.panel.Tabs = function(config){
40555     /**
40556     * The container element for this TabPanel.
40557     * @type Roo.Element
40558     */
40559     this.el = Roo.get(config.el);
40560     delete config.el;
40561     if(config){
40562         if(typeof config == "boolean"){
40563             this.tabPosition = config ? "bottom" : "top";
40564         }else{
40565             Roo.apply(this, config);
40566         }
40567     }
40568     
40569     if(this.tabPosition == "bottom"){
40570         // if tabs are at the bottom = create the body first.
40571         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40572         this.el.addClass("roo-tabs-bottom");
40573     }
40574     // next create the tabs holders
40575     
40576     if (this.tabPosition == "west"){
40577         
40578         var reg = this.region; // fake it..
40579         while (reg) {
40580             if (!reg.mgr.parent) {
40581                 break;
40582             }
40583             reg = reg.mgr.parent.region;
40584         }
40585         Roo.log("got nest?");
40586         Roo.log(reg);
40587         if (reg.mgr.getRegion('west')) {
40588             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40589             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40590             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40591             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40592             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40593         
40594             
40595         }
40596         
40597         
40598     } else {
40599      
40600         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40601         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40602         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40603         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40604     }
40605     
40606     
40607     if(Roo.isIE){
40608         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40609     }
40610     
40611     // finally - if tabs are at the top, then create the body last..
40612     if(this.tabPosition != "bottom"){
40613         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40614          * @type Roo.Element
40615          */
40616         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40617         this.el.addClass("roo-tabs-top");
40618     }
40619     this.items = [];
40620
40621     this.bodyEl.setStyle("position", "relative");
40622
40623     this.active = null;
40624     this.activateDelegate = this.activate.createDelegate(this);
40625
40626     this.addEvents({
40627         /**
40628          * @event tabchange
40629          * Fires when the active tab changes
40630          * @param {Roo.TabPanel} this
40631          * @param {Roo.TabPanelItem} activePanel The new active tab
40632          */
40633         "tabchange": true,
40634         /**
40635          * @event beforetabchange
40636          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40637          * @param {Roo.TabPanel} this
40638          * @param {Object} e Set cancel to true on this object to cancel the tab change
40639          * @param {Roo.TabPanelItem} tab The tab being changed to
40640          */
40641         "beforetabchange" : true
40642     });
40643
40644     Roo.EventManager.onWindowResize(this.onResize, this);
40645     this.cpad = this.el.getPadding("lr");
40646     this.hiddenCount = 0;
40647
40648
40649     // toolbar on the tabbar support...
40650     if (this.toolbar) {
40651         alert("no toolbar support yet");
40652         this.toolbar  = false;
40653         /*
40654         var tcfg = this.toolbar;
40655         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40656         this.toolbar = new Roo.Toolbar(tcfg);
40657         if (Roo.isSafari) {
40658             var tbl = tcfg.container.child('table', true);
40659             tbl.setAttribute('width', '100%');
40660         }
40661         */
40662         
40663     }
40664    
40665
40666
40667     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40668 };
40669
40670 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40671     /*
40672      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40673      */
40674     tabPosition : "top",
40675     /*
40676      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40677      */
40678     currentTabWidth : 0,
40679     /*
40680      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40681      */
40682     minTabWidth : 40,
40683     /*
40684      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40685      */
40686     maxTabWidth : 250,
40687     /*
40688      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40689      */
40690     preferredTabWidth : 175,
40691     /*
40692      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40693      */
40694     resizeTabs : false,
40695     /*
40696      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40697      */
40698     monitorResize : true,
40699     /*
40700      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40701      */
40702     toolbar : false,  // set by caller..
40703     
40704     region : false, /// set by caller
40705     
40706     disableTooltips : true, // not used yet...
40707
40708     /**
40709      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40710      * @param {String} id The id of the div to use <b>or create</b>
40711      * @param {String} text The text for the tab
40712      * @param {String} content (optional) Content to put in the TabPanelItem body
40713      * @param {Boolean} closable (optional) True to create a close icon on the tab
40714      * @return {Roo.TabPanelItem} The created TabPanelItem
40715      */
40716     addTab : function(id, text, content, closable, tpl)
40717     {
40718         var item = new Roo.bootstrap.panel.TabItem({
40719             panel: this,
40720             id : id,
40721             text : text,
40722             closable : closable,
40723             tpl : tpl
40724         });
40725         this.addTabItem(item);
40726         if(content){
40727             item.setContent(content);
40728         }
40729         return item;
40730     },
40731
40732     /**
40733      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40734      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40735      * @return {Roo.TabPanelItem}
40736      */
40737     getTab : function(id){
40738         return this.items[id];
40739     },
40740
40741     /**
40742      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40743      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40744      */
40745     hideTab : function(id){
40746         var t = this.items[id];
40747         if(!t.isHidden()){
40748            t.setHidden(true);
40749            this.hiddenCount++;
40750            this.autoSizeTabs();
40751         }
40752     },
40753
40754     /**
40755      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40756      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40757      */
40758     unhideTab : function(id){
40759         var t = this.items[id];
40760         if(t.isHidden()){
40761            t.setHidden(false);
40762            this.hiddenCount--;
40763            this.autoSizeTabs();
40764         }
40765     },
40766
40767     /**
40768      * Adds an existing {@link Roo.TabPanelItem}.
40769      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40770      */
40771     addTabItem : function(item)
40772     {
40773         this.items[item.id] = item;
40774         this.items.push(item);
40775         this.autoSizeTabs();
40776       //  if(this.resizeTabs){
40777     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40778   //         this.autoSizeTabs();
40779 //        }else{
40780 //            item.autoSize();
40781        // }
40782     },
40783
40784     /**
40785      * Removes a {@link Roo.TabPanelItem}.
40786      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40787      */
40788     removeTab : function(id){
40789         var items = this.items;
40790         var tab = items[id];
40791         if(!tab) { return; }
40792         var index = items.indexOf(tab);
40793         if(this.active == tab && items.length > 1){
40794             var newTab = this.getNextAvailable(index);
40795             if(newTab) {
40796                 newTab.activate();
40797             }
40798         }
40799         this.stripEl.dom.removeChild(tab.pnode.dom);
40800         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40801             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40802         }
40803         items.splice(index, 1);
40804         delete this.items[tab.id];
40805         tab.fireEvent("close", tab);
40806         tab.purgeListeners();
40807         this.autoSizeTabs();
40808     },
40809
40810     getNextAvailable : function(start){
40811         var items = this.items;
40812         var index = start;
40813         // look for a next tab that will slide over to
40814         // replace the one being removed
40815         while(index < items.length){
40816             var item = items[++index];
40817             if(item && !item.isHidden()){
40818                 return item;
40819             }
40820         }
40821         // if one isn't found select the previous tab (on the left)
40822         index = start;
40823         while(index >= 0){
40824             var item = items[--index];
40825             if(item && !item.isHidden()){
40826                 return item;
40827             }
40828         }
40829         return null;
40830     },
40831
40832     /**
40833      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40834      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40835      */
40836     disableTab : function(id){
40837         var tab = this.items[id];
40838         if(tab && this.active != tab){
40839             tab.disable();
40840         }
40841     },
40842
40843     /**
40844      * Enables a {@link Roo.TabPanelItem} that is disabled.
40845      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40846      */
40847     enableTab : function(id){
40848         var tab = this.items[id];
40849         tab.enable();
40850     },
40851
40852     /**
40853      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40854      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40855      * @return {Roo.TabPanelItem} The TabPanelItem.
40856      */
40857     activate : function(id)
40858     {
40859         //Roo.log('activite:'  + id);
40860         
40861         var tab = this.items[id];
40862         if(!tab){
40863             return null;
40864         }
40865         if(tab == this.active || tab.disabled){
40866             return tab;
40867         }
40868         var e = {};
40869         this.fireEvent("beforetabchange", this, e, tab);
40870         if(e.cancel !== true && !tab.disabled){
40871             if(this.active){
40872                 this.active.hide();
40873             }
40874             this.active = this.items[id];
40875             this.active.show();
40876             this.fireEvent("tabchange", this, this.active);
40877         }
40878         return tab;
40879     },
40880
40881     /**
40882      * Gets the active {@link Roo.TabPanelItem}.
40883      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40884      */
40885     getActiveTab : function(){
40886         return this.active;
40887     },
40888
40889     /**
40890      * Updates the tab body element to fit the height of the container element
40891      * for overflow scrolling
40892      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40893      */
40894     syncHeight : function(targetHeight){
40895         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40896         var bm = this.bodyEl.getMargins();
40897         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40898         this.bodyEl.setHeight(newHeight);
40899         return newHeight;
40900     },
40901
40902     onResize : function(){
40903         if(this.monitorResize){
40904             this.autoSizeTabs();
40905         }
40906     },
40907
40908     /**
40909      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40910      */
40911     beginUpdate : function(){
40912         this.updating = true;
40913     },
40914
40915     /**
40916      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40917      */
40918     endUpdate : function(){
40919         this.updating = false;
40920         this.autoSizeTabs();
40921     },
40922
40923     /**
40924      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40925      */
40926     autoSizeTabs : function()
40927     {
40928         var count = this.items.length;
40929         var vcount = count - this.hiddenCount;
40930         
40931         if (vcount < 2) {
40932             this.stripEl.hide();
40933         } else {
40934             this.stripEl.show();
40935         }
40936         
40937         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40938             return;
40939         }
40940         
40941         
40942         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40943         var availWidth = Math.floor(w / vcount);
40944         var b = this.stripBody;
40945         if(b.getWidth() > w){
40946             var tabs = this.items;
40947             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40948             if(availWidth < this.minTabWidth){
40949                 /*if(!this.sleft){    // incomplete scrolling code
40950                     this.createScrollButtons();
40951                 }
40952                 this.showScroll();
40953                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40954             }
40955         }else{
40956             if(this.currentTabWidth < this.preferredTabWidth){
40957                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40958             }
40959         }
40960     },
40961
40962     /**
40963      * Returns the number of tabs in this TabPanel.
40964      * @return {Number}
40965      */
40966      getCount : function(){
40967          return this.items.length;
40968      },
40969
40970     /**
40971      * Resizes all the tabs to the passed width
40972      * @param {Number} The new width
40973      */
40974     setTabWidth : function(width){
40975         this.currentTabWidth = width;
40976         for(var i = 0, len = this.items.length; i < len; i++) {
40977                 if(!this.items[i].isHidden()) {
40978                 this.items[i].setWidth(width);
40979             }
40980         }
40981     },
40982
40983     /**
40984      * Destroys this TabPanel
40985      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40986      */
40987     destroy : function(removeEl){
40988         Roo.EventManager.removeResizeListener(this.onResize, this);
40989         for(var i = 0, len = this.items.length; i < len; i++){
40990             this.items[i].purgeListeners();
40991         }
40992         if(removeEl === true){
40993             this.el.update("");
40994             this.el.remove();
40995         }
40996     },
40997     
40998     createStrip : function(container)
40999     {
41000         var strip = document.createElement("nav");
41001         strip.className = Roo.bootstrap.version == 4 ?
41002             "navbar-light bg-light" : 
41003             "navbar navbar-default"; //"x-tabs-wrap";
41004         container.appendChild(strip);
41005         return strip;
41006     },
41007     
41008     createStripList : function(strip)
41009     {
41010         // div wrapper for retard IE
41011         // returns the "tr" element.
41012         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41013         //'<div class="x-tabs-strip-wrap">'+
41014           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41015           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41016         return strip.firstChild; //.firstChild.firstChild.firstChild;
41017     },
41018     createBody : function(container)
41019     {
41020         var body = document.createElement("div");
41021         Roo.id(body, "tab-body");
41022         //Roo.fly(body).addClass("x-tabs-body");
41023         Roo.fly(body).addClass("tab-content");
41024         container.appendChild(body);
41025         return body;
41026     },
41027     createItemBody :function(bodyEl, id){
41028         var body = Roo.getDom(id);
41029         if(!body){
41030             body = document.createElement("div");
41031             body.id = id;
41032         }
41033         //Roo.fly(body).addClass("x-tabs-item-body");
41034         Roo.fly(body).addClass("tab-pane");
41035          bodyEl.insertBefore(body, bodyEl.firstChild);
41036         return body;
41037     },
41038     /** @private */
41039     createStripElements :  function(stripEl, text, closable, tpl)
41040     {
41041         var td = document.createElement("li"); // was td..
41042         td.className = 'nav-item';
41043         
41044         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41045         
41046         
41047         stripEl.appendChild(td);
41048         /*if(closable){
41049             td.className = "x-tabs-closable";
41050             if(!this.closeTpl){
41051                 this.closeTpl = new Roo.Template(
41052                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41053                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41054                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41055                 );
41056             }
41057             var el = this.closeTpl.overwrite(td, {"text": text});
41058             var close = el.getElementsByTagName("div")[0];
41059             var inner = el.getElementsByTagName("em")[0];
41060             return {"el": el, "close": close, "inner": inner};
41061         } else {
41062         */
41063         // not sure what this is..
41064 //            if(!this.tabTpl){
41065                 //this.tabTpl = new Roo.Template(
41066                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41067                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41068                 //);
41069 //                this.tabTpl = new Roo.Template(
41070 //                   '<a href="#">' +
41071 //                   '<span unselectable="on"' +
41072 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41073 //                            ' >{text}</span></a>'
41074 //                );
41075 //                
41076 //            }
41077
41078
41079             var template = tpl || this.tabTpl || false;
41080             
41081             if(!template){
41082                 template =  new Roo.Template(
41083                         Roo.bootstrap.version == 4 ? 
41084                             (
41085                                 '<a class="nav-link" href="#" unselectable="on"' +
41086                                      (this.disableTooltips ? '' : ' title="{text}"') +
41087                                      ' >{text}</a>'
41088                             ) : (
41089                                 '<a class="nav-link" href="#">' +
41090                                 '<span unselectable="on"' +
41091                                          (this.disableTooltips ? '' : ' title="{text}"') +
41092                                     ' >{text}</span></a>'
41093                             )
41094                 );
41095             }
41096             
41097             switch (typeof(template)) {
41098                 case 'object' :
41099                     break;
41100                 case 'string' :
41101                     template = new Roo.Template(template);
41102                     break;
41103                 default :
41104                     break;
41105             }
41106             
41107             var el = template.overwrite(td, {"text": text});
41108             
41109             var inner = el.getElementsByTagName("span")[0];
41110             
41111             return {"el": el, "inner": inner};
41112             
41113     }
41114         
41115     
41116 });
41117
41118 /**
41119  * @class Roo.TabPanelItem
41120  * @extends Roo.util.Observable
41121  * Represents an individual item (tab plus body) in a TabPanel.
41122  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41123  * @param {String} id The id of this TabPanelItem
41124  * @param {String} text The text for the tab of this TabPanelItem
41125  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41126  */
41127 Roo.bootstrap.panel.TabItem = function(config){
41128     /**
41129      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41130      * @type Roo.TabPanel
41131      */
41132     this.tabPanel = config.panel;
41133     /**
41134      * The id for this TabPanelItem
41135      * @type String
41136      */
41137     this.id = config.id;
41138     /** @private */
41139     this.disabled = false;
41140     /** @private */
41141     this.text = config.text;
41142     /** @private */
41143     this.loaded = false;
41144     this.closable = config.closable;
41145
41146     /**
41147      * The body element for this TabPanelItem.
41148      * @type Roo.Element
41149      */
41150     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41151     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41152     this.bodyEl.setStyle("display", "block");
41153     this.bodyEl.setStyle("zoom", "1");
41154     //this.hideAction();
41155
41156     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41157     /** @private */
41158     this.el = Roo.get(els.el);
41159     this.inner = Roo.get(els.inner, true);
41160      this.textEl = Roo.bootstrap.version == 4 ?
41161         this.el : Roo.get(this.el.dom.firstChild, true);
41162
41163     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41164     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41165
41166     
41167 //    this.el.on("mousedown", this.onTabMouseDown, this);
41168     this.el.on("click", this.onTabClick, this);
41169     /** @private */
41170     if(config.closable){
41171         var c = Roo.get(els.close, true);
41172         c.dom.title = this.closeText;
41173         c.addClassOnOver("close-over");
41174         c.on("click", this.closeClick, this);
41175      }
41176
41177     this.addEvents({
41178          /**
41179          * @event activate
41180          * Fires when this tab becomes the active tab.
41181          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41182          * @param {Roo.TabPanelItem} this
41183          */
41184         "activate": true,
41185         /**
41186          * @event beforeclose
41187          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41188          * @param {Roo.TabPanelItem} this
41189          * @param {Object} e Set cancel to true on this object to cancel the close.
41190          */
41191         "beforeclose": true,
41192         /**
41193          * @event close
41194          * Fires when this tab is closed.
41195          * @param {Roo.TabPanelItem} this
41196          */
41197          "close": true,
41198         /**
41199          * @event deactivate
41200          * Fires when this tab is no longer the active tab.
41201          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41202          * @param {Roo.TabPanelItem} this
41203          */
41204          "deactivate" : true
41205     });
41206     this.hidden = false;
41207
41208     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41209 };
41210
41211 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41212            {
41213     purgeListeners : function(){
41214        Roo.util.Observable.prototype.purgeListeners.call(this);
41215        this.el.removeAllListeners();
41216     },
41217     /**
41218      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41219      */
41220     show : function(){
41221         this.status_node.addClass("active");
41222         this.showAction();
41223         if(Roo.isOpera){
41224             this.tabPanel.stripWrap.repaint();
41225         }
41226         this.fireEvent("activate", this.tabPanel, this);
41227     },
41228
41229     /**
41230      * Returns true if this tab is the active tab.
41231      * @return {Boolean}
41232      */
41233     isActive : function(){
41234         return this.tabPanel.getActiveTab() == this;
41235     },
41236
41237     /**
41238      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41239      */
41240     hide : function(){
41241         this.status_node.removeClass("active");
41242         this.hideAction();
41243         this.fireEvent("deactivate", this.tabPanel, this);
41244     },
41245
41246     hideAction : function(){
41247         this.bodyEl.hide();
41248         this.bodyEl.setStyle("position", "absolute");
41249         this.bodyEl.setLeft("-20000px");
41250         this.bodyEl.setTop("-20000px");
41251     },
41252
41253     showAction : function(){
41254         this.bodyEl.setStyle("position", "relative");
41255         this.bodyEl.setTop("");
41256         this.bodyEl.setLeft("");
41257         this.bodyEl.show();
41258     },
41259
41260     /**
41261      * Set the tooltip for the tab.
41262      * @param {String} tooltip The tab's tooltip
41263      */
41264     setTooltip : function(text){
41265         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41266             this.textEl.dom.qtip = text;
41267             this.textEl.dom.removeAttribute('title');
41268         }else{
41269             this.textEl.dom.title = text;
41270         }
41271     },
41272
41273     onTabClick : function(e){
41274         e.preventDefault();
41275         this.tabPanel.activate(this.id);
41276     },
41277
41278     onTabMouseDown : function(e){
41279         e.preventDefault();
41280         this.tabPanel.activate(this.id);
41281     },
41282 /*
41283     getWidth : function(){
41284         return this.inner.getWidth();
41285     },
41286
41287     setWidth : function(width){
41288         var iwidth = width - this.linode.getPadding("lr");
41289         this.inner.setWidth(iwidth);
41290         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41291         this.linode.setWidth(width);
41292     },
41293 */
41294     /**
41295      * Show or hide the tab
41296      * @param {Boolean} hidden True to hide or false to show.
41297      */
41298     setHidden : function(hidden){
41299         this.hidden = hidden;
41300         this.linode.setStyle("display", hidden ? "none" : "");
41301     },
41302
41303     /**
41304      * Returns true if this tab is "hidden"
41305      * @return {Boolean}
41306      */
41307     isHidden : function(){
41308         return this.hidden;
41309     },
41310
41311     /**
41312      * Returns the text for this tab
41313      * @return {String}
41314      */
41315     getText : function(){
41316         return this.text;
41317     },
41318     /*
41319     autoSize : function(){
41320         //this.el.beginMeasure();
41321         this.textEl.setWidth(1);
41322         /*
41323          *  #2804 [new] Tabs in Roojs
41324          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41325          */
41326         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41327         //this.el.endMeasure();
41328     //},
41329
41330     /**
41331      * Sets the text for the tab (Note: this also sets the tooltip text)
41332      * @param {String} text The tab's text and tooltip
41333      */
41334     setText : function(text){
41335         this.text = text;
41336         this.textEl.update(text);
41337         this.setTooltip(text);
41338         //if(!this.tabPanel.resizeTabs){
41339         //    this.autoSize();
41340         //}
41341     },
41342     /**
41343      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41344      */
41345     activate : function(){
41346         this.tabPanel.activate(this.id);
41347     },
41348
41349     /**
41350      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41351      */
41352     disable : function(){
41353         if(this.tabPanel.active != this){
41354             this.disabled = true;
41355             this.status_node.addClass("disabled");
41356         }
41357     },
41358
41359     /**
41360      * Enables this TabPanelItem if it was previously disabled.
41361      */
41362     enable : function(){
41363         this.disabled = false;
41364         this.status_node.removeClass("disabled");
41365     },
41366
41367     /**
41368      * Sets the content for this TabPanelItem.
41369      * @param {String} content The content
41370      * @param {Boolean} loadScripts true to look for and load scripts
41371      */
41372     setContent : function(content, loadScripts){
41373         this.bodyEl.update(content, loadScripts);
41374     },
41375
41376     /**
41377      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41378      * @return {Roo.UpdateManager} The UpdateManager
41379      */
41380     getUpdateManager : function(){
41381         return this.bodyEl.getUpdateManager();
41382     },
41383
41384     /**
41385      * Set a URL to be used to load the content for this TabPanelItem.
41386      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41387      * @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)
41388      * @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)
41389      * @return {Roo.UpdateManager} The UpdateManager
41390      */
41391     setUrl : function(url, params, loadOnce){
41392         if(this.refreshDelegate){
41393             this.un('activate', this.refreshDelegate);
41394         }
41395         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41396         this.on("activate", this.refreshDelegate);
41397         return this.bodyEl.getUpdateManager();
41398     },
41399
41400     /** @private */
41401     _handleRefresh : function(url, params, loadOnce){
41402         if(!loadOnce || !this.loaded){
41403             var updater = this.bodyEl.getUpdateManager();
41404             updater.update(url, params, this._setLoaded.createDelegate(this));
41405         }
41406     },
41407
41408     /**
41409      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41410      *   Will fail silently if the setUrl method has not been called.
41411      *   This does not activate the panel, just updates its content.
41412      */
41413     refresh : function(){
41414         if(this.refreshDelegate){
41415            this.loaded = false;
41416            this.refreshDelegate();
41417         }
41418     },
41419
41420     /** @private */
41421     _setLoaded : function(){
41422         this.loaded = true;
41423     },
41424
41425     /** @private */
41426     closeClick : function(e){
41427         var o = {};
41428         e.stopEvent();
41429         this.fireEvent("beforeclose", this, o);
41430         if(o.cancel !== true){
41431             this.tabPanel.removeTab(this.id);
41432         }
41433     },
41434     /**
41435      * The text displayed in the tooltip for the close icon.
41436      * @type String
41437      */
41438     closeText : "Close this tab"
41439 });
41440 /**
41441 *    This script refer to:
41442 *    Title: International Telephone Input
41443 *    Author: Jack O'Connor
41444 *    Code version:  v12.1.12
41445 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41446 **/
41447
41448 Roo.bootstrap.PhoneInputData = function() {
41449     var d = [
41450       [
41451         "Afghanistan (‫افغانستان‬‎)",
41452         "af",
41453         "93"
41454       ],
41455       [
41456         "Albania (Shqipëri)",
41457         "al",
41458         "355"
41459       ],
41460       [
41461         "Algeria (‫الجزائر‬‎)",
41462         "dz",
41463         "213"
41464       ],
41465       [
41466         "American Samoa",
41467         "as",
41468         "1684"
41469       ],
41470       [
41471         "Andorra",
41472         "ad",
41473         "376"
41474       ],
41475       [
41476         "Angola",
41477         "ao",
41478         "244"
41479       ],
41480       [
41481         "Anguilla",
41482         "ai",
41483         "1264"
41484       ],
41485       [
41486         "Antigua and Barbuda",
41487         "ag",
41488         "1268"
41489       ],
41490       [
41491         "Argentina",
41492         "ar",
41493         "54"
41494       ],
41495       [
41496         "Armenia (Հայաստան)",
41497         "am",
41498         "374"
41499       ],
41500       [
41501         "Aruba",
41502         "aw",
41503         "297"
41504       ],
41505       [
41506         "Australia",
41507         "au",
41508         "61",
41509         0
41510       ],
41511       [
41512         "Austria (Österreich)",
41513         "at",
41514         "43"
41515       ],
41516       [
41517         "Azerbaijan (Azərbaycan)",
41518         "az",
41519         "994"
41520       ],
41521       [
41522         "Bahamas",
41523         "bs",
41524         "1242"
41525       ],
41526       [
41527         "Bahrain (‫البحرين‬‎)",
41528         "bh",
41529         "973"
41530       ],
41531       [
41532         "Bangladesh (বাংলাদেশ)",
41533         "bd",
41534         "880"
41535       ],
41536       [
41537         "Barbados",
41538         "bb",
41539         "1246"
41540       ],
41541       [
41542         "Belarus (Беларусь)",
41543         "by",
41544         "375"
41545       ],
41546       [
41547         "Belgium (België)",
41548         "be",
41549         "32"
41550       ],
41551       [
41552         "Belize",
41553         "bz",
41554         "501"
41555       ],
41556       [
41557         "Benin (Bénin)",
41558         "bj",
41559         "229"
41560       ],
41561       [
41562         "Bermuda",
41563         "bm",
41564         "1441"
41565       ],
41566       [
41567         "Bhutan (འབྲུག)",
41568         "bt",
41569         "975"
41570       ],
41571       [
41572         "Bolivia",
41573         "bo",
41574         "591"
41575       ],
41576       [
41577         "Bosnia and Herzegovina (Босна и Херцеговина)",
41578         "ba",
41579         "387"
41580       ],
41581       [
41582         "Botswana",
41583         "bw",
41584         "267"
41585       ],
41586       [
41587         "Brazil (Brasil)",
41588         "br",
41589         "55"
41590       ],
41591       [
41592         "British Indian Ocean Territory",
41593         "io",
41594         "246"
41595       ],
41596       [
41597         "British Virgin Islands",
41598         "vg",
41599         "1284"
41600       ],
41601       [
41602         "Brunei",
41603         "bn",
41604         "673"
41605       ],
41606       [
41607         "Bulgaria (България)",
41608         "bg",
41609         "359"
41610       ],
41611       [
41612         "Burkina Faso",
41613         "bf",
41614         "226"
41615       ],
41616       [
41617         "Burundi (Uburundi)",
41618         "bi",
41619         "257"
41620       ],
41621       [
41622         "Cambodia (កម្ពុជា)",
41623         "kh",
41624         "855"
41625       ],
41626       [
41627         "Cameroon (Cameroun)",
41628         "cm",
41629         "237"
41630       ],
41631       [
41632         "Canada",
41633         "ca",
41634         "1",
41635         1,
41636         ["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"]
41637       ],
41638       [
41639         "Cape Verde (Kabu Verdi)",
41640         "cv",
41641         "238"
41642       ],
41643       [
41644         "Caribbean Netherlands",
41645         "bq",
41646         "599",
41647         1
41648       ],
41649       [
41650         "Cayman Islands",
41651         "ky",
41652         "1345"
41653       ],
41654       [
41655         "Central African Republic (République centrafricaine)",
41656         "cf",
41657         "236"
41658       ],
41659       [
41660         "Chad (Tchad)",
41661         "td",
41662         "235"
41663       ],
41664       [
41665         "Chile",
41666         "cl",
41667         "56"
41668       ],
41669       [
41670         "China (中国)",
41671         "cn",
41672         "86"
41673       ],
41674       [
41675         "Christmas Island",
41676         "cx",
41677         "61",
41678         2
41679       ],
41680       [
41681         "Cocos (Keeling) Islands",
41682         "cc",
41683         "61",
41684         1
41685       ],
41686       [
41687         "Colombia",
41688         "co",
41689         "57"
41690       ],
41691       [
41692         "Comoros (‫جزر القمر‬‎)",
41693         "km",
41694         "269"
41695       ],
41696       [
41697         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41698         "cd",
41699         "243"
41700       ],
41701       [
41702         "Congo (Republic) (Congo-Brazzaville)",
41703         "cg",
41704         "242"
41705       ],
41706       [
41707         "Cook Islands",
41708         "ck",
41709         "682"
41710       ],
41711       [
41712         "Costa Rica",
41713         "cr",
41714         "506"
41715       ],
41716       [
41717         "Côte d’Ivoire",
41718         "ci",
41719         "225"
41720       ],
41721       [
41722         "Croatia (Hrvatska)",
41723         "hr",
41724         "385"
41725       ],
41726       [
41727         "Cuba",
41728         "cu",
41729         "53"
41730       ],
41731       [
41732         "Curaçao",
41733         "cw",
41734         "599",
41735         0
41736       ],
41737       [
41738         "Cyprus (Κύπρος)",
41739         "cy",
41740         "357"
41741       ],
41742       [
41743         "Czech Republic (Česká republika)",
41744         "cz",
41745         "420"
41746       ],
41747       [
41748         "Denmark (Danmark)",
41749         "dk",
41750         "45"
41751       ],
41752       [
41753         "Djibouti",
41754         "dj",
41755         "253"
41756       ],
41757       [
41758         "Dominica",
41759         "dm",
41760         "1767"
41761       ],
41762       [
41763         "Dominican Republic (República Dominicana)",
41764         "do",
41765         "1",
41766         2,
41767         ["809", "829", "849"]
41768       ],
41769       [
41770         "Ecuador",
41771         "ec",
41772         "593"
41773       ],
41774       [
41775         "Egypt (‫مصر‬‎)",
41776         "eg",
41777         "20"
41778       ],
41779       [
41780         "El Salvador",
41781         "sv",
41782         "503"
41783       ],
41784       [
41785         "Equatorial Guinea (Guinea Ecuatorial)",
41786         "gq",
41787         "240"
41788       ],
41789       [
41790         "Eritrea",
41791         "er",
41792         "291"
41793       ],
41794       [
41795         "Estonia (Eesti)",
41796         "ee",
41797         "372"
41798       ],
41799       [
41800         "Ethiopia",
41801         "et",
41802         "251"
41803       ],
41804       [
41805         "Falkland Islands (Islas Malvinas)",
41806         "fk",
41807         "500"
41808       ],
41809       [
41810         "Faroe Islands (Føroyar)",
41811         "fo",
41812         "298"
41813       ],
41814       [
41815         "Fiji",
41816         "fj",
41817         "679"
41818       ],
41819       [
41820         "Finland (Suomi)",
41821         "fi",
41822         "358",
41823         0
41824       ],
41825       [
41826         "France",
41827         "fr",
41828         "33"
41829       ],
41830       [
41831         "French Guiana (Guyane française)",
41832         "gf",
41833         "594"
41834       ],
41835       [
41836         "French Polynesia (Polynésie française)",
41837         "pf",
41838         "689"
41839       ],
41840       [
41841         "Gabon",
41842         "ga",
41843         "241"
41844       ],
41845       [
41846         "Gambia",
41847         "gm",
41848         "220"
41849       ],
41850       [
41851         "Georgia (საქართველო)",
41852         "ge",
41853         "995"
41854       ],
41855       [
41856         "Germany (Deutschland)",
41857         "de",
41858         "49"
41859       ],
41860       [
41861         "Ghana (Gaana)",
41862         "gh",
41863         "233"
41864       ],
41865       [
41866         "Gibraltar",
41867         "gi",
41868         "350"
41869       ],
41870       [
41871         "Greece (Ελλάδα)",
41872         "gr",
41873         "30"
41874       ],
41875       [
41876         "Greenland (Kalaallit Nunaat)",
41877         "gl",
41878         "299"
41879       ],
41880       [
41881         "Grenada",
41882         "gd",
41883         "1473"
41884       ],
41885       [
41886         "Guadeloupe",
41887         "gp",
41888         "590",
41889         0
41890       ],
41891       [
41892         "Guam",
41893         "gu",
41894         "1671"
41895       ],
41896       [
41897         "Guatemala",
41898         "gt",
41899         "502"
41900       ],
41901       [
41902         "Guernsey",
41903         "gg",
41904         "44",
41905         1
41906       ],
41907       [
41908         "Guinea (Guinée)",
41909         "gn",
41910         "224"
41911       ],
41912       [
41913         "Guinea-Bissau (Guiné Bissau)",
41914         "gw",
41915         "245"
41916       ],
41917       [
41918         "Guyana",
41919         "gy",
41920         "592"
41921       ],
41922       [
41923         "Haiti",
41924         "ht",
41925         "509"
41926       ],
41927       [
41928         "Honduras",
41929         "hn",
41930         "504"
41931       ],
41932       [
41933         "Hong Kong (香港)",
41934         "hk",
41935         "852"
41936       ],
41937       [
41938         "Hungary (Magyarország)",
41939         "hu",
41940         "36"
41941       ],
41942       [
41943         "Iceland (Ísland)",
41944         "is",
41945         "354"
41946       ],
41947       [
41948         "India (भारत)",
41949         "in",
41950         "91"
41951       ],
41952       [
41953         "Indonesia",
41954         "id",
41955         "62"
41956       ],
41957       [
41958         "Iran (‫ایران‬‎)",
41959         "ir",
41960         "98"
41961       ],
41962       [
41963         "Iraq (‫العراق‬‎)",
41964         "iq",
41965         "964"
41966       ],
41967       [
41968         "Ireland",
41969         "ie",
41970         "353"
41971       ],
41972       [
41973         "Isle of Man",
41974         "im",
41975         "44",
41976         2
41977       ],
41978       [
41979         "Israel (‫ישראל‬‎)",
41980         "il",
41981         "972"
41982       ],
41983       [
41984         "Italy (Italia)",
41985         "it",
41986         "39",
41987         0
41988       ],
41989       [
41990         "Jamaica",
41991         "jm",
41992         "1876"
41993       ],
41994       [
41995         "Japan (日本)",
41996         "jp",
41997         "81"
41998       ],
41999       [
42000         "Jersey",
42001         "je",
42002         "44",
42003         3
42004       ],
42005       [
42006         "Jordan (‫الأردن‬‎)",
42007         "jo",
42008         "962"
42009       ],
42010       [
42011         "Kazakhstan (Казахстан)",
42012         "kz",
42013         "7",
42014         1
42015       ],
42016       [
42017         "Kenya",
42018         "ke",
42019         "254"
42020       ],
42021       [
42022         "Kiribati",
42023         "ki",
42024         "686"
42025       ],
42026       [
42027         "Kosovo",
42028         "xk",
42029         "383"
42030       ],
42031       [
42032         "Kuwait (‫الكويت‬‎)",
42033         "kw",
42034         "965"
42035       ],
42036       [
42037         "Kyrgyzstan (Кыргызстан)",
42038         "kg",
42039         "996"
42040       ],
42041       [
42042         "Laos (ລາວ)",
42043         "la",
42044         "856"
42045       ],
42046       [
42047         "Latvia (Latvija)",
42048         "lv",
42049         "371"
42050       ],
42051       [
42052         "Lebanon (‫لبنان‬‎)",
42053         "lb",
42054         "961"
42055       ],
42056       [
42057         "Lesotho",
42058         "ls",
42059         "266"
42060       ],
42061       [
42062         "Liberia",
42063         "lr",
42064         "231"
42065       ],
42066       [
42067         "Libya (‫ليبيا‬‎)",
42068         "ly",
42069         "218"
42070       ],
42071       [
42072         "Liechtenstein",
42073         "li",
42074         "423"
42075       ],
42076       [
42077         "Lithuania (Lietuva)",
42078         "lt",
42079         "370"
42080       ],
42081       [
42082         "Luxembourg",
42083         "lu",
42084         "352"
42085       ],
42086       [
42087         "Macau (澳門)",
42088         "mo",
42089         "853"
42090       ],
42091       [
42092         "Macedonia (FYROM) (Македонија)",
42093         "mk",
42094         "389"
42095       ],
42096       [
42097         "Madagascar (Madagasikara)",
42098         "mg",
42099         "261"
42100       ],
42101       [
42102         "Malawi",
42103         "mw",
42104         "265"
42105       ],
42106       [
42107         "Malaysia",
42108         "my",
42109         "60"
42110       ],
42111       [
42112         "Maldives",
42113         "mv",
42114         "960"
42115       ],
42116       [
42117         "Mali",
42118         "ml",
42119         "223"
42120       ],
42121       [
42122         "Malta",
42123         "mt",
42124         "356"
42125       ],
42126       [
42127         "Marshall Islands",
42128         "mh",
42129         "692"
42130       ],
42131       [
42132         "Martinique",
42133         "mq",
42134         "596"
42135       ],
42136       [
42137         "Mauritania (‫موريتانيا‬‎)",
42138         "mr",
42139         "222"
42140       ],
42141       [
42142         "Mauritius (Moris)",
42143         "mu",
42144         "230"
42145       ],
42146       [
42147         "Mayotte",
42148         "yt",
42149         "262",
42150         1
42151       ],
42152       [
42153         "Mexico (México)",
42154         "mx",
42155         "52"
42156       ],
42157       [
42158         "Micronesia",
42159         "fm",
42160         "691"
42161       ],
42162       [
42163         "Moldova (Republica Moldova)",
42164         "md",
42165         "373"
42166       ],
42167       [
42168         "Monaco",
42169         "mc",
42170         "377"
42171       ],
42172       [
42173         "Mongolia (Монгол)",
42174         "mn",
42175         "976"
42176       ],
42177       [
42178         "Montenegro (Crna Gora)",
42179         "me",
42180         "382"
42181       ],
42182       [
42183         "Montserrat",
42184         "ms",
42185         "1664"
42186       ],
42187       [
42188         "Morocco (‫المغرب‬‎)",
42189         "ma",
42190         "212",
42191         0
42192       ],
42193       [
42194         "Mozambique (Moçambique)",
42195         "mz",
42196         "258"
42197       ],
42198       [
42199         "Myanmar (Burma) (မြန်မာ)",
42200         "mm",
42201         "95"
42202       ],
42203       [
42204         "Namibia (Namibië)",
42205         "na",
42206         "264"
42207       ],
42208       [
42209         "Nauru",
42210         "nr",
42211         "674"
42212       ],
42213       [
42214         "Nepal (नेपाल)",
42215         "np",
42216         "977"
42217       ],
42218       [
42219         "Netherlands (Nederland)",
42220         "nl",
42221         "31"
42222       ],
42223       [
42224         "New Caledonia (Nouvelle-Calédonie)",
42225         "nc",
42226         "687"
42227       ],
42228       [
42229         "New Zealand",
42230         "nz",
42231         "64"
42232       ],
42233       [
42234         "Nicaragua",
42235         "ni",
42236         "505"
42237       ],
42238       [
42239         "Niger (Nijar)",
42240         "ne",
42241         "227"
42242       ],
42243       [
42244         "Nigeria",
42245         "ng",
42246         "234"
42247       ],
42248       [
42249         "Niue",
42250         "nu",
42251         "683"
42252       ],
42253       [
42254         "Norfolk Island",
42255         "nf",
42256         "672"
42257       ],
42258       [
42259         "North Korea (조선 민주주의 인민 공화국)",
42260         "kp",
42261         "850"
42262       ],
42263       [
42264         "Northern Mariana Islands",
42265         "mp",
42266         "1670"
42267       ],
42268       [
42269         "Norway (Norge)",
42270         "no",
42271         "47",
42272         0
42273       ],
42274       [
42275         "Oman (‫عُمان‬‎)",
42276         "om",
42277         "968"
42278       ],
42279       [
42280         "Pakistan (‫پاکستان‬‎)",
42281         "pk",
42282         "92"
42283       ],
42284       [
42285         "Palau",
42286         "pw",
42287         "680"
42288       ],
42289       [
42290         "Palestine (‫فلسطين‬‎)",
42291         "ps",
42292         "970"
42293       ],
42294       [
42295         "Panama (Panamá)",
42296         "pa",
42297         "507"
42298       ],
42299       [
42300         "Papua New Guinea",
42301         "pg",
42302         "675"
42303       ],
42304       [
42305         "Paraguay",
42306         "py",
42307         "595"
42308       ],
42309       [
42310         "Peru (Perú)",
42311         "pe",
42312         "51"
42313       ],
42314       [
42315         "Philippines",
42316         "ph",
42317         "63"
42318       ],
42319       [
42320         "Poland (Polska)",
42321         "pl",
42322         "48"
42323       ],
42324       [
42325         "Portugal",
42326         "pt",
42327         "351"
42328       ],
42329       [
42330         "Puerto Rico",
42331         "pr",
42332         "1",
42333         3,
42334         ["787", "939"]
42335       ],
42336       [
42337         "Qatar (‫قطر‬‎)",
42338         "qa",
42339         "974"
42340       ],
42341       [
42342         "Réunion (La Réunion)",
42343         "re",
42344         "262",
42345         0
42346       ],
42347       [
42348         "Romania (România)",
42349         "ro",
42350         "40"
42351       ],
42352       [
42353         "Russia (Россия)",
42354         "ru",
42355         "7",
42356         0
42357       ],
42358       [
42359         "Rwanda",
42360         "rw",
42361         "250"
42362       ],
42363       [
42364         "Saint Barthélemy",
42365         "bl",
42366         "590",
42367         1
42368       ],
42369       [
42370         "Saint Helena",
42371         "sh",
42372         "290"
42373       ],
42374       [
42375         "Saint Kitts and Nevis",
42376         "kn",
42377         "1869"
42378       ],
42379       [
42380         "Saint Lucia",
42381         "lc",
42382         "1758"
42383       ],
42384       [
42385         "Saint Martin (Saint-Martin (partie française))",
42386         "mf",
42387         "590",
42388         2
42389       ],
42390       [
42391         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42392         "pm",
42393         "508"
42394       ],
42395       [
42396         "Saint Vincent and the Grenadines",
42397         "vc",
42398         "1784"
42399       ],
42400       [
42401         "Samoa",
42402         "ws",
42403         "685"
42404       ],
42405       [
42406         "San Marino",
42407         "sm",
42408         "378"
42409       ],
42410       [
42411         "São Tomé and Príncipe (São Tomé e Príncipe)",
42412         "st",
42413         "239"
42414       ],
42415       [
42416         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42417         "sa",
42418         "966"
42419       ],
42420       [
42421         "Senegal (Sénégal)",
42422         "sn",
42423         "221"
42424       ],
42425       [
42426         "Serbia (Србија)",
42427         "rs",
42428         "381"
42429       ],
42430       [
42431         "Seychelles",
42432         "sc",
42433         "248"
42434       ],
42435       [
42436         "Sierra Leone",
42437         "sl",
42438         "232"
42439       ],
42440       [
42441         "Singapore",
42442         "sg",
42443         "65"
42444       ],
42445       [
42446         "Sint Maarten",
42447         "sx",
42448         "1721"
42449       ],
42450       [
42451         "Slovakia (Slovensko)",
42452         "sk",
42453         "421"
42454       ],
42455       [
42456         "Slovenia (Slovenija)",
42457         "si",
42458         "386"
42459       ],
42460       [
42461         "Solomon Islands",
42462         "sb",
42463         "677"
42464       ],
42465       [
42466         "Somalia (Soomaaliya)",
42467         "so",
42468         "252"
42469       ],
42470       [
42471         "South Africa",
42472         "za",
42473         "27"
42474       ],
42475       [
42476         "South Korea (대한민국)",
42477         "kr",
42478         "82"
42479       ],
42480       [
42481         "South Sudan (‫جنوب السودان‬‎)",
42482         "ss",
42483         "211"
42484       ],
42485       [
42486         "Spain (España)",
42487         "es",
42488         "34"
42489       ],
42490       [
42491         "Sri Lanka (ශ්‍රී ලංකාව)",
42492         "lk",
42493         "94"
42494       ],
42495       [
42496         "Sudan (‫السودان‬‎)",
42497         "sd",
42498         "249"
42499       ],
42500       [
42501         "Suriname",
42502         "sr",
42503         "597"
42504       ],
42505       [
42506         "Svalbard and Jan Mayen",
42507         "sj",
42508         "47",
42509         1
42510       ],
42511       [
42512         "Swaziland",
42513         "sz",
42514         "268"
42515       ],
42516       [
42517         "Sweden (Sverige)",
42518         "se",
42519         "46"
42520       ],
42521       [
42522         "Switzerland (Schweiz)",
42523         "ch",
42524         "41"
42525       ],
42526       [
42527         "Syria (‫سوريا‬‎)",
42528         "sy",
42529         "963"
42530       ],
42531       [
42532         "Taiwan (台灣)",
42533         "tw",
42534         "886"
42535       ],
42536       [
42537         "Tajikistan",
42538         "tj",
42539         "992"
42540       ],
42541       [
42542         "Tanzania",
42543         "tz",
42544         "255"
42545       ],
42546       [
42547         "Thailand (ไทย)",
42548         "th",
42549         "66"
42550       ],
42551       [
42552         "Timor-Leste",
42553         "tl",
42554         "670"
42555       ],
42556       [
42557         "Togo",
42558         "tg",
42559         "228"
42560       ],
42561       [
42562         "Tokelau",
42563         "tk",
42564         "690"
42565       ],
42566       [
42567         "Tonga",
42568         "to",
42569         "676"
42570       ],
42571       [
42572         "Trinidad and Tobago",
42573         "tt",
42574         "1868"
42575       ],
42576       [
42577         "Tunisia (‫تونس‬‎)",
42578         "tn",
42579         "216"
42580       ],
42581       [
42582         "Turkey (Türkiye)",
42583         "tr",
42584         "90"
42585       ],
42586       [
42587         "Turkmenistan",
42588         "tm",
42589         "993"
42590       ],
42591       [
42592         "Turks and Caicos Islands",
42593         "tc",
42594         "1649"
42595       ],
42596       [
42597         "Tuvalu",
42598         "tv",
42599         "688"
42600       ],
42601       [
42602         "U.S. Virgin Islands",
42603         "vi",
42604         "1340"
42605       ],
42606       [
42607         "Uganda",
42608         "ug",
42609         "256"
42610       ],
42611       [
42612         "Ukraine (Україна)",
42613         "ua",
42614         "380"
42615       ],
42616       [
42617         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42618         "ae",
42619         "971"
42620       ],
42621       [
42622         "United Kingdom",
42623         "gb",
42624         "44",
42625         0
42626       ],
42627       [
42628         "United States",
42629         "us",
42630         "1",
42631         0
42632       ],
42633       [
42634         "Uruguay",
42635         "uy",
42636         "598"
42637       ],
42638       [
42639         "Uzbekistan (Oʻzbekiston)",
42640         "uz",
42641         "998"
42642       ],
42643       [
42644         "Vanuatu",
42645         "vu",
42646         "678"
42647       ],
42648       [
42649         "Vatican City (Città del Vaticano)",
42650         "va",
42651         "39",
42652         1
42653       ],
42654       [
42655         "Venezuela",
42656         "ve",
42657         "58"
42658       ],
42659       [
42660         "Vietnam (Việt Nam)",
42661         "vn",
42662         "84"
42663       ],
42664       [
42665         "Wallis and Futuna (Wallis-et-Futuna)",
42666         "wf",
42667         "681"
42668       ],
42669       [
42670         "Western Sahara (‫الصحراء الغربية‬‎)",
42671         "eh",
42672         "212",
42673         1
42674       ],
42675       [
42676         "Yemen (‫اليمن‬‎)",
42677         "ye",
42678         "967"
42679       ],
42680       [
42681         "Zambia",
42682         "zm",
42683         "260"
42684       ],
42685       [
42686         "Zimbabwe",
42687         "zw",
42688         "263"
42689       ],
42690       [
42691         "Åland Islands",
42692         "ax",
42693         "358",
42694         1
42695       ]
42696   ];
42697   
42698   return d;
42699 }/**
42700 *    This script refer to:
42701 *    Title: International Telephone Input
42702 *    Author: Jack O'Connor
42703 *    Code version:  v12.1.12
42704 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42705 **/
42706
42707 /**
42708  * @class Roo.bootstrap.PhoneInput
42709  * @extends Roo.bootstrap.TriggerField
42710  * An input with International dial-code selection
42711  
42712  * @cfg {String} defaultDialCode default '+852'
42713  * @cfg {Array} preferedCountries default []
42714   
42715  * @constructor
42716  * Create a new PhoneInput.
42717  * @param {Object} config Configuration options
42718  */
42719
42720 Roo.bootstrap.PhoneInput = function(config) {
42721     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42722 };
42723
42724 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42725         
42726         listWidth: undefined,
42727         
42728         selectedClass: 'active',
42729         
42730         invalidClass : "has-warning",
42731         
42732         validClass: 'has-success',
42733         
42734         allowed: '0123456789',
42735         
42736         max_length: 15,
42737         
42738         /**
42739          * @cfg {String} defaultDialCode The default dial code when initializing the input
42740          */
42741         defaultDialCode: '+852',
42742         
42743         /**
42744          * @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
42745          */
42746         preferedCountries: false,
42747         
42748         getAutoCreate : function()
42749         {
42750             var data = Roo.bootstrap.PhoneInputData();
42751             var align = this.labelAlign || this.parentLabelAlign();
42752             var id = Roo.id();
42753             
42754             this.allCountries = [];
42755             this.dialCodeMapping = [];
42756             
42757             for (var i = 0; i < data.length; i++) {
42758               var c = data[i];
42759               this.allCountries[i] = {
42760                 name: c[0],
42761                 iso2: c[1],
42762                 dialCode: c[2],
42763                 priority: c[3] || 0,
42764                 areaCodes: c[4] || null
42765               };
42766               this.dialCodeMapping[c[2]] = {
42767                   name: c[0],
42768                   iso2: c[1],
42769                   priority: c[3] || 0,
42770                   areaCodes: c[4] || null
42771               };
42772             }
42773             
42774             var cfg = {
42775                 cls: 'form-group',
42776                 cn: []
42777             };
42778             
42779             var input =  {
42780                 tag: 'input',
42781                 id : id,
42782                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42783                 maxlength: this.max_length,
42784                 cls : 'form-control tel-input',
42785                 autocomplete: 'new-password'
42786             };
42787             
42788             var hiddenInput = {
42789                 tag: 'input',
42790                 type: 'hidden',
42791                 cls: 'hidden-tel-input'
42792             };
42793             
42794             if (this.name) {
42795                 hiddenInput.name = this.name;
42796             }
42797             
42798             if (this.disabled) {
42799                 input.disabled = true;
42800             }
42801             
42802             var flag_container = {
42803                 tag: 'div',
42804                 cls: 'flag-box',
42805                 cn: [
42806                     {
42807                         tag: 'div',
42808                         cls: 'flag'
42809                     },
42810                     {
42811                         tag: 'div',
42812                         cls: 'caret'
42813                     }
42814                 ]
42815             };
42816             
42817             var box = {
42818                 tag: 'div',
42819                 cls: this.hasFeedback ? 'has-feedback' : '',
42820                 cn: [
42821                     hiddenInput,
42822                     input,
42823                     {
42824                         tag: 'input',
42825                         cls: 'dial-code-holder',
42826                         disabled: true
42827                     }
42828                 ]
42829             };
42830             
42831             var container = {
42832                 cls: 'roo-select2-container input-group',
42833                 cn: [
42834                     flag_container,
42835                     box
42836                 ]
42837             };
42838             
42839             if (this.fieldLabel.length) {
42840                 var indicator = {
42841                     tag: 'i',
42842                     tooltip: 'This field is required'
42843                 };
42844                 
42845                 var label = {
42846                     tag: 'label',
42847                     'for':  id,
42848                     cls: 'control-label',
42849                     cn: []
42850                 };
42851                 
42852                 var label_text = {
42853                     tag: 'span',
42854                     html: this.fieldLabel
42855                 };
42856                 
42857                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42858                 label.cn = [
42859                     indicator,
42860                     label_text
42861                 ];
42862                 
42863                 if(this.indicatorpos == 'right') {
42864                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42865                     label.cn = [
42866                         label_text,
42867                         indicator
42868                     ];
42869                 }
42870                 
42871                 if(align == 'left') {
42872                     container = {
42873                         tag: 'div',
42874                         cn: [
42875                             container
42876                         ]
42877                     };
42878                     
42879                     if(this.labelWidth > 12){
42880                         label.style = "width: " + this.labelWidth + 'px';
42881                     }
42882                     if(this.labelWidth < 13 && this.labelmd == 0){
42883                         this.labelmd = this.labelWidth;
42884                     }
42885                     if(this.labellg > 0){
42886                         label.cls += ' col-lg-' + this.labellg;
42887                         input.cls += ' col-lg-' + (12 - this.labellg);
42888                     }
42889                     if(this.labelmd > 0){
42890                         label.cls += ' col-md-' + this.labelmd;
42891                         container.cls += ' col-md-' + (12 - this.labelmd);
42892                     }
42893                     if(this.labelsm > 0){
42894                         label.cls += ' col-sm-' + this.labelsm;
42895                         container.cls += ' col-sm-' + (12 - this.labelsm);
42896                     }
42897                     if(this.labelxs > 0){
42898                         label.cls += ' col-xs-' + this.labelxs;
42899                         container.cls += ' col-xs-' + (12 - this.labelxs);
42900                     }
42901                 }
42902             }
42903             
42904             cfg.cn = [
42905                 label,
42906                 container
42907             ];
42908             
42909             var settings = this;
42910             
42911             ['xs','sm','md','lg'].map(function(size){
42912                 if (settings[size]) {
42913                     cfg.cls += ' col-' + size + '-' + settings[size];
42914                 }
42915             });
42916             
42917             this.store = new Roo.data.Store({
42918                 proxy : new Roo.data.MemoryProxy({}),
42919                 reader : new Roo.data.JsonReader({
42920                     fields : [
42921                         {
42922                             'name' : 'name',
42923                             'type' : 'string'
42924                         },
42925                         {
42926                             'name' : 'iso2',
42927                             'type' : 'string'
42928                         },
42929                         {
42930                             'name' : 'dialCode',
42931                             'type' : 'string'
42932                         },
42933                         {
42934                             'name' : 'priority',
42935                             'type' : 'string'
42936                         },
42937                         {
42938                             'name' : 'areaCodes',
42939                             'type' : 'string'
42940                         }
42941                     ]
42942                 })
42943             });
42944             
42945             if(!this.preferedCountries) {
42946                 this.preferedCountries = [
42947                     'hk',
42948                     'gb',
42949                     'us'
42950                 ];
42951             }
42952             
42953             var p = this.preferedCountries.reverse();
42954             
42955             if(p) {
42956                 for (var i = 0; i < p.length; i++) {
42957                     for (var j = 0; j < this.allCountries.length; j++) {
42958                         if(this.allCountries[j].iso2 == p[i]) {
42959                             var t = this.allCountries[j];
42960                             this.allCountries.splice(j,1);
42961                             this.allCountries.unshift(t);
42962                         }
42963                     } 
42964                 }
42965             }
42966             
42967             this.store.proxy.data = {
42968                 success: true,
42969                 data: this.allCountries
42970             };
42971             
42972             return cfg;
42973         },
42974         
42975         initEvents : function()
42976         {
42977             this.createList();
42978             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42979             
42980             this.indicator = this.indicatorEl();
42981             this.flag = this.flagEl();
42982             this.dialCodeHolder = this.dialCodeHolderEl();
42983             
42984             this.trigger = this.el.select('div.flag-box',true).first();
42985             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42986             
42987             var _this = this;
42988             
42989             (function(){
42990                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42991                 _this.list.setWidth(lw);
42992             }).defer(100);
42993             
42994             this.list.on('mouseover', this.onViewOver, this);
42995             this.list.on('mousemove', this.onViewMove, this);
42996             this.inputEl().on("keyup", this.onKeyUp, this);
42997             this.inputEl().on("keypress", this.onKeyPress, this);
42998             
42999             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43000
43001             this.view = new Roo.View(this.list, this.tpl, {
43002                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43003             });
43004             
43005             this.view.on('click', this.onViewClick, this);
43006             this.setValue(this.defaultDialCode);
43007         },
43008         
43009         onTriggerClick : function(e)
43010         {
43011             Roo.log('trigger click');
43012             if(this.disabled){
43013                 return;
43014             }
43015             
43016             if(this.isExpanded()){
43017                 this.collapse();
43018                 this.hasFocus = false;
43019             }else {
43020                 this.store.load({});
43021                 this.hasFocus = true;
43022                 this.expand();
43023             }
43024         },
43025         
43026         isExpanded : function()
43027         {
43028             return this.list.isVisible();
43029         },
43030         
43031         collapse : function()
43032         {
43033             if(!this.isExpanded()){
43034                 return;
43035             }
43036             this.list.hide();
43037             Roo.get(document).un('mousedown', this.collapseIf, this);
43038             Roo.get(document).un('mousewheel', this.collapseIf, this);
43039             this.fireEvent('collapse', this);
43040             this.validate();
43041         },
43042         
43043         expand : function()
43044         {
43045             Roo.log('expand');
43046
43047             if(this.isExpanded() || !this.hasFocus){
43048                 return;
43049             }
43050             
43051             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43052             this.list.setWidth(lw);
43053             
43054             this.list.show();
43055             this.restrictHeight();
43056             
43057             Roo.get(document).on('mousedown', this.collapseIf, this);
43058             Roo.get(document).on('mousewheel', this.collapseIf, this);
43059             
43060             this.fireEvent('expand', this);
43061         },
43062         
43063         restrictHeight : function()
43064         {
43065             this.list.alignTo(this.inputEl(), this.listAlign);
43066             this.list.alignTo(this.inputEl(), this.listAlign);
43067         },
43068         
43069         onViewOver : function(e, t)
43070         {
43071             if(this.inKeyMode){
43072                 return;
43073             }
43074             var item = this.view.findItemFromChild(t);
43075             
43076             if(item){
43077                 var index = this.view.indexOf(item);
43078                 this.select(index, false);
43079             }
43080         },
43081
43082         // private
43083         onViewClick : function(view, doFocus, el, e)
43084         {
43085             var index = this.view.getSelectedIndexes()[0];
43086             
43087             var r = this.store.getAt(index);
43088             
43089             if(r){
43090                 this.onSelect(r, index);
43091             }
43092             if(doFocus !== false && !this.blockFocus){
43093                 this.inputEl().focus();
43094             }
43095         },
43096         
43097         onViewMove : function(e, t)
43098         {
43099             this.inKeyMode = false;
43100         },
43101         
43102         select : function(index, scrollIntoView)
43103         {
43104             this.selectedIndex = index;
43105             this.view.select(index);
43106             if(scrollIntoView !== false){
43107                 var el = this.view.getNode(index);
43108                 if(el){
43109                     this.list.scrollChildIntoView(el, false);
43110                 }
43111             }
43112         },
43113         
43114         createList : function()
43115         {
43116             this.list = Roo.get(document.body).createChild({
43117                 tag: 'ul',
43118                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43119                 style: 'display:none'
43120             });
43121             
43122             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43123         },
43124         
43125         collapseIf : function(e)
43126         {
43127             var in_combo  = e.within(this.el);
43128             var in_list =  e.within(this.list);
43129             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43130             
43131             if (in_combo || in_list || is_list) {
43132                 return;
43133             }
43134             this.collapse();
43135         },
43136         
43137         onSelect : function(record, index)
43138         {
43139             if(this.fireEvent('beforeselect', this, record, index) !== false){
43140                 
43141                 this.setFlagClass(record.data.iso2);
43142                 this.setDialCode(record.data.dialCode);
43143                 this.hasFocus = false;
43144                 this.collapse();
43145                 this.fireEvent('select', this, record, index);
43146             }
43147         },
43148         
43149         flagEl : function()
43150         {
43151             var flag = this.el.select('div.flag',true).first();
43152             if(!flag){
43153                 return false;
43154             }
43155             return flag;
43156         },
43157         
43158         dialCodeHolderEl : function()
43159         {
43160             var d = this.el.select('input.dial-code-holder',true).first();
43161             if(!d){
43162                 return false;
43163             }
43164             return d;
43165         },
43166         
43167         setDialCode : function(v)
43168         {
43169             this.dialCodeHolder.dom.value = '+'+v;
43170         },
43171         
43172         setFlagClass : function(n)
43173         {
43174             this.flag.dom.className = 'flag '+n;
43175         },
43176         
43177         getValue : function()
43178         {
43179             var v = this.inputEl().getValue();
43180             if(this.dialCodeHolder) {
43181                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43182             }
43183             return v;
43184         },
43185         
43186         setValue : function(v)
43187         {
43188             var d = this.getDialCode(v);
43189             
43190             //invalid dial code
43191             if(v.length == 0 || !d || d.length == 0) {
43192                 if(this.rendered){
43193                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43194                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43195                 }
43196                 return;
43197             }
43198             
43199             //valid dial code
43200             this.setFlagClass(this.dialCodeMapping[d].iso2);
43201             this.setDialCode(d);
43202             this.inputEl().dom.value = v.replace('+'+d,'');
43203             this.hiddenEl().dom.value = this.getValue();
43204             
43205             this.validate();
43206         },
43207         
43208         getDialCode : function(v)
43209         {
43210             v = v ||  '';
43211             
43212             if (v.length == 0) {
43213                 return this.dialCodeHolder.dom.value;
43214             }
43215             
43216             var dialCode = "";
43217             if (v.charAt(0) != "+") {
43218                 return false;
43219             }
43220             var numericChars = "";
43221             for (var i = 1; i < v.length; i++) {
43222               var c = v.charAt(i);
43223               if (!isNaN(c)) {
43224                 numericChars += c;
43225                 if (this.dialCodeMapping[numericChars]) {
43226                   dialCode = v.substr(1, i);
43227                 }
43228                 if (numericChars.length == 4) {
43229                   break;
43230                 }
43231               }
43232             }
43233             return dialCode;
43234         },
43235         
43236         reset : function()
43237         {
43238             this.setValue(this.defaultDialCode);
43239             this.validate();
43240         },
43241         
43242         hiddenEl : function()
43243         {
43244             return this.el.select('input.hidden-tel-input',true).first();
43245         },
43246         
43247         // after setting val
43248         onKeyUp : function(e){
43249             this.setValue(this.getValue());
43250         },
43251         
43252         onKeyPress : function(e){
43253             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43254                 e.stopEvent();
43255             }
43256         }
43257         
43258 });
43259 /**
43260  * @class Roo.bootstrap.MoneyField
43261  * @extends Roo.bootstrap.ComboBox
43262  * Bootstrap MoneyField class
43263  * 
43264  * @constructor
43265  * Create a new MoneyField.
43266  * @param {Object} config Configuration options
43267  */
43268
43269 Roo.bootstrap.MoneyField = function(config) {
43270     
43271     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43272     
43273 };
43274
43275 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43276     
43277     /**
43278      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43279      */
43280     allowDecimals : true,
43281     /**
43282      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43283      */
43284     decimalSeparator : ".",
43285     /**
43286      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43287      */
43288     decimalPrecision : 0,
43289     /**
43290      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43291      */
43292     allowNegative : true,
43293     /**
43294      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43295      */
43296     allowZero: true,
43297     /**
43298      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43299      */
43300     minValue : Number.NEGATIVE_INFINITY,
43301     /**
43302      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43303      */
43304     maxValue : Number.MAX_VALUE,
43305     /**
43306      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43307      */
43308     minText : "The minimum value for this field is {0}",
43309     /**
43310      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43311      */
43312     maxText : "The maximum value for this field is {0}",
43313     /**
43314      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43315      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43316      */
43317     nanText : "{0} is not a valid number",
43318     /**
43319      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43320      */
43321     castInt : true,
43322     /**
43323      * @cfg {String} defaults currency of the MoneyField
43324      * value should be in lkey
43325      */
43326     defaultCurrency : false,
43327     /**
43328      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43329      */
43330     thousandsDelimiter : false,
43331     /**
43332      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43333      */
43334     max_length: false,
43335     
43336     inputlg : 9,
43337     inputmd : 9,
43338     inputsm : 9,
43339     inputxs : 6,
43340     
43341     store : false,
43342     
43343     getAutoCreate : function()
43344     {
43345         var align = this.labelAlign || this.parentLabelAlign();
43346         
43347         var id = Roo.id();
43348
43349         var cfg = {
43350             cls: 'form-group',
43351             cn: []
43352         };
43353
43354         var input =  {
43355             tag: 'input',
43356             id : id,
43357             cls : 'form-control roo-money-amount-input',
43358             autocomplete: 'new-password'
43359         };
43360         
43361         var hiddenInput = {
43362             tag: 'input',
43363             type: 'hidden',
43364             id: Roo.id(),
43365             cls: 'hidden-number-input'
43366         };
43367         
43368         if(this.max_length) {
43369             input.maxlength = this.max_length; 
43370         }
43371         
43372         if (this.name) {
43373             hiddenInput.name = this.name;
43374         }
43375
43376         if (this.disabled) {
43377             input.disabled = true;
43378         }
43379
43380         var clg = 12 - this.inputlg;
43381         var cmd = 12 - this.inputmd;
43382         var csm = 12 - this.inputsm;
43383         var cxs = 12 - this.inputxs;
43384         
43385         var container = {
43386             tag : 'div',
43387             cls : 'row roo-money-field',
43388             cn : [
43389                 {
43390                     tag : 'div',
43391                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43392                     cn : [
43393                         {
43394                             tag : 'div',
43395                             cls: 'roo-select2-container input-group',
43396                             cn: [
43397                                 {
43398                                     tag : 'input',
43399                                     cls : 'form-control roo-money-currency-input',
43400                                     autocomplete: 'new-password',
43401                                     readOnly : 1,
43402                                     name : this.currencyName
43403                                 },
43404                                 {
43405                                     tag :'span',
43406                                     cls : 'input-group-addon',
43407                                     cn : [
43408                                         {
43409                                             tag: 'span',
43410                                             cls: 'caret'
43411                                         }
43412                                     ]
43413                                 }
43414                             ]
43415                         }
43416                     ]
43417                 },
43418                 {
43419                     tag : 'div',
43420                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43421                     cn : [
43422                         {
43423                             tag: 'div',
43424                             cls: this.hasFeedback ? 'has-feedback' : '',
43425                             cn: [
43426                                 input
43427                             ]
43428                         }
43429                     ]
43430                 }
43431             ]
43432             
43433         };
43434         
43435         if (this.fieldLabel.length) {
43436             var indicator = {
43437                 tag: 'i',
43438                 tooltip: 'This field is required'
43439             };
43440
43441             var label = {
43442                 tag: 'label',
43443                 'for':  id,
43444                 cls: 'control-label',
43445                 cn: []
43446             };
43447
43448             var label_text = {
43449                 tag: 'span',
43450                 html: this.fieldLabel
43451             };
43452
43453             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43454             label.cn = [
43455                 indicator,
43456                 label_text
43457             ];
43458
43459             if(this.indicatorpos == 'right') {
43460                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43461                 label.cn = [
43462                     label_text,
43463                     indicator
43464                 ];
43465             }
43466
43467             if(align == 'left') {
43468                 container = {
43469                     tag: 'div',
43470                     cn: [
43471                         container
43472                     ]
43473                 };
43474
43475                 if(this.labelWidth > 12){
43476                     label.style = "width: " + this.labelWidth + 'px';
43477                 }
43478                 if(this.labelWidth < 13 && this.labelmd == 0){
43479                     this.labelmd = this.labelWidth;
43480                 }
43481                 if(this.labellg > 0){
43482                     label.cls += ' col-lg-' + this.labellg;
43483                     input.cls += ' col-lg-' + (12 - this.labellg);
43484                 }
43485                 if(this.labelmd > 0){
43486                     label.cls += ' col-md-' + this.labelmd;
43487                     container.cls += ' col-md-' + (12 - this.labelmd);
43488                 }
43489                 if(this.labelsm > 0){
43490                     label.cls += ' col-sm-' + this.labelsm;
43491                     container.cls += ' col-sm-' + (12 - this.labelsm);
43492                 }
43493                 if(this.labelxs > 0){
43494                     label.cls += ' col-xs-' + this.labelxs;
43495                     container.cls += ' col-xs-' + (12 - this.labelxs);
43496                 }
43497             }
43498         }
43499
43500         cfg.cn = [
43501             label,
43502             container,
43503             hiddenInput
43504         ];
43505         
43506         var settings = this;
43507
43508         ['xs','sm','md','lg'].map(function(size){
43509             if (settings[size]) {
43510                 cfg.cls += ' col-' + size + '-' + settings[size];
43511             }
43512         });
43513         
43514         return cfg;
43515     },
43516     
43517     initEvents : function()
43518     {
43519         this.indicator = this.indicatorEl();
43520         
43521         this.initCurrencyEvent();
43522         
43523         this.initNumberEvent();
43524     },
43525     
43526     initCurrencyEvent : function()
43527     {
43528         if (!this.store) {
43529             throw "can not find store for combo";
43530         }
43531         
43532         this.store = Roo.factory(this.store, Roo.data);
43533         this.store.parent = this;
43534         
43535         this.createList();
43536         
43537         this.triggerEl = this.el.select('.input-group-addon', true).first();
43538         
43539         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43540         
43541         var _this = this;
43542         
43543         (function(){
43544             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43545             _this.list.setWidth(lw);
43546         }).defer(100);
43547         
43548         this.list.on('mouseover', this.onViewOver, this);
43549         this.list.on('mousemove', this.onViewMove, this);
43550         this.list.on('scroll', this.onViewScroll, this);
43551         
43552         if(!this.tpl){
43553             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43554         }
43555         
43556         this.view = new Roo.View(this.list, this.tpl, {
43557             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43558         });
43559         
43560         this.view.on('click', this.onViewClick, this);
43561         
43562         this.store.on('beforeload', this.onBeforeLoad, this);
43563         this.store.on('load', this.onLoad, this);
43564         this.store.on('loadexception', this.onLoadException, this);
43565         
43566         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43567             "up" : function(e){
43568                 this.inKeyMode = true;
43569                 this.selectPrev();
43570             },
43571
43572             "down" : function(e){
43573                 if(!this.isExpanded()){
43574                     this.onTriggerClick();
43575                 }else{
43576                     this.inKeyMode = true;
43577                     this.selectNext();
43578                 }
43579             },
43580
43581             "enter" : function(e){
43582                 this.collapse();
43583                 
43584                 if(this.fireEvent("specialkey", this, e)){
43585                     this.onViewClick(false);
43586                 }
43587                 
43588                 return true;
43589             },
43590
43591             "esc" : function(e){
43592                 this.collapse();
43593             },
43594
43595             "tab" : function(e){
43596                 this.collapse();
43597                 
43598                 if(this.fireEvent("specialkey", this, e)){
43599                     this.onViewClick(false);
43600                 }
43601                 
43602                 return true;
43603             },
43604
43605             scope : this,
43606
43607             doRelay : function(foo, bar, hname){
43608                 if(hname == 'down' || this.scope.isExpanded()){
43609                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43610                 }
43611                 return true;
43612             },
43613
43614             forceKeyDown: true
43615         });
43616         
43617         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43618         
43619     },
43620     
43621     initNumberEvent : function(e)
43622     {
43623         this.inputEl().on("keydown" , this.fireKey,  this);
43624         this.inputEl().on("focus", this.onFocus,  this);
43625         this.inputEl().on("blur", this.onBlur,  this);
43626         
43627         this.inputEl().relayEvent('keyup', this);
43628         
43629         if(this.indicator){
43630             this.indicator.addClass('invisible');
43631         }
43632  
43633         this.originalValue = this.getValue();
43634         
43635         if(this.validationEvent == 'keyup'){
43636             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43637             this.inputEl().on('keyup', this.filterValidation, this);
43638         }
43639         else if(this.validationEvent !== false){
43640             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43641         }
43642         
43643         if(this.selectOnFocus){
43644             this.on("focus", this.preFocus, this);
43645             
43646         }
43647         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43648             this.inputEl().on("keypress", this.filterKeys, this);
43649         } else {
43650             this.inputEl().relayEvent('keypress', this);
43651         }
43652         
43653         var allowed = "0123456789";
43654         
43655         if(this.allowDecimals){
43656             allowed += this.decimalSeparator;
43657         }
43658         
43659         if(this.allowNegative){
43660             allowed += "-";
43661         }
43662         
43663         if(this.thousandsDelimiter) {
43664             allowed += ",";
43665         }
43666         
43667         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43668         
43669         var keyPress = function(e){
43670             
43671             var k = e.getKey();
43672             
43673             var c = e.getCharCode();
43674             
43675             if(
43676                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43677                     allowed.indexOf(String.fromCharCode(c)) === -1
43678             ){
43679                 e.stopEvent();
43680                 return;
43681             }
43682             
43683             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43684                 return;
43685             }
43686             
43687             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43688                 e.stopEvent();
43689             }
43690         };
43691         
43692         this.inputEl().on("keypress", keyPress, this);
43693         
43694     },
43695     
43696     onTriggerClick : function(e)
43697     {   
43698         if(this.disabled){
43699             return;
43700         }
43701         
43702         this.page = 0;
43703         this.loadNext = false;
43704         
43705         if(this.isExpanded()){
43706             this.collapse();
43707             return;
43708         }
43709         
43710         this.hasFocus = true;
43711         
43712         if(this.triggerAction == 'all') {
43713             this.doQuery(this.allQuery, true);
43714             return;
43715         }
43716         
43717         this.doQuery(this.getRawValue());
43718     },
43719     
43720     getCurrency : function()
43721     {   
43722         var v = this.currencyEl().getValue();
43723         
43724         return v;
43725     },
43726     
43727     restrictHeight : function()
43728     {
43729         this.list.alignTo(this.currencyEl(), this.listAlign);
43730         this.list.alignTo(this.currencyEl(), this.listAlign);
43731     },
43732     
43733     onViewClick : function(view, doFocus, el, e)
43734     {
43735         var index = this.view.getSelectedIndexes()[0];
43736         
43737         var r = this.store.getAt(index);
43738         
43739         if(r){
43740             this.onSelect(r, index);
43741         }
43742     },
43743     
43744     onSelect : function(record, index){
43745         
43746         if(this.fireEvent('beforeselect', this, record, index) !== false){
43747         
43748             this.setFromCurrencyData(index > -1 ? record.data : false);
43749             
43750             this.collapse();
43751             
43752             this.fireEvent('select', this, record, index);
43753         }
43754     },
43755     
43756     setFromCurrencyData : function(o)
43757     {
43758         var currency = '';
43759         
43760         this.lastCurrency = o;
43761         
43762         if (this.currencyField) {
43763             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43764         } else {
43765             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43766         }
43767         
43768         this.lastSelectionText = currency;
43769         
43770         //setting default currency
43771         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43772             this.setCurrency(this.defaultCurrency);
43773             return;
43774         }
43775         
43776         this.setCurrency(currency);
43777     },
43778     
43779     setFromData : function(o)
43780     {
43781         var c = {};
43782         
43783         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43784         
43785         this.setFromCurrencyData(c);
43786         
43787         var value = '';
43788         
43789         if (this.name) {
43790             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43791         } else {
43792             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43793         }
43794         
43795         this.setValue(value);
43796         
43797     },
43798     
43799     setCurrency : function(v)
43800     {   
43801         this.currencyValue = v;
43802         
43803         if(this.rendered){
43804             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43805             this.validate();
43806         }
43807     },
43808     
43809     setValue : function(v)
43810     {
43811         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43812         
43813         this.value = v;
43814         
43815         if(this.rendered){
43816             
43817             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43818             
43819             this.inputEl().dom.value = (v == '') ? '' :
43820                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43821             
43822             if(!this.allowZero && v === '0') {
43823                 this.hiddenEl().dom.value = '';
43824                 this.inputEl().dom.value = '';
43825             }
43826             
43827             this.validate();
43828         }
43829     },
43830     
43831     getRawValue : function()
43832     {
43833         var v = this.inputEl().getValue();
43834         
43835         return v;
43836     },
43837     
43838     getValue : function()
43839     {
43840         return this.fixPrecision(this.parseValue(this.getRawValue()));
43841     },
43842     
43843     parseValue : function(value)
43844     {
43845         if(this.thousandsDelimiter) {
43846             value += "";
43847             r = new RegExp(",", "g");
43848             value = value.replace(r, "");
43849         }
43850         
43851         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43852         return isNaN(value) ? '' : value;
43853         
43854     },
43855     
43856     fixPrecision : function(value)
43857     {
43858         if(this.thousandsDelimiter) {
43859             value += "";
43860             r = new RegExp(",", "g");
43861             value = value.replace(r, "");
43862         }
43863         
43864         var nan = isNaN(value);
43865         
43866         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43867             return nan ? '' : value;
43868         }
43869         return parseFloat(value).toFixed(this.decimalPrecision);
43870     },
43871     
43872     decimalPrecisionFcn : function(v)
43873     {
43874         return Math.floor(v);
43875     },
43876     
43877     validateValue : function(value)
43878     {
43879         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43880             return false;
43881         }
43882         
43883         var num = this.parseValue(value);
43884         
43885         if(isNaN(num)){
43886             this.markInvalid(String.format(this.nanText, value));
43887             return false;
43888         }
43889         
43890         if(num < this.minValue){
43891             this.markInvalid(String.format(this.minText, this.minValue));
43892             return false;
43893         }
43894         
43895         if(num > this.maxValue){
43896             this.markInvalid(String.format(this.maxText, this.maxValue));
43897             return false;
43898         }
43899         
43900         return true;
43901     },
43902     
43903     validate : function()
43904     {
43905         if(this.disabled || this.allowBlank){
43906             this.markValid();
43907             return true;
43908         }
43909         
43910         var currency = this.getCurrency();
43911         
43912         if(this.validateValue(this.getRawValue()) && currency.length){
43913             this.markValid();
43914             return true;
43915         }
43916         
43917         this.markInvalid();
43918         return false;
43919     },
43920     
43921     getName: function()
43922     {
43923         return this.name;
43924     },
43925     
43926     beforeBlur : function()
43927     {
43928         if(!this.castInt){
43929             return;
43930         }
43931         
43932         var v = this.parseValue(this.getRawValue());
43933         
43934         if(v || v == 0){
43935             this.setValue(v);
43936         }
43937     },
43938     
43939     onBlur : function()
43940     {
43941         this.beforeBlur();
43942         
43943         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43944             //this.el.removeClass(this.focusClass);
43945         }
43946         
43947         this.hasFocus = false;
43948         
43949         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43950             this.validate();
43951         }
43952         
43953         var v = this.getValue();
43954         
43955         if(String(v) !== String(this.startValue)){
43956             this.fireEvent('change', this, v, this.startValue);
43957         }
43958         
43959         this.fireEvent("blur", this);
43960     },
43961     
43962     inputEl : function()
43963     {
43964         return this.el.select('.roo-money-amount-input', true).first();
43965     },
43966     
43967     currencyEl : function()
43968     {
43969         return this.el.select('.roo-money-currency-input', true).first();
43970     },
43971     
43972     hiddenEl : function()
43973     {
43974         return this.el.select('input.hidden-number-input',true).first();
43975     }
43976     
43977 });/**
43978  * @class Roo.bootstrap.BezierSignature
43979  * @extends Roo.bootstrap.Component
43980  * Bootstrap BezierSignature class
43981  * This script refer to:
43982  *    Title: Signature Pad
43983  *    Author: szimek
43984  *    Availability: https://github.com/szimek/signature_pad
43985  *
43986  * @constructor
43987  * Create a new BezierSignature
43988  * @param {Object} config The config object
43989  */
43990
43991 Roo.bootstrap.BezierSignature = function(config){
43992     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43993     this.addEvents({
43994         "resize" : true
43995     });
43996 };
43997
43998 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43999 {
44000      
44001     curve_data: [],
44002     
44003     is_empty: true,
44004     
44005     mouse_btn_down: true,
44006     
44007     /**
44008      * @cfg {int} canvas height
44009      */
44010     canvas_height: '200px',
44011     
44012     /**
44013      * @cfg {float|function} Radius of a single dot.
44014      */ 
44015     dot_size: false,
44016     
44017     /**
44018      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44019      */
44020     min_width: 0.5,
44021     
44022     /**
44023      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44024      */
44025     max_width: 2.5,
44026     
44027     /**
44028      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44029      */
44030     throttle: 16,
44031     
44032     /**
44033      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44034      */
44035     min_distance: 5,
44036     
44037     /**
44038      * @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.
44039      */
44040     bg_color: 'rgba(0, 0, 0, 0)',
44041     
44042     /**
44043      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44044      */
44045     dot_color: 'black',
44046     
44047     /**
44048      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44049      */ 
44050     velocity_filter_weight: 0.7,
44051     
44052     /**
44053      * @cfg {function} Callback when stroke begin. 
44054      */
44055     onBegin: false,
44056     
44057     /**
44058      * @cfg {function} Callback when stroke end.
44059      */
44060     onEnd: false,
44061     
44062     getAutoCreate : function()
44063     {
44064         var cls = 'roo-signature column';
44065         
44066         if(this.cls){
44067             cls += ' ' + this.cls;
44068         }
44069         
44070         var col_sizes = [
44071             'lg',
44072             'md',
44073             'sm',
44074             'xs'
44075         ];
44076         
44077         for(var i = 0; i < col_sizes.length; i++) {
44078             if(this[col_sizes[i]]) {
44079                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44080             }
44081         }
44082         
44083         var cfg = {
44084             tag: 'div',
44085             cls: cls,
44086             cn: [
44087                 {
44088                     tag: 'div',
44089                     cls: 'roo-signature-body',
44090                     cn: [
44091                         {
44092                             tag: 'canvas',
44093                             cls: 'roo-signature-body-canvas',
44094                             height: this.canvas_height,
44095                             width: this.canvas_width
44096                         }
44097                     ]
44098                 },
44099                 {
44100                     tag: 'input',
44101                     type: 'file',
44102                     style: 'display: none'
44103                 }
44104             ]
44105         };
44106         
44107         return cfg;
44108     },
44109     
44110     initEvents: function() 
44111     {
44112         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44113         
44114         var canvas = this.canvasEl();
44115         
44116         // mouse && touch event swapping...
44117         canvas.dom.style.touchAction = 'none';
44118         canvas.dom.style.msTouchAction = 'none';
44119         
44120         this.mouse_btn_down = false;
44121         canvas.on('mousedown', this._handleMouseDown, this);
44122         canvas.on('mousemove', this._handleMouseMove, this);
44123         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44124         
44125         if (window.PointerEvent) {
44126             canvas.on('pointerdown', this._handleMouseDown, this);
44127             canvas.on('pointermove', this._handleMouseMove, this);
44128             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44129         }
44130         
44131         if ('ontouchstart' in window) {
44132             canvas.on('touchstart', this._handleTouchStart, this);
44133             canvas.on('touchmove', this._handleTouchMove, this);
44134             canvas.on('touchend', this._handleTouchEnd, this);
44135         }
44136         
44137         Roo.EventManager.onWindowResize(this.resize, this, true);
44138         
44139         // file input event
44140         this.fileEl().on('change', this.uploadImage, this);
44141         
44142         this.clear();
44143         
44144         this.resize();
44145     },
44146     
44147     resize: function(){
44148         
44149         var canvas = this.canvasEl().dom;
44150         var ctx = this.canvasElCtx();
44151         var img_data = false;
44152         
44153         if(canvas.width > 0) {
44154             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44155         }
44156         // setting canvas width will clean img data
44157         canvas.width = 0;
44158         
44159         var style = window.getComputedStyle ? 
44160             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44161             
44162         var padding_left = parseInt(style.paddingLeft) || 0;
44163         var padding_right = parseInt(style.paddingRight) || 0;
44164         
44165         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44166         
44167         if(img_data) {
44168             ctx.putImageData(img_data, 0, 0);
44169         }
44170     },
44171     
44172     _handleMouseDown: function(e)
44173     {
44174         if (e.browserEvent.which === 1) {
44175             this.mouse_btn_down = true;
44176             this.strokeBegin(e);
44177         }
44178     },
44179     
44180     _handleMouseMove: function (e)
44181     {
44182         if (this.mouse_btn_down) {
44183             this.strokeMoveUpdate(e);
44184         }
44185     },
44186     
44187     _handleMouseUp: function (e)
44188     {
44189         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44190             this.mouse_btn_down = false;
44191             this.strokeEnd(e);
44192         }
44193     },
44194     
44195     _handleTouchStart: function (e) {
44196         
44197         e.preventDefault();
44198         if (e.browserEvent.targetTouches.length === 1) {
44199             // var touch = e.browserEvent.changedTouches[0];
44200             // this.strokeBegin(touch);
44201             
44202              this.strokeBegin(e); // assume e catching the correct xy...
44203         }
44204     },
44205     
44206     _handleTouchMove: function (e) {
44207         e.preventDefault();
44208         // var touch = event.targetTouches[0];
44209         // _this._strokeMoveUpdate(touch);
44210         this.strokeMoveUpdate(e);
44211     },
44212     
44213     _handleTouchEnd: function (e) {
44214         var wasCanvasTouched = e.target === this.canvasEl().dom;
44215         if (wasCanvasTouched) {
44216             e.preventDefault();
44217             // var touch = event.changedTouches[0];
44218             // _this._strokeEnd(touch);
44219             this.strokeEnd(e);
44220         }
44221     },
44222     
44223     reset: function () {
44224         this._lastPoints = [];
44225         this._lastVelocity = 0;
44226         this._lastWidth = (this.min_width + this.max_width) / 2;
44227         this.canvasElCtx().fillStyle = this.dot_color;
44228     },
44229     
44230     strokeMoveUpdate: function(e)
44231     {
44232         this.strokeUpdate(e);
44233         
44234         if (this.throttle) {
44235             this.throttleStroke(this.strokeUpdate, this.throttle);
44236         }
44237         else {
44238             this.strokeUpdate(e);
44239         }
44240     },
44241     
44242     strokeBegin: function(e)
44243     {
44244         var newPointGroup = {
44245             color: this.dot_color,
44246             points: []
44247         };
44248         
44249         if (typeof this.onBegin === 'function') {
44250             this.onBegin(e);
44251         }
44252         
44253         this.curve_data.push(newPointGroup);
44254         this.reset();
44255         this.strokeUpdate(e);
44256     },
44257     
44258     strokeUpdate: function(e)
44259     {
44260         var rect = this.canvasEl().dom.getBoundingClientRect();
44261         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44262         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44263         var lastPoints = lastPointGroup.points;
44264         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44265         var isLastPointTooClose = lastPoint
44266             ? point.distanceTo(lastPoint) <= this.min_distance
44267             : false;
44268         var color = lastPointGroup.color;
44269         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44270             var curve = this.addPoint(point);
44271             if (!lastPoint) {
44272                 this.drawDot({color: color, point: point});
44273             }
44274             else if (curve) {
44275                 this.drawCurve({color: color, curve: curve});
44276             }
44277             lastPoints.push({
44278                 time: point.time,
44279                 x: point.x,
44280                 y: point.y
44281             });
44282         }
44283     },
44284     
44285     strokeEnd: function(e)
44286     {
44287         this.strokeUpdate(e);
44288         if (typeof this.onEnd === 'function') {
44289             this.onEnd(e);
44290         }
44291     },
44292     
44293     addPoint:  function (point) {
44294         var _lastPoints = this._lastPoints;
44295         _lastPoints.push(point);
44296         if (_lastPoints.length > 2) {
44297             if (_lastPoints.length === 3) {
44298                 _lastPoints.unshift(_lastPoints[0]);
44299             }
44300             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44301             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44302             _lastPoints.shift();
44303             return curve;
44304         }
44305         return null;
44306     },
44307     
44308     calculateCurveWidths: function (startPoint, endPoint) {
44309         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44310             (1 - this.velocity_filter_weight) * this._lastVelocity;
44311
44312         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44313         var widths = {
44314             end: newWidth,
44315             start: this._lastWidth
44316         };
44317         
44318         this._lastVelocity = velocity;
44319         this._lastWidth = newWidth;
44320         return widths;
44321     },
44322     
44323     drawDot: function (_a) {
44324         var color = _a.color, point = _a.point;
44325         var ctx = this.canvasElCtx();
44326         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44327         ctx.beginPath();
44328         this.drawCurveSegment(point.x, point.y, width);
44329         ctx.closePath();
44330         ctx.fillStyle = color;
44331         ctx.fill();
44332     },
44333     
44334     drawCurve: function (_a) {
44335         var color = _a.color, curve = _a.curve;
44336         var ctx = this.canvasElCtx();
44337         var widthDelta = curve.endWidth - curve.startWidth;
44338         var drawSteps = Math.floor(curve.length()) * 2;
44339         ctx.beginPath();
44340         ctx.fillStyle = color;
44341         for (var i = 0; i < drawSteps; i += 1) {
44342         var t = i / drawSteps;
44343         var tt = t * t;
44344         var ttt = tt * t;
44345         var u = 1 - t;
44346         var uu = u * u;
44347         var uuu = uu * u;
44348         var x = uuu * curve.startPoint.x;
44349         x += 3 * uu * t * curve.control1.x;
44350         x += 3 * u * tt * curve.control2.x;
44351         x += ttt * curve.endPoint.x;
44352         var y = uuu * curve.startPoint.y;
44353         y += 3 * uu * t * curve.control1.y;
44354         y += 3 * u * tt * curve.control2.y;
44355         y += ttt * curve.endPoint.y;
44356         var width = curve.startWidth + ttt * widthDelta;
44357         this.drawCurveSegment(x, y, width);
44358         }
44359         ctx.closePath();
44360         ctx.fill();
44361     },
44362     
44363     drawCurveSegment: function (x, y, width) {
44364         var ctx = this.canvasElCtx();
44365         ctx.moveTo(x, y);
44366         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44367         this.is_empty = false;
44368     },
44369     
44370     clear: function()
44371     {
44372         var ctx = this.canvasElCtx();
44373         var canvas = this.canvasEl().dom;
44374         ctx.fillStyle = this.bg_color;
44375         ctx.clearRect(0, 0, canvas.width, canvas.height);
44376         ctx.fillRect(0, 0, canvas.width, canvas.height);
44377         this.curve_data = [];
44378         this.reset();
44379         this.is_empty = true;
44380     },
44381     
44382     fileEl: function()
44383     {
44384         return  this.el.select('input',true).first();
44385     },
44386     
44387     canvasEl: function()
44388     {
44389         return this.el.select('canvas',true).first();
44390     },
44391     
44392     canvasElCtx: function()
44393     {
44394         return this.el.select('canvas',true).first().dom.getContext('2d');
44395     },
44396     
44397     getImage: function(type)
44398     {
44399         if(this.is_empty) {
44400             return false;
44401         }
44402         
44403         // encryption ?
44404         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44405     },
44406     
44407     drawFromImage: function(img_src)
44408     {
44409         var img = new Image();
44410         
44411         img.onload = function(){
44412             this.canvasElCtx().drawImage(img, 0, 0);
44413         }.bind(this);
44414         
44415         img.src = img_src;
44416         
44417         this.is_empty = false;
44418     },
44419     
44420     selectImage: function()
44421     {
44422         this.fileEl().dom.click();
44423     },
44424     
44425     uploadImage: function(e)
44426     {
44427         var reader = new FileReader();
44428         
44429         reader.onload = function(e){
44430             var img = new Image();
44431             img.onload = function(){
44432                 this.reset();
44433                 this.canvasElCtx().drawImage(img, 0, 0);
44434             }.bind(this);
44435             img.src = e.target.result;
44436         }.bind(this);
44437         
44438         reader.readAsDataURL(e.target.files[0]);
44439     },
44440     
44441     // Bezier Point Constructor
44442     Point: (function () {
44443         function Point(x, y, time) {
44444             this.x = x;
44445             this.y = y;
44446             this.time = time || Date.now();
44447         }
44448         Point.prototype.distanceTo = function (start) {
44449             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44450         };
44451         Point.prototype.equals = function (other) {
44452             return this.x === other.x && this.y === other.y && this.time === other.time;
44453         };
44454         Point.prototype.velocityFrom = function (start) {
44455             return this.time !== start.time
44456             ? this.distanceTo(start) / (this.time - start.time)
44457             : 0;
44458         };
44459         return Point;
44460     }()),
44461     
44462     
44463     // Bezier Constructor
44464     Bezier: (function () {
44465         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44466             this.startPoint = startPoint;
44467             this.control2 = control2;
44468             this.control1 = control1;
44469             this.endPoint = endPoint;
44470             this.startWidth = startWidth;
44471             this.endWidth = endWidth;
44472         }
44473         Bezier.fromPoints = function (points, widths, scope) {
44474             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44475             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44476             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44477         };
44478         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44479             var dx1 = s1.x - s2.x;
44480             var dy1 = s1.y - s2.y;
44481             var dx2 = s2.x - s3.x;
44482             var dy2 = s2.y - s3.y;
44483             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44484             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44485             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44486             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44487             var dxm = m1.x - m2.x;
44488             var dym = m1.y - m2.y;
44489             var k = l2 / (l1 + l2);
44490             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44491             var tx = s2.x - cm.x;
44492             var ty = s2.y - cm.y;
44493             return {
44494                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44495                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44496             };
44497         };
44498         Bezier.prototype.length = function () {
44499             var steps = 10;
44500             var length = 0;
44501             var px;
44502             var py;
44503             for (var i = 0; i <= steps; i += 1) {
44504                 var t = i / steps;
44505                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44506                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44507                 if (i > 0) {
44508                     var xdiff = cx - px;
44509                     var ydiff = cy - py;
44510                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44511                 }
44512                 px = cx;
44513                 py = cy;
44514             }
44515             return length;
44516         };
44517         Bezier.prototype.point = function (t, start, c1, c2, end) {
44518             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44519             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44520             + (3.0 * c2 * (1.0 - t) * t * t)
44521             + (end * t * t * t);
44522         };
44523         return Bezier;
44524     }()),
44525     
44526     throttleStroke: function(fn, wait) {
44527       if (wait === void 0) { wait = 250; }
44528       var previous = 0;
44529       var timeout = null;
44530       var result;
44531       var storedContext;
44532       var storedArgs;
44533       var later = function () {
44534           previous = Date.now();
44535           timeout = null;
44536           result = fn.apply(storedContext, storedArgs);
44537           if (!timeout) {
44538               storedContext = null;
44539               storedArgs = [];
44540           }
44541       };
44542       return function wrapper() {
44543           var args = [];
44544           for (var _i = 0; _i < arguments.length; _i++) {
44545               args[_i] = arguments[_i];
44546           }
44547           var now = Date.now();
44548           var remaining = wait - (now - previous);
44549           storedContext = this;
44550           storedArgs = args;
44551           if (remaining <= 0 || remaining > wait) {
44552               if (timeout) {
44553                   clearTimeout(timeout);
44554                   timeout = null;
44555               }
44556               previous = now;
44557               result = fn.apply(storedContext, storedArgs);
44558               if (!timeout) {
44559                   storedContext = null;
44560                   storedArgs = [];
44561               }
44562           }
44563           else if (!timeout) {
44564               timeout = window.setTimeout(later, remaining);
44565           }
44566           return result;
44567       };
44568   }
44569   
44570 });
44571
44572  
44573
44574